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)
Спасибо
Ответы
Ответ 1
Мне нравится этот подход:
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.
Ответ 2
Вам нужно всего лишь два случая в представлении перед тем, как произошла обратная передача, что-то вроде
if id:
form = MyForm(instance=obj)
else
form = MyForm()
то вы можете вызвать form.save() в postback, а Django позаботится об остальном.
Ответ 3
Что вы подразумеваете под "если существует идентичная запись"? Если это простая проверка идентификатора, то ваш код вида будет выглядеть примерно так:
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 уже определил, является ли это новой или существующей записью.
Если ваш чек для идентичной записи более сложный, может потребоваться немного сдвинуть логику.
Ответ 4
Я бы сделал это -
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()
Ответ 5
На основе ответов и комментариев мне пришлось создать другое решение для моего случая, которое включало использование 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