Django: formulaire de recherche dans ListView basé sur la classe

J'essaie de réaliser un {[4] } qui affiche une sélection d'un ensemble de table. Si le site est demandé la première fois, l'ensemble de données doit être affiché. Je préférerais une soumission de poste, mais GET est également très bien.

C'est un problème, qui était facile à gérer avec function based views, mais avec les vues basées sur les classes, j'ai du mal à me débrouiller.

Mon problème est que j'obtiens un nombre différent d'erreurs, qui sont causées par ma compréhension limitée des vues basées sur les classes. J'ai lisez diverses documentations et je comprends les vues pour les requêtes de requête directes, mais dès que je voudrais ajouter un formulaire à l'instruction de requête, je rencontre une erreur différente. Pour le code ci-dessous, je reçois un ValueError: Cannot use None as a query value.

Quel serait le flux de travail de meilleure pratique pour un ListView basé sur une classe en fonction des entrées de formulaire (sinon en sélectionnant l'ensemble de la base de données)?

C'est mon échantillon code:

Models.py

class Profile(models.Model):
    name = models.CharField(_('Name'), max_length=255)

    def __unicode__(self):
        return '%name' % {'name': self.name}

    @staticmethod
    def get_queryset(params):

        date_created = params.get('date_created')
        keyword = params.get('keyword')
        qset = Q(pk__gt = 0)
        if keyword:
            qset &= Q(title__icontains = keyword)
        if date_created:
            qset &= Q(date_created__gte = date_created)
        return qset

Forms.py

class ProfileSearchForm(forms.Form):
    name = forms.CharField(required=False)

Views.py

class ProfileList(ListView):
    model = Profile
    form_class = ProfileSearchForm
    context_object_name = 'profiles'
    template_name = 'pages/profile/list_profiles.html'
    profiles = []


    def post(self, request, *args, **kwargs):
        self.show_results = False
        self.object_list = self.get_queryset()
        form = form_class(self.request.POST or None)
        if form.is_valid():
            self.show_results = True
            self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
        else:
            self.profiles = Profile.objects.all()
        return self.render_to_response(self.get_context_data(object_list=self.object_list, form=form))

    def get_context_data(self, **kwargs):
        context = super(ProfileList, self).get_context_data(**kwargs)
        if not self.profiles:
            self.profiles = Profile.objects.all()
        context.update({
            'profiles': self.profiles
        })
        return context

Ci-dessous, j'ai ajouté le FBV qui fait le travail. Comment puis-je traduire cette fonctionnalité dans un CBV? Il semble être si simple dans les vues basées sur les fonctions, mais pas dans les vues basées sur les classes.

def list_profiles(request):
    form_class = ProfileSearchForm
    model = Profile
    template_name = 'pages/profile/list_profiles.html'
    paginate_by = 10

    form = form_class(request.POST or None)
    if form.is_valid():
        profile_list = model.objects.filter(name__icontains=form.cleaned_data['name'])
    else:
        profile_list = model.objects.all()

    paginator = Paginator(profile_list, 10) # Show 10 contacts per page
    page = request.GET.get('page')
    try:
        profiles = paginator.page(page)
    except PageNotAnInteger:
        profiles = paginator.page(1)
    except EmptyPage:
        profiles = paginator.page(paginator.num_pages)

    return render_to_response(template_name, 
            {'form': form, 'profiles': suppliers,}, 
            context_instance=RequestContext(request))
23
demandé sur Scott Woodall 2012-11-16 16:22:04

6 réponses

Je pense que votre objectif est d'essayer de filtrer queryset basé sur la soumission de formulaire, si c'est le cas, en utilisant GET:

class ProfileSearchView(ListView)
    template_name = '/your/template.html'
    model = Person

    def get_queryset(self):
        try:
            name = self.kwargs['name']
        except:
            name = ''
        if (name != ''):
            object_list = self.model.objects.filter(name__icontains = name)
        else:
            object_list = self.model.objects.all()
        return object_list

Alors tout ce que vous devez faire est d'écrire une méthode get pour rendre le modèle et le contexte.

Peut-être pas la meilleure approche. En utilisant le code ci-dessus, vous n'avez pas besoin de définir un formulaire django.

Voici comment cela fonctionne : les vues basées sur les classes séparent leur manière de rendre le modèle, de traiter le formulaire, etc. Comme, get gère la réponse GET, post gère la réponse POST, get_queryset et get_object est explicite, et ainsi de suite. Le moyen facile de savoir quelle est la méthode disponible, lancez un shell et tapez:

from django.views.generic import ListView si vous voulez savoir à propos de ListView

, puis tapez dir(ListView). Là, vous pouvez voir toute la méthode définie et aller visiter le code source pour le comprendre. La méthode get_queryset utilisée pour obtenir un queryset. Pourquoi ne pas simplement le Définir comme ceci, cela fonctionne aussi:

class FooView(ListView):
    template_name = 'foo.html'
    queryset = Photo.objects.all() # or anything

Nous pouvons le faire comme ci-dessus, mais nous ne pouvons pas faire de filtrage dynamique en utilisant cette approche. En utilisant get_queryset nous peut faire un filtrage dynamique, en utilisant toutes les données / valeurs / informations que nous avons, cela signifie que nous pouvons également utiliser name paramètre qui est envoyé par GET, et son disponible sur kwargs, ou dans ce cas, sur self.kwargs["some_key"]some_key est un paramètre que vous avez spécifié

37
répondu Otskimanot Sqilal 2012-11-23 11:44:25

Eh bien, je pense que laisser la validation à la forme est une bonne idée. Peut-être pas la peine dans ce cas particulier, parce que c'est une forme très simple - mais à coup sûr avec un plus compliqué (et peut-être que le vôtre va grandir aussi), donc je ferais quelque chose comme:

class ProfileList(ListView):
    model = Profile
    form_class = ProfileSearchForm
    context_object_name = 'profiles'
    template_name = 'pages/profile/list_profiles.html'
    profiles = []


    def get_queryset(self):
        form = self.form_class(self.request.GET)
        if form.is_valid():
            return Profile.objects.filter(name__icontains=form.cleaned_data['name'])
        return Profile.objects.all()
10
répondu jasisz 2016-08-24 20:16:33

Cela a été bien expliqué sur le sujet des vues génériques ici filtrage dynamique .

Vous pouvez faire du filtrage via GET, Je ne pense pas que vous puissiez utiliser la méthode POST pour cela car ListView n'est pas hérité des mixages d'édition.

Ce que vous pouvez faire est:

Urls.py

urlpatterns = patterns('', 
                (r'^search/(\w+)/$', ProfileSearchListView.as_view()),
              )

Views.py

class ProfileSearchListView(ListView):
    model = Profile
    context_object_name = 'profiles'
    template_name = 'pages/profile/list_profiles.html'
    profiles = []

    def get_queryset(self):
         if len(self.args) > 0:
               return Profile.objects.filter(name__icontains=self.args[0])
         else:
               return Profile.objects.filter()
2
répondu Rohan 2012-11-23 13:49:12

Je pense que l'erreur que vous obtenez est parce que votre formulaire ne nécessite pas le champ name. Ainsi, bien que le formulaire soit valide, le cleaned_data de votre Champ name est vide.

Ce pourraient être les lignes problématiques:

if form.is_valid():
    self.show_results = True
    self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])

Si j'étais vous, j'essaierais de changer la ligne:

self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])

À ceci:

self.profiles = Profile.objects.none()

Si vous arrêtez de recevoir des erreurs (et que votre modèle reçoit un object_list vide), le problème que vous avez Est ce que j'ai dit auparavant: champ de nom non requis.

Laissez-nous savoir si cela ne fonctionne pas!

1
répondu marianobianchi 2012-11-24 15:56:06

Recherche sur tous les champs du modèle

class SearchListView(ItemsListView):

# Display a Model List page filtered by the search query.

def get_queryset(self):
    fields = [m.name for m in super(SearchListView, self).model._meta.fields]
    result = super(SearchListView, self).get_queryset()
    query = self.request.GET.get('q')
    if query:
        result = result.filter(
            reduce(lambda x, y: x | Q(**{"{}__icontains".format(y): query}), fields, Q())
        )
    return result
0
répondu Алексей Степаненко 2017-10-16 10:22:48

Je pense que vous feriez mieux de le faire via get_context_data. Créez manuellement votre formulaire HTML et utilisez GET pour récupérer ces données. Un exemple de quelque chose que j'ai écrit est ci-dessous. Lorsque vous soumettez le formulaire, vous pouvez utiliser le obtenir les données à transmettre via les données de contexte. Cet exemple n'est pas adapté à votre demande, mais il devrait aider les autres utilisateurs.

def get_context_data(self, **kwargs):
    context = super(Search, self).get_context_data(**kwargs)
    filter_set = Gauges.objects.all()
    if self.request.GET.get('gauge_id'):
        gauge_id = self.request.GET.get('gauge_id')
        filter_set = filter_set.filter(gauge_id=gauge_id)

    if self.request.GET.get('type'):
        type = self.request.GET.get('type')
        filter_set = filter_set.filter(type=type)

    if self.request.GET.get('location'):
        location = self.request.GET.get('location')
        filter_set = filter_set.filter(location=location)

    if self.request.GET.get('calibrator'):
        calibrator = self.request.GET.get('calibrator')
        filter_set = filter_set.filter(calibrator=calibrator)

    if self.request.GET.get('next_cal_date'):
        next_cal_date = self.request.GET.get('next_cal_date')
        filter_set = filter_set.filter(next_cal_date__lte=next_cal_date)

    context['gauges'] = filter_set
    context['title'] = "Gauges "
    context['types'] = Gauge_Types.objects.all()
    context['locations'] = Locations.objects.all()
    context['calibrators'] = Calibrator.objects.all()
    # And so on for more models
    return context
0
répondu Sherd 2018-01-25 21:28:22