Django call function when an object gets added
Hay, i have a simple model
class Manufacturer(models.Model):
name = models.CharField()
car_count = models.IntegerField()
class Car(models.Model):
maker = ForeignKey(Manufacturer)
I want to update the car_count field when a car is added to a manufacturer, I'm aware i could just count the Manufacturer.car_set() to get the value, but i want the value to be stored within that car_count field.
How would i do this?
EDIT
Would something like this work?
def 开发者_运维百科save(self):
if self.id:
car_count = self.car_set.count()
self.save()
The best way make something happen when a model is saved it to use a signal. Django's documentation does a good job of describing what signals are and how to use them: http://docs.djangoproject.com/en/dev/topics/signals/
I'm not sure why you need to make it a field in the model though. Databases are very good at counting rows, so you could add a model method to count the cars which would use a very fast COUNT() query.
class Manufacturer(models.Model):
name = models.CharField()
def car_count(self):
return Car.objects.filter(maker=self).count()
class Car(models.Model):
maker = ForeignKey(Manufacturer)
In light of the requirement added by your comment, you're back to updating a field on the Manufacturer model whenever a Car is saved. I would still recommend using the count() method to ensure the car_count field is accurate. So your signal handler could look something like this:
def update_car_count(sender, **kwargs):
instance = kwargs['instance']
manufacturer = instance.maker
manufacturer.car_count = Car.objects.filter(maker=self).count()
manufacturer.save()
Then you would connect it to both the post_save and post_delete signals of the Car model.
post_save.connect(update_car_count, sender=Car)
post_delete.connect(update_car_count, sender=Car)
The proper way to let the database show how many cars a manufacturer has, is to let the database calculate it in the view using aggregations.
from django.db.models import Count
Manufacturer.objects.all().annotate(car_count=Count(car)).order_by('car_count')
Databases are very efficient at that sort of thing, and you can order by the result as seen above.
I'm a tiny bit confused.
.. when a car is added to a manufacturer ..
In the code shown in your question, I'd guess, you save a car with some manufacturer, e.g.
car.maker = Manufacturer.objects.get(name='BMW')
car.save()
Then the save method of the Car class would need to update the car_count
of the manufacturer (see Overriding predefined model methods for more details).
def save(self, *args, **kwargs):
if self.id:
self.maker.car_count = len(self.maker.car_set.all())
super(Car, self).save(*args, **kwargs)
Since this isn't the most elegant code, I'd suggest as @Josh Wright to look into signals for that matter.
P.S. You could also add a method on the Manufacturer class, but I guess, you want this attribute to live in the database.
class Manufacturer(models.Model):
name = models.CharField()
def _car_count(self):
return len(self.car_set.all())
car_count = property(_car_count)
...
The override in MYYN's answer won't work, since Car.id
won't be set (and probably not included in the Manufacturer's car_set) until it's saved. Instead, I'd do something like:
def save(self, *args, **kwargs):
super(Car, self).save(*args, **kwargs)
self.maker.car_count = len(self.maker.car_set.all())
self.maker.save()
Which is untested, but should work.
Of course, the best way is to use Josh's solution, since that's going 'with the grain' of Django.
精彩评论