Ad

Django-reversion Conflict With Models Clean Method

i use django-reversion to controll changes of my models but i have a problem with the models clean method.

For example my model looks like this:

class Document(models.Model):
    name = models.CharField(max_length=255, blank=True, null=True)
    client = models.ForeignKey(Client, on_delete=models.CASCADE)
    template = models.ForeignKey(Template, on_delete=models.CASCADE)

    def clean(self):
        if self.template.only_one and (Document.objects.filter(client=self.client, template=self.template).count() > 0):
            raise ValidationError('Test')

I register this model in the admin.py:

@admin.register(Document)
class DocumentReversion(VersionAdmin):
    pass

Now i create a record for Document in the admin section referencing to a template where the field only_one is True. If i delete this record and retrieve it, a ValidationError fires. Why? Because i have deleted the only available record. Very curious...

Ad

Answer

A clean is performed on any update. If you thus edit your Document, then it will check the given condition as well. Since that condition holds: the template has only_one=True, and there is such document (your document), it will thus raise a ValidationError.

You thus need to exclude the current item from the queryset:

class Document(models.Model):
    name = models.CharField(max_length=255, blank=True, null=True)
    client = models.ForeignKey(Client, on_delete=models.CASCADE)
    template = models.ForeignKey(Template, on_delete=models.CASCADE)

    def clean(self):
        qs = Document.objects.filter(client_id=self.client_id, template_id=self.template_id)
        if self.pk is not None:
            qs = qs.exclude(pk=self.pk)
        if self.template.only_one and qs.exists():
            raise ValidationError('A document with this template already exists')
        return super().clean()

If the primary key is thus set, we can exclude that primary key from the queryset. In fact we do not need the if condition here, since .exclude(pk=None) will always succeed anyway.

Ad
source: stackoverflow.com
Ad