QuerySet order_by промежуточные модели

Я пытаюсь создать промежуточную модель с полем created, записывающим время следующих отношений.

Модели следующие:

class Profile(models.Model):
    user = models.OneToOneField(User, related_name='profile')
    realname = models.CharField(max_length=20, verbose_name='真實姓名', blank=True)
    nickname = models.CharField(max_length=20, verbose_name='暱稱/顯示名稱')

## the intermediate model is connected through following field  ##
    followings = models.ManyToManyField('self', through='FollowShip',
                                        related_name='followers', symmetrical=False,
                                        through_fields=('profile_from', 'profile_to'))

    total_followers = models.IntegerField(default=0)

    def __str__(self):
        return 'Profile of User:{}'.format(self.user.username)


class FollowShip(models.Model):
    profile_from = models.ForeignKey(Profile, related_name='follow_from_set')
    profile_to = models.ForeignKey(Profile, related_name='follow_to_set')
    created = models.DateField(auto_now_add=True, db_index=True)

    def __str__(self):
        return "{} follows {}".format(self.profile_from, self.profile_to)

    class Meta:
        unique_together = ('profile_from', 'profile_to')

Это работает нормально. Однако, когда я попытался получить доступ к порядку QuerySet по полю created модели FollowShip. Ошибка возникает.

In manage.py shell:

>>> user.profile.followings.order_by('followship__created')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/query.py", line 232, in __repr__
    data = list(self[:REPR_OUTPUT_SIZE + 1])
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/query.py", line 256, in __iter__
    self._fetch_all()
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/query.py", line 1087, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/query.py", line 54, in __iter__
    results = compiler.execute_sql()
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 824, in execute_sql
    sql, params = self.as_sql()
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 369, in as_sql
    extra_select, order_by, group_by = self.pre_sql_setup()
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 47, in pre_sql_setup
    order_by = self.get_order_by()
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 293, in get_order_by
    field, self.query.get_meta(), default_order=asc))
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 553, in find_ordering_name
    field, targets, alias, joins, path, opts = self._setup_joins(pieces, opts, alias)
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 586, in _setup_joins
    pieces, opts, alias)
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1402, in setup_joins
    names, opts, allow_many, fail_on_missing=True)
  File "/Users/young/Desktop/env/strayvoice/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1327, in names_to_path
    "Choices are: %s" % (name, ", ".join(available)))
django.core.exceptions.FieldError: Cannot resolve keyword 'followship' into field. Choices are: follow_from_set, follow_to_set, followers, followings, id, nickname, realname, total_followers, user, user_id

Похоже, что промежуточная модель была установлена ​​неправильно. Так что я не могу найти followship во время запроса. Тем не менее, я могу получить доступ к обеим сторонам хорошо. Например:

>>> user.profile.followings.all()
`<QuerySet [<Profile: Profile of User:222222>, <Profile: Profile of User:333333>]>`
>>> user.profile.followers.all()
`<QuerySet [<Profile: Profile of User:222222>]>`

Понятия не имею, что пошло не так.


person JianWei    schedule 19.01.2017    source источник
comment
То же сообщение об ошибке. Похоже, что отношение m2m указывает на «я», а промежуточная модель действует иначе, чем указывает на другую модель.   -  person JianWei    schedule 19.01.2017


Ответы (1)


Используйте 1_.

user.profile.followings означает профиль, на который подписан user.profile.
user.profile.followers означает профиль, на который подписан user.profile.
Таким образом, и user.profile.followers, и user.profile.followings вернут набор запросов Profile, их можно упорядочить только по полям Profile, такие как пользователь, настоящее имя, псевдоним.

Если вы хотите найти профиль, за которым следуют use.profile и order_by created, вы можете использовать:

result = []
for profile_id in user.profile.follow_from_set.order_by('created').values('profile_to'):
    result.append(Profile.objects.get(id=profile_id))

Если есть много следующих, возможно, вы можете оптимизировать sql следующим образом:

profile_ids = user.profile.follow_from_set.order_by('created').values('profile_to')
profiles = Profile.objects.filter(id__in=profile_ids)

Наконец, измените порядок профилей в порядке profile_ids.

result = []
result_dict = {}
for profile in profiles:
    result_dict[profile.id] = profile
for id in profile_ids:
    result.append(result_dict[id])
return result

Может быть, есть много лучших способов решить проблему. Может ли кто-нибудь сделать лучшее разрешение?

person ramwin    schedule 19.01.2017
comment
См. последнюю часть документа django. Я предположил, что могу запросить как >>> Person.objects.filter( ... group__name='The Beatles', ... membership__date_joined__gt=date(1961,1,1)), как сказано в документе. - person JianWei; 19.01.2017
comment
Ваш способ работает. Но для этого я должен несколько раз добавлять хиты в базу данных, стоимость огромна. - person JianWei; 19.01.2017
comment
Набор запросов Profile.objects.filter(followers__nickname__contains='') подобен тому, что вы рекомендовали. Здесь вам может понадобиться что-то вроде Profile.objects.filter(follower_from_set__created__lt=datetime.date(2016,1,1)) или Profile.objects.filter(followers__created__lt=datetime.date(2016,1,1)) (ни один из двух не работает) - person ramwin; 19.01.2017
comment
Кажется, единственный способ получить доступ к промежуточной модели — это то, что вы сказали через follow_from_set или follow_to_set. - person JianWei; 19.01.2017
comment
Использование второго способа допустимо. SQL - это 'SELECT "myuser_followship"."profile_to_id" FROM "myuser_followship" WHERE "myuser_followship"."profile_from_id" = 1 ORDER BY "myuser_followship"."created" ASC LIMIT 21' и 'SELECT "myuser_profile"."id", "myuser_profile"."user_id", "myuser_profile"."realname", "myuser_profile"."nickname", "myuser_profile"."total_followers" FROM "myuser_profile" WHERE "myuser_profile"."id" IN (SELECT U0."profile_to_id" FROM "myuser_followship" U0 WHERE U0."profile_from_id" = 1) LIMIT 21'. Я немного смущен тем, что означает LIMIT 21. - person ramwin; 19.01.2017
comment
Таким образом, я могу заказать профиль, за которым следит пользователь, по полю created следующим образом: user.profile.followings.order_by('follow_from_set__created'). - person JianWei; 19.01.2017
comment
Понял. Спасибо. SQL показывает так: SELECT "myuser_profile"."id", "myuser_profile"."user_id", "myuser_profile"."realname", "myuser_profile"."nickname", "myuser_profile"."total_followers" FROM "myuser_profile" INNER JOIN "myuser_followship" ON ("myuser_profile"."id" = "myuser_followship"."profile_to_id") WHERE "myuser_followship"."profile_from_id" = 2 ORDER BY "myuser_followship"."created" ASC LIMIT 21 - person ramwin; 19.01.2017