Подтвердить что ты не робот

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)

Спасибо

4b9b3361

Ответ 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