Use different model's field with the same value in Django

1.1k views Asked by At

I have two Django models, and I want to have same-value field in both of them. Basically, when CarModification.engine_status is 'inactive' or 'active', I want to have the same field in Car set to the latest CarModification's field value.

class CarManager(models.Manager):

    def get_queryset(self):
        # Select engine_status from the latest
        # carmodification. If there are no modifications,
        # use default 'inactive'
        return super(CarManager, self).get_queryset().extra(
            select={
            'engine_status': "SELECT COALESCE ( "
                          "    (SELECT engine_status "
                          "    FROM carmodifications "
                          "    WHERE cars.id = carmodifications.car_id "
                          "    ORDER BY carmodifications.created_at DESC "
                          "    LIMIT 1), 'inactive')"
        )

class Car(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = CarManager()

class CarModification(models.Model):
    CHOICES = (
        ('active', 'Active')
        ('inactive', 'Inactive')
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    comment = models.CharField()
    engine_status = models.CharField(default='inactive', choices=CHOICES)
    car = models.ForeignKey(Car, related_name='modifications')

This works fine, but I also want an ability to filter Car objects by this extra field. Which is possible with more SQL queries, but it gets (and I think it already is) pretty ugly. Is there a way to accomplish the same, but cleaner way using Django ORM?

2

There are 2 answers

0
Sinux On BEST ANSWER

I prefer use signal in this situation:

class Car(models.Model):
    engine_status =  models.CharField(max_length=256, default='inactive')


class CarModification(models.Model):
    CHOICES = (
        ('active', 'Active'),
        ('inactive', 'Inactive')
        )
    engine_status = models.CharField(max_length=256, default='inactive', choices=CHOICES)
    car = models.ForeignKey(Car, related_name='modifications')


@receiver(post_save, sender=CarModification)
def pre_save_handler(sender, **kwargs):
    """after saving CarModification, change car's engine_status"""
    instance = kwargs.get('instance')
    if instance.car_id:
        current_car = Car.objects.filter(id=instance.car_id)[0]
        current_car.engine_status = instance.engine_status
        current_car.save()
0
argaen On

I think you can try to solve this with annotations rather than with extra. Annotations will allow you to use the new created field for filtering. More info: https://docs.djangoproject.com/en/1.8/topics/db/aggregation/.

Btw, since it seems you always need the engine_status on your Car. Why don't you just create a field on your Car model and set its status with signals every time there is a CarModification creation?