Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return error message after saving a model with custom save() method on Django Admin

I've two models. One is a Subscriber and at save time I'm assigning a pool based on the number of subscribers already assigned to that pool:

class Subscriber(models.Model):
    pool = models.ForeignKey(Pool)
    interface = models.CharField(max_length=30)


class Pool(models.Model):
    name = models.CharField(max_length=50)

Let's say that only 4 Subscribers can be assigned to a pool. That's why I'm overriding Subscriber's save() method:

def save(self, *args, **kwargs):
    if not self.pk:
        #Look for a free Pool among the available ones
        for pool in Pool.objects.all():
            if pool.subscriber_set.count() < 4:
                self.pool = pool
                print "Assigned pool: %s" % pool.name
                super(Subscriber, self).save(*args, **kwargs)

It's working great until I run out of pools (all of them have 4 users assigned). How should I handle this from Django admin? Ideally I'd like to show some error message to the user so he can create more pools.

I would rather not move the pool assignation code to the form's clean() method because probably users will also be created from a different interface, not the admin GUI.

Any ideas?

Thanks a lot!

like image 393
Ignacio Verona Avatar asked May 21 '14 15:05

Ignacio Verona


People also ask

What does save () do in Django?

The save method is an inherited method from models. Model which is executed to save an instance into a particular Model. Whenever one tries to create an instance of a model either from admin interface or django shell, save() function is run.

How do I save changes in Django model?

To save data in Django, you normally use . save() on a model instance. However the ORM also provides a . update() method on queryset objects.

Does Django objects create call save?

Creating objectsDjango doesn't hit the database until you explicitly call save() . The save() method has no return value.


1 Answers

I would recommend validating in both clean() and save(). The former will give you a nice error message and workflow in the admin, while the latter will make sure that save() itself gives an error message regardless of how you're creating the instance.

First the validation:

class Subscriber(models.Model):

    def clean(self)
        if not self.pk:
            if not Pool.objects.annotate(num_subscribers=Count('subscriber'))
                               .filter(num_subscribers__lt=4)
                               .exists():
                raise ValidationError('The pools are all full.')

This will be called automatically from the admin (see the documentation on ModelForm validation). Alternatively - if you're not cleaning things outside of ModelForm validation - you could supply this logic in the form's clean() method.

Then do the same thing in the save() method.

    def save(self, *args, **kwargs):
        if not self.pk:
            try:
                self.pool = Pool.objects.annotate(num_subscribers=Count('subscriber'))
                                        .filter(num_subscribers__lt=4)[0]
                super(Subscriber, self).save(*args, **kwargs)
            except IndexError:
                raise ValidationError(..)

You could instead call self.full_clean() inside save() to do the validation, but this version seems more straightforward (and is definitely more efficient).

like image 116
Kevin Christopher Henry Avatar answered Oct 14 '22 01:10

Kevin Christopher Henry