Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why define create_foo() in a Django models.Manager instead of overriding create()?

Reading the Django docs, it advices to make a custom creation method for a model named Foo by defining it as create_foo in the manager:

class BookManager(models.Manager):
    def create_book(self, title):
        book = self.create(title=title)
        # do something with the book
        return book

class Book(models.Model):
    title = models.CharField(max_length=100)

    objects = BookManager()

book = Book.objects.create_book("Pride and Prejudice")

My question is that why is the previous one preferred to simply overriding the base class's create method:

class BookManager(models.Manager):
    def create(self, title):
        book = self.model(title=title)
        # do something with the book
        book.save()
        return book

class Book(models.Model):
    title = models.CharField(max_length=100)

    objects = BookManager()

book = Book.objects.create("Pride and Prejudice")

Imo it seems that only overriding create will prevent anyone from accidentally using it to make a illformed model instance, since create_foo can always be bypassed completely:

class BookManager(models.Manager):
    def create_book(self, title):
        book = self.create(title=title, should_not_be_set_manually="critical text")
        return book

class Book(models.Model):
    title = models.CharField(max_length=100)
    should_not_be_set_manually = models.CharField(max_length=100)

    objects = BookManager()

# Can make an illformed Book!!
book = Book.objects.create(title="Some title", should_not_be_set_manually="bad value")

Is there any advantage in doing it like the docs suggest, or is actually overriding create just objectively better?

like image 812
ruohola Avatar asked Jan 21 '20 13:01

ruohola


1 Answers

Yes, obviously, you can do that. But if you look closer to the example you are quoting from documentation, it is not about whether you should override create or not, it is about

If you do so, however, take care not to change the calling signature as any change may prevent the model instance from being saved.

preserving the calling signature. Because interfaces available for you may also be used by django internally. If you modify them, things may not break for you but for Django.

In this example, they are not suggesting this for create but model constructor.

Secondly, even standard interface for create is only taking keyword arguments

def create(self, **kwargs):

But if you modify it to take positional arguments, def create(self, title): it will break wherever it is used inside Django or in standard way. So you should extend existing functionality not modify and most probably break it.

like image 117
Nafees Anwar Avatar answered Sep 23 '22 16:09

Nafees Anwar