Менее дорогой способ найти положение модели в наборе запросов?

#django #django-models

#django #django-модели

Вопрос:

У меня есть две модели:

 class Person(models.Model):
    name = models.CharField()
    email = models.EmailField()

class Vote(models.Model):
    person = models.ForeignKey('Person', related_name='votes')
    email = models.EmailField()
  

Я могу получить положение пользователя, используя этот метод в модели person:

 @property
def position(self):
    person_list = Person.objects.annotate(Count('votes')).order_by(
        '-votes__count', 'name')
    for i, x in enumerate(person_list, start=1):
        if x == self:
            return i
  

Проблема в том, что набор запросов person_list вычисляется каждый раз при вызове метода position, который, на мой взгляд, запускает ненужные запросы к базе данных, поскольку этот запрос нужно выполнить только один раз во время цикла запроса / ответа. В идеале я хочу воспользоваться кэшем набора запросов. У кого-нибудь есть идеи, как я мог бы это сделать?

Спасибо.

РЕДАКТИРОВАТЬ: я вызываю метод position из шаблона, поэтому не думаю, что смогу передать набор запросов в качестве аргумента.

Ответ №1:

Вы могли бы просто сохранить его внутри модели

 @property
def position(self):
    if not self.entry_list: 
        self.entry_list = Entry.objects.annotate(Count('votes')).order_by(
            '-votes__count', 'name')
    for i, x in enumerate(self.entry_list, start=1):
        if x == self:
            return i
  

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

 class PersonManager(models.Manager):
    def most_votes(self):
        if not self.most_votes_queryset:
            self.most_votes_queryset = self.get_query_set()
                .annotate(Count('votes'))
                .order_by('-votes__count','name')
        return self.most_votes_queryset

class Person(models.Model):
    objects = VoteManager()
  

что тогда привело бы к вашей модели:

 @property
def position(self):
    entry_list = Person.objects.most_votes()
    for i, x in enumerate(entry_list, start=1):
        if x == self:
            return i
  

Ответ №2:

Как насчет чего-то подобного:

 def position(self):
    if hasattr(self, '_position'):
        return self._position
    person_list = Person.objects.annotate(vc=Count('votes'))
        .order_by('-vc', 'name')
    num_votes = self.votes.count()
    place = person_list.filter(vc__gt=num_votes).count()

    place  = person_list.filter(vc=num_votes, name__lte=self.name).count()

    self._position = place
    return place
  

Я думаю, это должно работать * лучше *, если у вас много записей в таблице модели Person и это позиция кэширования в объекте модели.