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!
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.
To save data in Django, you normally use . save() on a model instance. However the ORM also provides a . update() method on queryset objects.
Creating objectsDjango doesn't hit the database until you explicitly call save() . The save() method has no return value.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With