23

Ich versuche eine Class Based ListView zu realisieren, die eine Auswahl eines Tabellensatzes anzeigt. Wenn die Site zum ersten Mal angefordert wird, sollte das Dataset angezeigt werden. Ich würde eine POST-Einreichung bevorzugen, aber GET ist auch in Ordnung.Django: Suchformular in Class Based ListView

Das ist ein Problem, das war einfach zu handhaben mit function based views, aber mit klassenbasierten Ansichten habe ich eine harte Zeit, um mich zu rächen.

Mein Problem ist, dass ich eine verschiedene Anzahl von Fehler, die durch mein begrenztes Verständnis der klassifizierten Ansichten verursacht werden. Ich habe verschiedene Dokumentationen gelesen und verstehe Ansichten für direkte Abfrageanforderungen, aber sobald ich der Abfrage ein Formular hinzufügen möchte, stoße ich auf einen anderen Fehler. Für den unten stehenden Code erhalte ich eine ValueError: Cannot use None as a query value.

Was wäre der Best-Practice-Workflow für ein klassenbasiertes ListView abhängig von den Formulareinträgen (andernfalls wird die gesamte Datenbank ausgewählt)?

Das ist mein Beispielcode:

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

Unten habe ich die FBV hinzugefügt, die die Arbeit erledigt. Wie kann ich diese Funktionalität in ein CBV übersetzen? Es scheint so einfach in funktionsbasierten Ansichten, aber nicht in klassenbasierten Ansichten.

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)) 
+1

Frage, versuchen Sie, einen Wert aus einem Abfrage-Set basierend auf Formularvorlage anzuzeigen? –

+0

Ja, das war das Ziel. – neurix

+0

Werfen Sie einen Blick auf diesen generischen Mixin-Ansatz: http://stackoverflow.com/questions/7011773/how-to-create-a-filter-form-for-a-class-based-generic-object- list-in-django –

Antwort

35

Ich denke, Ihr Ziel versucht queryset zu filtern basierend auf Formularübermittlung, wenn ja, von 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 

Dann alles, was Sie tun müssen, ist eine get Methode schreibt Vorlage zu machen und Kontext.

Vielleicht nicht der beste Ansatz. Wenn Sie den obigen Code verwenden, müssen Sie kein Django-Formular definieren.

So funktioniert es: Klassenbasierte Ansichten unterscheiden sich in der Art und Weise, wie Vorlagen gerendert, verarbeitet und so weiter. Wie, get Griffe GET-Antwort, post behandelt POST-Antwort, get_queryset und get_object ist selbsterklärend, und so weiter. Der einfache Weg zu wissen, was Methode zur Verfügung, eine Schale und Typen Feuer:

from django.views.generic import ListView wenn Sie ListView über

wissen wollen, und dann dir(ListView) eingeben. Dort können Sie alle definierten Methoden sehen und den Quellcode aufrufen, um ihn zu verstehen. Die get_queryset-Methode zum Abrufen eines Abfrage-Sets.Warum definieren nicht nur es so, es funktioniert auch:

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

Wir es wie oben tun können, aber wir können dynamische Filterung nicht durch diesen Ansatz. Unter Verwendung von get_queryset können wir dynamische Filterung unter Verwendung aller Daten/Werte/Informationen, die wir haben, verwenden, dh wir können auch name Parameter verwenden, der von GET gesendet wird, und dessen , oder in diesem Fall self.kwargs["some_key"], wo some_key ist Parameter, den Sie angegeben haben

+2

statt mit dem Versuch, Sie könnten get verwenden mit einem Standardwert, ich denke, es hilft mit der Lesbarkeit. 'name = self.kargs.get ('name', None)' und dann 'falls name: # ein paar Sachen machen ' – agmezr

2

Dies wurde in den allgemeinen Ansichten Thema hier Dynamic filtering schön erklärt.

Sie können Filtern durch GET, ich glaube nicht, dass Sie POST Methode dafür verwenden können, da ListView nicht von Bearbeitungsmischungen geerbt wird.

Was können Sie tun, ist:

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() 
9

Nun, ich denke, dass die Validierung verlassen, ist nette Idee zu bilden. Vielleicht lohnt sich nicht in diesem speziellen Fall, weil es sehr einfache Form ist - aber sicher mit kompliziertere (und vielleicht wachsen wird Ihnen auch), so würde ich so etwas wie:

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() 
+0

Ich habe einen Fehler bekommen, dass form_class nicht definiert ist, wie man es repariert? –

+0

Vic Nicethemer, Sie können es definieren – meteor

+3

@VicNicethemer Es sollte Form = self.form_class (self.request.GET) sein. –

1

Ich denke, dass die Fehler, den Sie bekommen, ist, weil Ihr Formular das Namensfeld nicht erfordert. Obwohl das Formular gültig ist, ist die cleaned_data für Ihr Feld name leer.

Diese könnten die problematischen Zeilen sein:

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

Wenn ich Sie wäre, würde ich versuchen, die Linie zu ändern:

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

dazu:

self.profiles = Profile.objects.none() 

Wenn Sie aufhören Empfangen von Fehlern (und Ihre Vorlage erhält eine leere object_list), das Problem, das Sie haben, ist, was ich gesagt habe vorher: Namensfeld nicht erforderlich.

Lassen Sie uns wissen, wenn das nicht funktioniert!

0

Suchen auf allen Feldern Modell

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

Ich glaube, Sie würden tun dies über get_context_data besser dran. Erstellen Sie manuell Ihr HTML-Formular und verwenden Sie GET, um diese Daten abzurufen. Ein Beispiel von etwas, das ich geschrieben habe, ist unten. Wenn Sie das Formular absenden, können Sie die get-Daten über die Kontextdaten zurückgeben. Dieses Beispiel ist nicht auf Ihre Anfrage zugeschnitten, sollte aber anderen Benutzern helfen.

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