Зачем определять create_foo () в Django models.Manager вместо переопределения create ()? - PullRequest
10 голосов
/ 21 января 2020

Считая Django документы , рекомендуется создать собственный метод создания для модели с именем Foo, определив его как create_foo в менеджере:

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")

Мой вопрос заключается в том, почему предыдущий предпочитает просто переопределять метод create базового класса:

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")

Похоже, что только переопределение create не позволит никому случайно использовать его для создания Неправильно сформированный экземпляр модели, поскольку create_foo всегда можно полностью обойти:

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")

Есть ли какое-либо преимущество в этом, как предполагают документы, или на самом деле переопределение create просто объективно лучше?

1 Ответ

10 голосов
/ 21 января 2020

Да, очевидно, вы можете сделать это. Но если вы посмотрите ближе на пример, который вы цитируете из документации, речь идет не о том, должны ли вы переопределять create, или нет, речь идет о

Однако, если вы это сделаете, постарайтесь не менять вызывающая подпись как любое изменение может помешать сохранению экземпляра модели.

с сохранением вызывающей подписи . Поскольку доступные для вас интерфейсы могут также использоваться django для внутренних целей. Если вы измените их, вещи могут не сломаться для вас, но для Django.

В этом примере они предлагают это не для create, а для конструктора модели.

Во-вторых , даже стандартный интерфейс для create принимает только ключевые аргументы

def create(self, **kwargs):

Но если вы измените его, чтобы принимать позиционные аргументы, def create(self, title): он сломается везде, где он используется внутри Django или стандартным способом. Таким образом, вы должны расширить существующую функциональность, а не изменить и, скорее всего, сломать это.

...