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.