it-swarm.com.de

Django: Suchformular in Class Based ListView

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

Das ist ein Problem, das mit function based views leicht zu handhaben war, aber mit klassenbasierten Ansichten fällt es mir schwer, den Kopf zu bekommen.

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

Was wäre der beste Arbeitsablauf für eine klassenbasierte ListView in Abhängigkeit von Formulareinträgen (ansonsten Auswahl der gesamten Datenbank)?

Dies 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

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

Unten habe ich den FBV hinzugefügt, der den Job erledigt. Wie kann ich diese Funktionalität in ein CBV übersetzen? In funktionsbasierten Ansichten scheint es so einfach zu sein, 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))
23
neurix

Ich denke, Ihr Ziel ist es, Queryset basierend auf der Formularübermittlung zu filtern, falls ja, indem Sie GET verwenden: 

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 müssen Sie nur eine get-Methode schreiben, um Vorlage und Kontext zu rendern.

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

Das funktioniert so: Klassenbasierte Ansichten unterscheiden sich in der Art und Weise, wie sie eine Vorlage rendern, ein Formular verarbeiten und so weiter. Beispielsweise behandelt get die GET-Antwort, post die Antwort POST, get_queryset und get_object sind selbsterklärend und so weiter. Der einfachste Weg, um herauszufinden, welche Methode verfügbar ist, starten Sie eine Shell und geben Sie Folgendes ein:

from Django.views.generic import ListView wenn Sie etwas über ListView wissen wollen

geben Sie dann dir(ListView) ein. Dort können Sie alle definierten Methoden sehen und den Quellcode besuchen, um sie zu verstehen. Die get_queryset-Methode, mit der ein Queryset abgerufen wird. Warum nicht einfach so definieren, es funktioniert auch: 

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

Wir können es wie oben machen, aber mit diesem Ansatz können wir keine dynamische Filterung durchführen. Durch die Verwendung von get_queryset können wir eine dynamische Filterung durchführen, indem wir alle Daten/Werte/Informationen verwenden, die wir haben. Dies bedeutet, dass wir auch den name-Parameter verwenden können, der von GET gesendet wird, und dessen verfügbare Variablen in kwargs oder in diesem Fall in self.kwargs["some_key"] stehen, wobei some_key ein beliebiger ist Parameter, den Sie angegeben haben

39

Nun, ich denke, dass es eine schöne Idee ist, die Gültigkeit zu hinterlassen. Vielleicht lohnt es sich in diesem speziellen Fall nicht, weil es eine sehr einfache Form ist - aber sicherlich mit einer komplizierteren (und vielleicht wird Ihre auch wachsen), also würde ich so etwas tun:

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()
11
jasisz

Dies wurde am generischen Views-Thema hier Dynamische Filterung gut erklärt. 

Sie können Filtern durch GET durchführen. Ich glaube nicht, dass Sie die POST-Methode dafür verwenden können, da ListView nicht von Edit-Mixings geerbt wird.

Was Sie tun können, 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()
2
Rohan

Ich denke, der Fehler, den Sie erhalten, ist, dass Ihr Formular das Namensfeld nicht benötigt. Obwohl das Formular gültig ist, ist die clean_data für Ihr name-Feld leer.

Dies 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 Zeile zu ändern:

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

zu diesem:

self.profiles = Profile.objects.none()

Wenn Sie keine Fehler mehr erhalten (und Ihre Vorlage einen leeren object_list erhält), haben Sie das Problem, das ich zuvor gesagt habe: Namensfeld nicht erforderlich.

Lassen Sie uns wissen, ob dies nicht funktioniert!

1
marianobianchi

Ich denke, es wäre besser, wenn Sie dies über get_context_data tun. Erstellen Sie Ihr HTML-Formular manuell und rufen Sie diese Daten mithilfe von GET ab. Ein Beispiel von etwas, das ich geschrieben habe, ist unten. Wenn Sie das Formular absenden, können Sie die get-Daten verwenden, um über die Kontextdaten zurückzukehren. Dieses Beispiel ist nicht auf Ihre Anfrage zugeschnitten, sollte jedoch 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
0
Sherd

Suche in allen Feldern des Modells

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

Dies ist dem Ansatz von @jasisz ähnlich, aber einfacher.

class ProfileList(ListView):
    template_name = 'your_template.html'
    model = Profile

    def get_queryset(self):
        query = self.request.GET.get('q')
        if query:
            object_list = self.model.objects.filter(name__icontains=query)
        else:
            object_list = self.model.objects.none()
        return object_list

Dann müssen Sie auf der HTML-Vorlage nur noch Folgendes tun:

<form method='GET'>
  <input type='text' name='q' value='{{ request.GET.q }}'>
  <input class="button" type='submit' value="Search Profile">
</form>
0
supertramp