scoopseven
scoopseven

Reputation: 1777

Override get() in Django Class Based View to Filter

I'm starting a new app and I'm trying my best to embrace Class Based Views. Ahhh, growing pains. I'm trying to do a simple filter here from a GET variable, if it doesn't exist I want to return all objects ordered by id desc, if it does, I want to filter, order and return the filtered list.

I'm writing 30+ lines of code to do this, so I must be doing something wrong, right? I tried overriding get_queryset() and now that I think of it, maybe I should be calling self.request.GET['search'] in get_queryset() to do this filtering. Is there a standard way of accomplishing this?

class MyModelList(AdminPageMixin, ListView):
    model = MyModel
    context_object_name = 'object'
    template_name = 'template/list.html'

    def get(self, request, *args, **kwargs):
        if 'search' in request.GET and len(request.GET['search']):
            search = request.GET['search']
            self.object_list = MyModel.objects.filter(advertiser__name=search).orderby('-id')
            context = self.get_context_data(object_list=self.object_list, search=search)
        else:
            self.object_list = MyModel.objects.all()
            context = self.get_context_data(object_list=self.object_list).orderby('-id')
        return self.render_to_response(context)

    def get_context_data(self, **kwargs):
        context = super(MyModelList, self).get_context_data(**kwargs)
        form = MyModelForm
        try:
            context['search'] = kwargs['search']
        except KeyError:
            pass
        context['form'] = form
        context['form_action'] = reverse('mymodel-add')
        context['form_display'] = 'hide'
        context['form_save_label'] = 'Add'
        return context

Secondarily, to complicate matters, I have a CreateView and an UpdateView that I would like to share the get() or get_queryset() and get_context_data() methods of my ListView. I'm sure that it's possible with a Mixin, but again, I'm looking for a standardized way of doing it. Suggestions? Here's the CreateView in which I'd like to share ListView's methods.

class MyModelAdd(AdminPageMixin, CreateView):
    model = MyModel
    form_class = MyModelForm
    context_object_name = 'object'
    template_name = 'templates/list.html'

    def get_success_url(self):
        return reverse('mymodel-list')

    def get_context_data(self, **kwargs):
        context = super(MyModelAdd, self).get_context_data(**kwargs)
        context['form_action'] = reverse('mymodel-add')
        context['form_display'] = 'show'
        context['object_list'] = MyModel.objects.all()
        return context

    def form_invalid(self, form):
        form = MyModelForm(self.request.POST)
        context = self.get_context_data()
        context['form'] = form
        context['form_action'] = reverse('mymodel-add')
        context['form_display'] = 'show'
        context['form_save_label'] = 'Add'
        return render(self.request, 'templates/list.html', context)

Upvotes: 4

Views: 12700

Answers (2)

Henning Lee
Henning Lee

Reputation: 584

The best way is to filter the data in 'get_context_data'.

def get_context_data(self, **kwargs):
    context = super(MyModelAdd, self).get_context_data(**kwargs)
    context['form_action'] = reverse('mymodel-add')
    context['form_display'] = 'show'
    context['object_list'] = MyModel.objects.all(user=self.request.user)# that's will filter the user data
    return context

Upvotes: 0

Berislav Lopac
Berislav Lopac

Reputation: 17243

First, you are correct in your observation: you should definitely use get_queryset for filtering the list. I suggest something along the lines of:

class MyModelList(AdminPageMixin, ListView):
    model = MyModel
    context_object_name = 'object'
    template_name = 'template/list.html'

    def get_queryset(self):
        qs = self.model.objects.all()
        search = self.request.GET.get('search')
        if search:
            qs = qs.filter(advertiser__name__icontains=search)
        qs = qs.order_by("-id") # you don't need this if you set up your ordering on the model
        return qs

Second, if you really need to reuse code between List and other views, you'd be better off to write a separate function than use a mixin.

A side note: you don't need the form_action URL in your form; simply set action="" and it will POST to the same URL.

Upvotes: 5

Related Questions