Josie
Josie

Reputation: 169

Django Update view creates new object while also updating

I'm working on a test application in Django and have run in to an issue I've been unable to solve.

I have two models, Farm and Product which have a One to Many relationship. When creating a Farm you can add multiple images which gets added as individual products with the Farm as the foreign key.

As I was working on being able to update the name of the farm I also wanted to add the ability to add more products on the same page. So I wrote a post function that is similiar to the one I used for creating the farm. However when I submit the update it doesn't only update and add the new products, it also creates a new farm object with the same name.

I think I have to get the correct instance for this to work but I don't have enough experience with Django to know where I need to implement those changes. (I might also have to change from the UpdateView to something else, if so please let me know!)

Here is my UpdateView:

    class FarmUpdateView(UpdateView):
        model = Farm
        fields = ['title']

        def post(self, request, *args, **kwargs):
            farm = self.get_object()
            form_class = self.get_form_class()
            form = self.get_form(form_class)
            files = request.FILES.getlist('images')
            if form.is_valid():
                title = form.cleaned_data['title']
                updateFarm = farm
                updateFarm.title = title
                updateFarm.save()
                farm_id = updateFarm
                for f in files:
                    Product.objects.create(image=f, farm=farm_id)
                return self.form_valid(form)
            else:
                return self.form_invalid(form)

And here is my template:

        <form method="post" class="farm-form" enctype="multipart/form-data">
            {% csrf_token %}
                {{ form.as_p }}
            <label for="farm-image">Images</label>
            <input type="file" name="images" class="form-control-file" id="farm-image" multiple>
             <button type="submit" class="btn btn-primary">Submit</button>
        </form>

Upvotes: 2

Views: 1304

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477686

The reason this fails is because you did not set self.object before making a form with get_form. As we can see in the source code of get_form_kwargs [GitHub], it will pass self.object as instance, if it exists:

    def get_form_kwargs(self):
        """Return the keyword arguments for instantiating the form."""
        kwargs = super().get_form_kwargs()
        if hasattr(self, 'object'):
            kwargs.update({'instance': self.object})
        return kwargs

So that means your self.get_form will construct a form, without an instance, and thus create a record.

You furthermore also do too much manual work yourself. Django's modelforms actually can remove a lot of boilerplate code. You can implement the creation of the Products in the form_valid method, and let Django do most of the work for you:

class FarmUpdateView(UpdateView):
        model = Farm
        fields = ['title']

        def form_valid(self, form):
            farm = form.instance
            products = [
                Product(image=f, farm_id=farm) for f in request.FILES.getlist('images')
            ]
            Product.objects.bulk_create(products)
            super().form_valid(form)

Upvotes: 6

Related Questions