开发者

Django call function when an object gets added

开发者 https://www.devze.com 2023-01-02 02:32 出处:网络
Hay, i have a simple model class Manufacturer(models.Model): name = models.CharField() car_count = models.IntegerField()

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消