Django Forms с get_or_create

Я использую Django ModelForms для создания формы. У меня есть моя форма, и она работает нормально.

form = MyForm(data=request.POST)
if form.is_valid():
 form.save()

Теперь я хочу, чтобы форма сначала проверяла, существует ли идентичная запись. Если это так, я хочу, чтобы он получил идентификатор этого объекта, и если нет, я хочу, чтобы он вставлял его в базу данных, а затем дал мне идентификатор этого объекта. Возможно ли это, используя что-то вроде:

form.get_or_create(data=request.POST)

Я знаю, что мог бы сделать

form = MyForm(instance=object)

при создании формы, но это не сработает, поскольку я все еще хочу иметь случай, когда нет экземпляра объекта

изменить:

Скажите, что моя модель

class Book(models.Model):
 name = models.CharField(max_length=50)
 author = models.CharField(max_length=50)
 price = models.CharField(max_length=50)

Мне нужна форма, которую кто-то может заполнить, чтобы хранить книги. Однако, если уже есть книга в db с таким же именем, автором и ценой, я, очевидно, не хочу, чтобы эта запись добавлялась снова, поэтому просто хочу узнать свой идентификатор, а не добавить его.

Я знаю, что в Django есть функция; get_or_create, который делает это, но есть ли что-то подобное для форм? или мне нужно будет сделать что-то вроде

if form.is_valid(): 
 f = form.save(commit=false)
 id = get_or_create(name=f.name, author=f.author, price=f.price)

Спасибо

5 ответов

Мне нравится этот подход:

if request.method == 'POST':
 form = MyForm(request.POST)
 if form.is_valid():
 book, created = Book.objects.get_or_create(**form.cleaned_data)

Таким образом вы сможете воспользоваться всеми функциональными возможностями форм модели (кроме .save()) и ярлыка get_or_create.


Вам нужно всего лишь два случая в представлении перед тем, как произошла обратная передача, что-то вроде

if id:
 form = MyForm(instance=obj)
else
 form = MyForm()

то вы можете вызвать form.save() в postback, а Django позаботится об остальном.


Что вы подразумеваете под "если существует идентичная запись"? Если это простая проверка идентификатора, то ваш код вида будет выглядеть примерно так:

if request.method == 'POST':
 form = MyForm(request.POST)
 if form.is_valid():
 form.save()
else:
 if get_id:
 obj = MyModel.objects.get(id=get_id)
 form = MyForm(instance=obj)
 else:
 form = MyForm()

Концепция здесь - проверка происходит в запросе GET, так что в POST для сохранения Django уже определил, является ли это новой или существующей записью.

Если ваш чек для идентичной записи более сложный, может потребоваться немного сдвинуть логику.


Я бы сделал это -

if request.method == 'POST':
 form = MyForm(request.POST)
 if form.is_valid():
 name = form.cleaned_data['name']
 author = form.cleaned_data['author']
 price = form.cleaned_data['prince']
 if name and author and price:
 book, created = Book.objects.get_or_create(name=name, \
 author=author, price=price)
 if created:
 # fresh entry in db.
 else:
 # already there, maybe update?
 book.save()


На основе ответов и комментариев мне пришлось создать другое решение для моего случая, которое включало использование unique_together в базовой модели. Вы также можете найти этот код полезным, так как я действительно сделал его довольно общим.

У меня есть собственный код в методе form.save(), который я хочу использовать для создания нового объекта, поэтому я не хочу просто не использовать вызов form.save(). Мне нужно проверить код в методе form.save(), который, по моему мнению, является разумным местом для его размещения.

У меня есть функция утилиты для сглаживания итераций.

def flatten(l, a=list()):
 """ 
 Flattens a list. Just do flatten(l).
 Disregard the a since it is used in recursive calls.
 """
 for i in l:
 if isinstance(i, Iterable):
 flatten_layout(i, a)
 else:
 a.append(i)
 return a

В ModelForm я перезаписываю метод validate_unique():

def validate_unique(self):
 pass

Это о том, как выглядит мой метод сохранения:

def save(self, commit=True):
 unique_fields = flatten(MyObject._meta.unique_together)
 unique_cleaned_data = {k: v for k, v in self.cleaned_data.items() if k in unique_fields}
 # check if the object exists in the database based on unique data
 try:
 my_object = MyObject.objects.get(**unique_cleaned_data)
 except MyObject.DoesNotExist:
 my_object = super(MyModelFormAjax, self).save(commit)
 # -- insert extra code for saving a new object here ---
 else:
 for data, value in self.cleaned_data.items():
 if data not in unique_fields:
 # only update the field if it has data; otherwise, retain
 # the old value; you may want to comment or remove this
 # next line
 if value:
 setattr(my_object, data, value)
 if commit:
 my_object.save()
 return my_object

licensed under cc by-sa 3.0 with attribution.