#django
#django
Вопрос:
Я пытаюсь отфильтровать участников по имени и вернуть участников и дочерние компании. Мои модели:
class Member( models.Model ):
first_name = models.CharField( max_length=40, blank=False, null=False )
last_name = models.CharField( max_length=40, blank=False, null=False )
class Affiliation( models.Model ):
member = models.ForeignKey( Member )
company = models.ForeignKey( Company )
date_entered = models.DateField( null = False, blank = False )
date_exited = models.DateField( null = True, blank = True )
class Company( models.Model ):
name = models.CharField( max_length = 100 )
и затем я хочу выполнить поиск по участнику, найти всех, чье имя соответствует некоторой строке, и показать их всех вместе с их принадлежностью к компании. Итак, для поиска я мог бы сделать:
def member_search(request):
member_list = Member.objects.filter(
Q( first_name__icontains=request.POST['q'].lower() ) |
Q( last_name__icontains=request.POST['q'].lower() ) )
return render_to_response('member/member_list.html', {'member_list': member_list } )
или
def member_search2(request):
affiliation_list = Affiliation.objects.filter(
Q( member__first_name__icontains=request.POST['q'].lower() ) |
Q( member__last_name__icontains=request.POST['q'].lower() ) )
return render_to_response('member/member_list2.html', {'affiliation_list': affiliation_list } )
В первом случае шаблон будет выглядеть примерно так:
<ul>
{% for member in member_list %}
<li>
<a href="/member/{{ member.pk }}">view</a>
<a href="/member/{{ member.pk }}/update">edit</a>
{{ member.first_name }} {{ member.last_name }}
{{ member.affiliation.all ??? }}
</li>
{% endfor %}
</ul>
но я не вижу, как выполнить обратный возврат через внешний ключ принадлежности. Я подозреваю, что мой фильтр придется изменить, но я не понимаю, как.
и во втором случае это будет что-то вроде:
<ul>
{% for affiliation in affiliation_list %}
<li>
<a href="/member/{{ affiliation.member.pk }}">view</a>
<a href="/member/{{ affiliation.member.pk }}/update">edit</a>
{{ affiliation.member.first_name }} {{ affiliation.member.last_name }}
{{ affiliation.member.company.name }}
</li>
{% endfor %}
но затем я получаю несколько строк для участника, если они связаны с несколькими компаниями. Я посмотрел на совокупность, но в данном случае это неясно, поскольку я тоже хочу перечислить компании.
В итоге я хочу:
member 1 ( company 1 [entry date], company 2 [entry date] )
member 2 ( company 1 [entry date] )
member 3 ( company 2 [entry date] )
Это, скорее всего, дубликат другого вопроса, но я не нашел этот вопрос :-). Кажется, что это обычная вещь, которую кто-то хотел бы сделать.
Комментарии:
1. Подумав об этом немного больше, я думаю, что хочу склониться ко второму методу, как мне бы очень хотелось
member 1 (company 1 [entry date])
и т.д.
Ответ №1:
Вот что вы можете попробовать:
Для 1-го вы можете получить связи с affiliation_set
{{ member.affiliation_set.all }}
Для 2-го вам нужно получить список значений, которые вы хотите показать. После этого вы можете использовать regroup
тег фильтра шаблона для соответствующей группировки dict. Пример кода
def member_search2(request):
affiliation_list = Affiliation.objects.filter( #your filter ...)
.values('member__first_name', 'member__last_name',
'company__name', `date_entered')
....
И в шаблоне
{% regroup affiliation_list|dictsort:"member__first_name" by
member__first_name as aflist %}
<ul>
{% for af in aflist %}
<li> {{af.grouper }}
<ul>
{%for m in af.list %}
<li> {{m.company__name}} : {{m.date_entered}} </li>
{%endfor%}
</ul>
</li>
{%endfor%}
</ul>
Комментарии:
1. кроме того, как бы я отсортировал по last_name, first_name?
2. Позвольте мне перефразировать мой вопрос… Я хотел бы сгруппировать по члену__id, поскольку у людей может быть одно и то же имя, и я хотел бы отсортировать по фамилии, имени. Я пытался
{% regroup affiliation_list|dictsort:"member__last_name" by member__id as aflist %}
, но идентификатор участника был указан в{{af.grouper }}
. Итак, как бы я поместил туда их имя и отсортировал его поmember__last_name,member__first_name
?
Ответ №2:
Первый метод намного проще и по-прежнему дает вам желаемый результат — как показывает Рохан, вам просто нужно использовать {{ member.affiliation_set.all }}
для каждого участника.
Однако это будет очень дорого, поскольку каждый вызов — это еще один запрос к базе данных, а переход от принадлежности к компании — это еще один вызов базы данных каждый раз. Вы можете сделать это намного дешевле, используя prefetch_related
в своем первоначальном вызове базы данных:
member_list = Member.objects.prefetch_related('affiliation__toppings').filter(...)
Еще одна вещь, о которой стоит подумать, хотя это не обязательно поможет в вашем текущем сценарии, заключается в том, что отношения между участником и компанией на самом деле являются отношениями «многие ко многим», с аффилированностью в виде сквозной таблицы. Вы можете добавить дополнительные функции, не изменяя структуру базы данных, объявив это явно:
class Member( models.Model ):
...
companies = models.ManyToManyField('Company', through='Affiliation')
и теперь объекты-члены могут делать member.companies.all()
, чтобы напрямую получать все свои компании.
Комментарии:
1. Спасибо за помощь. Приветствуется.
2. Это последнее логическое изменение, верно? (отсюда и комментарий о том, что структура базы данных не меняется?) На самом деле это было бы очень полезно.