Присоединение полей ManyToMany с prefetch, связанных в Django

Я могу пропустить что-то очевидное, но у меня возникли проблемы с получением соединения на поле ManyToMany для работы в приложении Django. У меня две модели:

class Area(models.Model):
    name = CharField(...)

class Role(models.Model):
    name = CharField(...)
    areas = ManyToManyField('Area', ...)

моя цель состоит в том, чтобы иметь эквивалент этого запроса:

select a.name, r.name
from area a
join area_role ar on ar.area_id = a.id
join role r on ar.role_id = r.id
order by a.name, r.name

результирующий набор данных будет выглядеть так:

Area    Role
---------------------
A       My Role
A       Your Role
A       Yo Mamas Role
B       My Role
B       Some Other Role

как вы можете видеть в Примере Моя Роль пункт появляется дважды, один раз для каждой области. Я знаю, что могу получить список областей, а затем получить список ролей для каждого (в результате N + 1 запросов), но я хотел бы быть эффективным, если это возможно. Таким образом, я обнаружил, что prefetch_related возможно, это то, что я хотел использовать. Однако, когда я использую это, я получаю все значения области как нет. Вот что я попробовал:

rqs = ( Role.objects.filter(areas__id__in=[1,2,3])
        .prefetch_related(areas).order_by('areas__name', 'name') )

for r in rqs:
    print("Area Name: " + str(r.areas.name))
    print("Role Name: " + str(r.name))

имена ролей правильно подходят для поездки, но имена областей-нет. Что я здесь делаю не так?

1 ответов


невозможно получить доступ к r.areas__name роли r. Вы все еще должны получить доступ к ролям через r.areas.all(). Однако, используя prefetch_related, вы получаете все связанные объекты в одном дополнительном запросе вместо o (n) запросов.

так как вы хотите заказать по названию области, вы, вероятно, должны использовать Area модель для вашего queryset, затем цикл через связанные роли.

areas = Area.objects.filter(id__in=[1, 2, 3]).order_by('name').prefetch_related('role_set')

for area in areas:
    roles = area.role_set.all()
    for role in roles:
        print area.name, roles.name

это должно дать вам заказ, который вы хотите, до тех пор, как Role модель заказана name по умолчанию. Если нет, вы можете использовать Prefetch объект для заказа связанного набора запросов.