How to clean and save multiple instances one after one in Django using clean and save methods?

I noticed that when using Django admin and whenever select/change multiple instances and click on save button (for example see the below image, it's not directly related to the code below), Django will clean/validate all instances and then save them one by one.

enter image description here

is this how things are working in Django or the process should be clean and then save the instance before repeat same process with the next instance? because when trying to set is_active value to be true for multiple instances, it passing the clean method condition without shown the error message that tells should only one instance be selected as true and that's correct cause no one of the instances have is_active as true in the database yet But if I click the save button again will show the error message.

models.py:


class SupplierAddress(models.Model):
    """Model to create supplier's address instances"""

    class Meta:
        """Customize django default way to plural the class name"""

        verbose_name = 'Supplier Address'
        verbose_name_plural = 'Supplier Addresses'
        constraints = [
            models.UniqueConstraint(
                fields=['supplier', 'address'],
                name='supplier_address_unique_appversion'
            )
        ]

    # Define model fields.
    supplier = models.ForeignKey(
        'Supplier',
        on_delete=models.CASCADE,
        related_name='supplier_addresses_supplier'
    )
    address = models.ForeignKey(
        'Address',
        on_delete=models.CASCADE,
        related_name='supplier_addresses_address'
    )
    is_active = models.BooleanField(default=False)

    def clean(self):
       """Restrict the add/change to model fields"""

       if self.is_active is True:

          if SupplierAddress.objects.filter(
                  supplier=self.supplier,
                  is_active=True
          ).exclude(id=self.id).count() >= 1:
              raise forms.ValidationError(
                  {
                     "is_active": "You can't set more than one active address"
                  }
              )

So, I was able to reproduce your issue. What happens is that Django admin page uses a formset to save data in this kind of editable list. What you need is to override this formset (thanks to this answer) and add validation to it, something like:

from django.forms import BaseModelFormSet

class MyAdminFormSet(BaseModelFormSet):
    def clean(self):
        active_count = 0
        form_set = self.cleaned_data
        for form_data in form_set:
            if form_data['is_active']:
                active_count += 1
                if active_count > 1:
                    raise forms.ValidationError('Cannot have more than one active object')
        return form_set 

class MyModelAdmin(admin.ModelAdmin):
    list_display = (..., 'is_active')
    list_editable = ('is_active',)

    def get_changelist_formset(self, request, **kwargs):
        kwargs['formset'] = MyAdminFormSet
        return super().get_changelist_formset(request, **kwargs)

Note that you need to adapt MyAdminFormSet to your problem, I just did a shallow counting of active objects in mine.

Back to Top