Saving class-based view formset items with a new "virtual" column Saving class-based view formset items with a new "virtual" column django django

Saving class-based view formset items with a new "virtual" column


Annotating query with virtual column

Sum is an aggregate expression and is not how you want to be annotating this query in this case. Instead, you should use an F exrepssion to add the value of two numeric fields

qs.annotate(virtual_col=F('field_one') + F('field_two'))

So your corrected queryset would be

Item.objects.order_by('code__name').annotate(amount=F('box_one') + F('box_two'))

The answer provided by cezar works great if intend to use the property only for 'row-level' operations. However, if you intend to make a query based on amount, you need to annotate the query.

Saving the formset

You have not provided a post method in your view class. You'll need to provide one yourself since you're not inheriting from a generic view that provides one for you. See the docs on Handling forms with class-based views. You should also consider inheriting from a generic view that handles forms. For example ListView does not implement a post method, but FormView does.

Note that your template is also not rendering form errors. Since you're rendering the formset manually, you should consider adding the field errors (e.g. {{ form.field.errors}}) so problems with validation will be presented in the HTML. See the docs on rendering fields manually.

Additionally, you can log/print the errors in your post method. For example:

def post(self, request, *args, **kwargs):    formset = MyFormSet(request.POST)    if formset.is_valid():        formset.save()        return SomeResponse    else:        print(formset.errors)        return super().post(request, *args, **kwargs)

Then if the form does not validate you should see the errors in your console/logs.


You're already on the right path. So you say you need a virtual column. You could define a virtual property in your model class, which won't be stored in the database table, nevertheless it will be accessible as any other property of the model class.

This is the code you should add to your model class Item:

class Item(models.Model):    # existing code    @property    def amount(self):        return self.box_one + self.box_one

Now you could do something like:

item = Item.objects.get(pk=1)print(item.box_one) # return for example 1print(item.box_two) # return for example 2print(item.amount) # it will return 3 (1 + 2 = 3)

EDIT:
Through the ModelForm we have access to the model instance and thus to all of its properties. When rendering a model form in a template we can access the properties like this:

{{ form.instance.amount }}

The idea behind the virtual property amount is to place the business logic in the model and follow the approach fat models - thin controllers. The amount as sum of box_one and box_two can be thus reused in different places without code duplication.