5

I see in the Django documentation :

Model Instance reference : Creating objects

You may be tempted to customize the model by overriding the __init__ method. If you do so, however, take care not to change the calling signature as any change may prevent the model instance from being saved.
Rather than overriding __init__, try using one of these approaches:

  • Add a classmethod on the model class.
  • Add a method on a custom manager (usually preferred)

Why is the second solution "usually preferred" ?

In a situation where I have a model B which extends a model A through a OneToOne relation, and I want to create a method generating a B object which generates the corresponding A object as well, how is it "better" to use a custom manager as suggested, given I'll probably not use this manager for anything other than what is provided by default manager ?

Levans
  • 14,196
  • 3
  • 49
  • 53

1 Answers1

5

I think it is preferred because it looks cleaner in code. You might also be reading into the emphasizes a bit too much, as the benefit or difference isn't that big. That said, when implementing things myself I do use the proposed approach.

Consider the following model (purely for illustrative purposes):

class Vehicle(models.Model):
    wheels = models.IntegerField()
    color = models.CharField(max_length=100)

In your application, the need often arises to get all cars, or all motorcycles, or whatever type of vehicle. To keep things DRY, you want some standard form of retrieving this data. By using class methods, you'd get the following:

class Vehicle(models.Model):
    #(...)
    @classmethod
    def cars(cls):
        return Vehicle.objects.filter(wheels=4)

cars = Vehicle.cars()
green_cars = Vehicle.cars().filter(color='green')

If you create a manager, you'll get something like this:

class CarManager(models.Manager):
    def get_query_set(self):
        return super(CarManager, self).get_query_set().filter(wheels=4)

class Vehicle(models.Model):
    #(...)
    car_objects = CarManager()

cars = Vehicle.car_objects.all()
green_cars = Vehicle.car_objects.filter(color='green')

In my opinion, the latter looks cleaner, especially when things get more complex. It keeps the clutter out of your model definitions, and keeps things similar to using the default objects manager.

Marcin
  • 48,559
  • 18
  • 128
  • 201
jro
  • 9,300
  • 2
  • 32
  • 37
  • In my opinion it's mainly because Django integrates it's ORM with a lot of other packages and libs as like as DRF for example. This kind of packages used to integrate with models and call Model.objects on normal conditions. To implement methods on Models is, in my opinion a kind of naive solution. It's obvious clear and simple, but how do you do when you need to get for example a category of green cars? Another method! – Yago Gehres Sep 12 '20 at 15:55