[Django]-Django Forms with get_or_create

37πŸ‘

βœ…

I like this approach:

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

That way you get to take advantage of all the functionality of model forms (except .save()) and the get_or_create shortcut.

πŸ‘€rz.

1πŸ‘

You just need two cases in the view before the postback has occurred, something like

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

then you can call form.save() in the postback and Django will take care of the rest.

πŸ‘€Tom

1πŸ‘

What do you mean by β€œif an identical record exists”? If this is a simple ID check, then your view code would look something like this:

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()

The concept here is the check occurs on the GET request, such that on the POST to save, Django will already have determined if this is a new or existing record.

If your check for an identical record is more complex, it might require shifting the logic around a bit.

πŸ‘€Marc L

0πŸ‘

I would do this –

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()

0πŸ‘

Based on the answers and comments, I had to create a different solution for my case, which included the use of unique_together on the base model. You may find this code useful as well, as I actually made it fairly generic.

I have custom code in the form.save() method that I want to utilize for creating a new object, so I don’t want to simply not use the form.save() call. I do have to put my code check in the form.save() method, which I think is a reasonable place to put it.

I have a utility function to flatten iterables.

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

In the ModelForm, I overwrite the validate_unique() method:

def validate_unique(self):
    pass

This is about what my save method looks like:

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
πŸ‘€Bobort

Leave a comment