简体   繁体   English

如何在Django中的“多对多”字段中搜索?

[英]How to search on a Many to Many field in Django?

I have a Profile model with a ManyToManyField on another model Specialty . 我有一个Profile模型,在另一个模型Specialty上具有ManyToManyField

I want to have a simple search on the Profile model against specialties and return matching profiles. 我想对Profile模型进行简单的搜索以找到特长并返回匹配的Profile。 As it stands, my form displays in my template correctly, but I can't get anything after the submission. 就目前而言,我的表单可以正确显示在模板中,但是提交后我什么也收不到。

models.py : models.py

from django.db import models
from django.conf import settings

class Specialty(models.Model):
    title = models.CharField(max_length=255)

    class Meta:
        verbose_name_plural = 'Specialties'

    def __unicode__(self):
        return u"%s" % self.title

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    specialties = models.ManyToManyField(Specialty, blank=True)

    def __unicode__(self):
        return u"%s" % (self.user.username)

    def get_absolute_url(self):
        return reverse("profile_detail", args=[str(self.user.username)])

forms.py : forms.py

from django import forms

from .profiles.models import Profile, Specialty

class ProfileSearchForm(forms.ModelForm):
    specialty = forms.ModelMultipleChoiceField(queryset=Specialty.objects.all(), widget=forms.CheckboxSelectMultiple, required=False)

    class Meta:
        model = Profile
        fields = ('specialty',)

views.py : views.py

from django.views.generic.edit import FormView
from django.core.urlresolvers import reverse_lazy

from .forms import ProfileSearchForm
from .profiles.models import Profile

class IndexView(FormView):
    template_name = 'index.html'
    form_class = ProfileSearchForm
    success_url = reverse_lazy('index')

    def form_valid(self, form):
        specialty = form.cleaned_data['specialty']
        self.profile_list = Profile.objects.filter(specialty__in=specialty)
        return super(IndexView, self).form_valid(form)

index.html : index.html

<form action="{% url 'index' %}" method="get">
    {{ form.as_p }}
    <p><input type="submit" value="Search"></p>
</form>

<ul>
{% for profile in profile_list %}
    <li><a href="{{ profile.get_absolute_url }}">{{ profile.user.get_full_name }}</a></li>
{% endfor %}
</ul>

I have a feeling it has to do with self.profile_list . 我觉得这与self.profile_list I don't know if/how it should go into a get_extra_context . 我不知道是否/如何将它放入get_extra_context It can't exist on the first visit, so I don't know how to make it exist or pass it around. 它在第一次访问时就不存在,所以我不知道如何使其存在或传递它。 I'm also not sure if the Profile.objects.filter(specialty__in=specialty) is quite the right way to field lookup on a many-to-many field. 我也不确定Profile.objects.filter(specialty__in=specialty)是否是在多对多字段上进行字段查找的正确方法。

I'm also open to other search suggestions like Haystack if they have advantages. 如果有优势,我也欢迎其他搜索建议(例如Haystack)。 I prefer a group of checkboxes, which I don't think Haystack can handle via faceting. 我更喜欢一组复选框,我认为Haystack无法通过构面来处理。

Thanks, Gergo and Cameron. 感谢Gergo和Cameron。 I got it fixed now. 我现在把它修好了。 You were right about that one problem, but there were quite a few steps left to go. 您对这个问题是正确的,但是还有很多步骤要走。

  • What I really wanted was a ListView plus the ability to do a simple search, which should be a FormMixin that lets me add form_class and success_url , instead of it all as a FormView . 我真正想要的是一个ListView加做一个简单的搜索,这应该是一个能力FormMixin ,让我补充form_classsuccess_url ,而不是把它们当作一个FormView
  • When a default model is specified in a ListView , the view blows away the context, so form never reaches the template. 当在ListView指定默认model ,视图会吹走上下文,因此form永远不会到达模板。 get_context_data needs to add the form back to the context, of which the docs have an example . get_context_data需要将表单添加回上下文中,文档中有一个example
  • form_valid should be removed because a search is never a POST request, despite what the docs say under the "Note" in FormMixin requiring form_valid and form_invalid . 应该删除form_valid因为搜索绝不是POST请求,尽管文档在FormMixin的“注释”下说了要求form_validform_invalid
  • I need get_queryset to either get a default queryset via model or read the GET request's specialties value and filter the results appropriately. 我需要get_queryset来通过model获取默认的查询集,或者读取GET请求的specialties值并适当地过滤结果。
  • For bonus points, get_form_kwargs needs to pass the current request to the form so initial form values can remain after a page refresh. 为了获得奖励积分, get_form_kwargs需要将当前请求传递给表单,以便在页面刷新后仍可以保留初始表单值。 The tricky part is that when using ModelMultipleChoiceField , you have to use request.GET 's getlist and not get method to read that list of values. 棘手的部分是,当使用ModelMultipleChoiceField ,必须使用request.GETgetlist而不是get方法来读取该值列表。

All together now... 现在都在一起了...

forms.py : forms.py

from django import forms
from .profiles.models import Profile, Specialty

class ProfileSearchForm(forms.ModelForm):
    specialties = forms.ModelMultipleChoiceField(queryset=Specialty.objects.all(), widget=forms.CheckboxSelectMultiple, required=False)

    class Meta:
        model = Profile
        fields = ('specialties',)

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super(ProfileSearchForm, self).__init__(*args, **kwargs)
        self.fields['specialties'].initial = self.request.GET.getlist('specialties')

views.py : views.py

from django.views.generic import ListView
from django.views.generic.edit import FormMixin
from django.core.urlresolvers import reverse_lazy
from .profiles.models import Profile
from .forms import ProfileSearchForm

class IndexView(FormMixin, ListView):
    model = Profile
    template_name = 'index.html'
    form_class = ProfileSearchForm
    success_url = reverse_lazy('index')

    def get_queryset(self):
        queryset = super(IndexView, self).get_queryset()
        specialties = self.request.GET.getlist('specialties')
        if specialties:
            queryset = queryset.filter(specialties__in=specialties).distinct('user')
        return queryset

    def get_form_kwargs(self):
        kwargs = super(IndexView, self).get_form_kwargs()
        kwargs['request'] = self.request
        return kwargs

    def get_context_data(self, **kwargs):
        context = super(IndexView, self).get_context_data(**kwargs)
        form_class = self.get_form_class()
        context['form'] = self.get_form(form_class)
        return context

我认为您正在寻找Profile.objects.filter(specialties__in=specialty) -概要文件没有专业字段,它有一个专业字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM