Абстрактная базовая модель Django с пользовательским классом QuerySet - PullRequest
3 голосов
/ 06 января 2012

Я использую подход, аналогичный ответу Т. Стоуна на этот вопрос . Тем не менее, я добавил абстрактный базовый класс, поэтому мой models.py выглядит так:

class CustomQuerySetManager(models.Manager):
    """A re-usable Manager to access a custom QuerySet"""
    def __getattr__(self, attr, *args):
        try:
            return getattr(self.__class__, attr, *args)
        except AttributeError:
            return getattr(self.get_query_set(), attr, *args)

    def get_query_set(self):
        return self.model.QuerySet(self.model)

class MyModel(models.Model): 
    class Meta:
        abstract = True

    class QuerySet(QuerySet):
        def user(self, pub, *args, **kwargs):
            return self.filter(publisher=pub, *args, **kwargs)

    ...some more methods here

class Book(MyModel):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author, related_name='book_author')
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()

    objects=models.Manager()
    obj=CustomQuerySetManager() #for testing purposes only, this will override objects later

Это позволяет мне получить все книги для данного издателя, например:

p = Publisher.object.get(pk=1)
Book.obj.user(p).all()

Я хотел бы расширить это, чтобы я мог определить пользовательский запрос в модели Book и затем передать объект Q в класс QuerySet, чтобы запрос "publisher = pub" мог быть различным для разных моделей. Я все еще хочу иметь возможность называть это как Book.obj.user (p) .all (). Где-то в модели Книги мне нужно:

pubQ=Q(publisher=pub)

Где я могу поместить это и как передать его в QuerySet, определенный в абстрактном базовом классе, сохранив при этом код как DRY?

1 Ответ

3 голосов
/ 06 января 2012

Этот ответ умный, но он нарушает принцип Питона «явный лучше, чем неявный».Моя первая реакция на ваш код состояла в том, чтобы сказать вам, что вы не можете объявить пользовательский набор запросов внутри вашей модели, но я решил проверить упомянутый SO-ответ, чтобы узнать, откуда у вас эта идея.Опять же, это умно - не исключая этого, но хорошо написанный код самодокументируется и должен быть в состоянии быть выбранным любым случайным разработчиком Django и работать с ним.Вот где рецензии кода для коллег могут пригодиться - если бы у вас был такой, вы бы сразу получили WTF с этим.

Основная команда Django делает это следующим образом:

class MyQuerySet(models.query.QuerySet):
    def some_method(self, an_arg, another_arg, a_kwarg='some_value'):
        # do something
        return a_queryset

class MyManager(models.Manager):
    def get_query_set(self):
        return MyQuerySet(self.model)

    def some_method(self, *args, **kwargs):
        return self.get_query_set().some_method(*args, **kwargs)

Это СУХОЙ в том смысле, что вы не повторяете фактическое определение метода в менеджере.Но это также ясно - вы точно знаете, что происходит.Это не такой СУХОЙ, как метод, на который вы ссылаетесь, но «явный лучше, чем неявный».Кроме того, если это делается в реальной кодовой базе Django, вы можете быть разумно уверены, что это хорошая практика в вашем собственном коде.И у него есть побочный эффект, который значительно облегчает расширение и переопределение в подклассах.

...