Django: Search form in Class Based ListView

前端 未结 7 2057
遥遥无期
遥遥无期 2020-12-04 13:22

I am trying to realize a Class Based ListView which displays a selection of a table set. If the site is requested the first time, the dataset should be displaye

相关标签:
7条回答
  • 2020-12-04 13:55

    Search on all fields in model

    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 讨论(0)
  • 2020-12-04 13:58

    This is similar to @jasisz 's approach, but simpler.

    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
    

    Then all you have to do on the html template is:

    <form method='GET'>
      <input type='text' name='q' value='{{ request.GET.q }}'>
      <input class="button" type='submit' value="Search Profile">
    </form>
    
    0 讨论(0)
  • 2020-12-04 14:01

    Well, I think that leaving validation to form is nice idea. Maybe not worth it in this particular case, because it is very simple form - but for sure with more complicated one (and maybe yours will grow also), so I would do something like:

    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 讨论(0)
  • 2020-12-04 14:10

    I think you would be better off doing this via get_context_data. Manually create your HTML form and use GET to retrieve this data. An example from something I wrote is below. When you submit the form, you can use the get data to pass back via the context data. This example isn't tailored to your request, but it should help other users.

    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 讨论(0)
  • 2020-12-04 14:12

    I think your goal is trying to filter queryset based on form submission, if so, by using GET :

    class ProfileSearchView(ListView)
        template_name = '/your/template.html'
        model = Person
    
        def get_queryset(self):
            name = self.kwargs.get('name', '')
            object_list = self.model.objects.all()
            if name:
                object_list = object_list.filter(name__icontains=name)
            return object_list
    

    Then all you need to do is write a get method to render template and context.

    Maybe not the best approach. By using the code above, you no need define a Django form.

    Here's how it works : Class based views separates its way to render template, to process form and so on. Like, get handles GET response, post handles POST response, get_queryset and get_object is self explanatory, and so on. The easy way to know what's method available, fire up a shell and type :

    from django.views.generic import ListView if you want to know about ListView

    and then type dir(ListView). There you can see all the method defined and go visit the source code to understand it. The get_queryset method used to get a queryset. Why not just define it like this, it works too :

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

    We can do it like above, but we can't do dynamic filtering by using that approach. By using get_queryset we can do dynamic filtering, using any data/value/information we have, it means we also can use name parameter that is sent by GET, and it's available on kwargs, or in this case, on self.kwargs["some_key"] where some_key is any parameter you specified

    0 讨论(0)
  • This has been explained nicely on the generic views topic here Dynamic filtering.

    You can do filtering through GET, I don't think you can use POST method for this as ListView is not inherited from edit mixings.

    What you can do is:

    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()
    
    0 讨论(0)
提交回复
热议问题