Фильтр сложных запросов Django через обратные отношения - PullRequest
0 голосов
/ 24 июня 2019

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

class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(db_index=True, max_length=20, unique = True)

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    full_name = models.CharField(db_index=True, max_length=256, null=True)
    friends = models.ManyToManyField("self", related_name = 'friends', symmetrical=True)

Для поиска мне нужен запрос:

  1. ВЫБЕРИТЕ всех друзей и друзей друзей (кроме себя)

  2. ГДЕ имя пользователя __startswith (query) ИЛИ полное_имя__startswith (query)

Я не могу понять, как это сделать, учитывая структуру: нужно получить доступ к друзьям, затем к друзьям друзей и поменять User объекты. Поэтому вопрос таков: Можно ли избежать циклического просмотра результатов и применить фильтр для работы базы данных?

Ответы [ 2 ]

0 голосов
/ 26 июня 2019

Так как друзья symmetrical, возможно это:

profile = UserProfile.objects.get(user=<some_user>)
all_profiles = UserProfile.objects.filter(Q(user__username__startswith=query) | 
                                          Q(full_name__startswith=query)).distinct()
profile_friends = all_profiles.filter(friends=profile)
friends_of_friends = all_profiles.filter(friends__in=profile_friends)
all_friends = profile_friends | friends_of_friends

Я не проверял это, но я думаю, что это довольно близко.Вероятно, будут некоторые дубликаты, поэтому, возможно, потребуется применить окончательный distinct.

0 голосов
/ 24 июня 2019

Почему UserProfile это отдельный класс? Похоже, эти поля принадлежат User.

Вы не задаете вопрос, поэтому я почти наверняка дам частичный ответ, в лучшем случае. Надеюсь, это поможет вам. Чтобы получить доступ к друзьям, вы можете:

profile = UserProfile.objects.get(pk=1)
friends = UserProfile.friends.all().prefetch_related('friends')
for friend in friends:
    f_user = friend.user
    f_friends = friend.friends.all().prefetch_related('friends')

Это будет неэффективно в масштабе, но хорошо для одиночных UserProfile с. В противном случае cached_property может быть подходящим вариантом. На самом деле, документы кажутся похожими на вашу проблему. Если вы получаете friends из набора запросов UserProfile с, вам следует .prefetch_related('friends')

class UserProfile(models.Model):
    ...

    @cached_property
    def my_friends(self):
        return [f for f in self.friends.all().prefetch_related('friends')]

Использование такого понимания списка избавляет вас от попадания в БД каждый раз, если я не ошибаюсь.

...