Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Search form in Class Based ListView

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 displayed. I would prefer a POST submission, but GET is also fine.

That is a problem, which was easy to handle with function based views, however with class based views I have a hard time to get my head around.

My problem is that I get a various number of error, which are caused by my limited understanding of the classed based views. I have read various documentations and I understand views for direct query requests, but as soon as I would like to add a form to the query statement, I run into different error. For the code below, I receive an ValueError: Cannot use None as a query value.

What would be the best practise work flow for a class based ListView depending on form entries (otherwise selecting the whole database)?

This is my sample 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 

Below I added the FBV which does the job. How can I translate this functionality into a CBV? It seems to be so simple in function based views, but not in class based views.

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)) 
like image 709
neurix Avatar asked Nov 16 '12 12:11

neurix


2 Answers

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

like image 188
Otskimanot Sqilal Avatar answered Sep 24 '22 00:09

Otskimanot Sqilal


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() 
like image 43
jasisz Avatar answered Sep 24 '22 00:09

jasisz