Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django 1.8 custom manager and .create() method is not used by .update_or_create()

I have two related instances that should always be created together. I'd like to do this without using signals or override the model's save() method.

class Car(models.Mode):
    make = models.CharField(max_length=32)
    model = models.CharField(max_length=32)

    class Meta:
        unique_together = ('make', 'model',)

    objects = CarManager()

class CarProfile(models.Model):
    car = models.OneToOneField(Car)
    last_checkup = models.DateTimeField(blank=True, null=True)        

I have created a custom CarManager that overrides models.Manager.create() to ensure the creation of CarProfile when a Car is created:

class CarManager(models.Manager):
    def create(self, **kwargs):
        with transaction.atomic():
            car = self.model(**kwargs)
            car.save(force_insert=True)
            CarProfile.objects.create(car=car)
        return car

When I call Car.objects.create(make='Audi', model='R8'), a new Car instance and its corresponding CarProfile is created as expected. However when I try creating a new Car using Car.objects.update_or_create(make='Audi', model='R8') or Car.objects.get_or_create(make='Audi', model='R8'), in both cases a Car instance is created but no corresponding CarProfile is created.

Why doesn't update_or_create and get_or_create produce the expected CarProfile instance when I've specified that behaviour in the custom create() method?

It seems that both these methods are calling the create() from QuerySet class instead of my custom one.

like image 347
lajitong Avatar asked Oct 29 '15 05:10

lajitong


2 Answers

Django's Manager class is actually a QuerySet that has been reconstituted into a Manager. What if you implement create on a QuerySet and then build a Manager from that?

class CarQuerySet(models.QuerySet):

    def create(self, **kwargs):
        with transaction.atomic():
            car = self.model(**kwargs)
            car.save(force_insert=True)
            CarProfile.objects.create(car=car)
        return car

Then the Car is:

class Car(models.Mode):
    make = models.CharField(max_length=32)
    model = models.CharField(max_length=32)

    class Meta:
        unique_together = ('make', 'model',)

    objects = CarQuerySet.as_manager()
like image 113
Nathan Villaescusa Avatar answered Nov 09 '22 17:11

Nathan Villaescusa


The actual reason that overriding create is not working in this instance is because you need to override both the update_and_create and the get_or_create methods as well as create to affect their behaviour.

like image 2
abarax Avatar answered Nov 09 '22 16:11

abarax