Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to process a form (via get or post) using class-based views?

Im trying to learn class-based views, for a detail or list view is not that complicated.

I have a search form and I just want to see if I send a query to show up the results.

Here is the function code (is not mine, is from a django book):

def search_page(request):     form = SearchForm()     bookmarks = []     show_results = False     if 'query' in request.GET:         show_results = True         query = request.GET['query'].strip()         if query:             form = SearchForm({'query': query})             bookmarks = Bookmark.objects.filter(title__icontains=query)[:10]       show_tags = True     show_user = True      if request.is_ajax():         return render_to_response("bookmarks/bookmark_list.html", locals(), context_instance=RequestContext(request))     else:         return render_to_response("search/search.html", locals(), context_instance=RequestContext(request)) 

Ignoring the ajax fact (just to make the problem easier for now), how can I translate this to class-based views?

I quick tried something like this:

class SearchPageView(FormView):     template_name = 'search/search.html'      def get(self, request, *args, **kwargs):         form = SearchForm()         self.bookmarks = []         self.show_results = False         if 'query' in self.request.GET:             self.show_results = True             query = self.request.GET['query'].strip()             if query:                 form = SearchForm({'query': query})                 self.bookmarks = Bookmark.objects.filter(title__icontains=query)[:10]         return super(SearchPageView, self).get(request, *args, **kwargs)      def get_context_data(self, **kwargs):         context = super(SearchPageView, self).get_context_data(**kwargs)         context.update({             'show_tags': True,             'show_user': True,             'show_results': self.show_results,             'bookmarks': self.bookmarks         })         return context 

Doesn't work, I get a: "'NoneType' object is not callable"

Fair enough, I started today with this stuff.

So, what's the way to make a Class-based views that can manage a get (and a post too if needed) request?

I have another example:

@render_to('registration/register.html') def register_page(request):     if request.method == 'POST':         form = RegistrationForm(request.POST)         if form.is_valid():             user = User.objects.create_user(                 username=form.cleaned_data['username'],                 password=form.cleaned_data['password1'],                 email=form.cleaned_data['email']             )             return HttpResponseRedirect('/accounts/register/success/')     else:         form = RegistrationForm()     return locals() 

Would this be "transformed" the same way that the first one? Or they extend differents Views?

Im confused a lot. I don't know if the first is ProcessFormView and the second FormView or what.

Thanks.

EDIT: Solution I ended with:

class SearchPageView(FormView):     template_name = 'search/search.html'      def get(self, request, *args, **kwargs):         self.bookmarks = []         self.show_results = False         form = SearchForm(self.request.GET or None)         if form.is_valid():             self.show_results = True             self.bookmarks = Bookmark.objects.filter(title__icontains=form.cleaned_data['query'])[:10]          return self.render_to_response(self.get_context_data(form=form))       def get_context_data(self, **kwargs):         context = super(SearchPageView, self).get_context_data(**kwargs)         context.update({             'show_tags': True,             'show_user': True,             'show_results': self.show_results,             'bookmarks': self.bookmarks         })         return context 

I leave this here to someone with same question :)

like image 311
Jesus Rodriguez Avatar asked Jan 18 '12 00:01

Jesus Rodriguez


People also ask

How do you use class based views in Django?

A view is a callable which takes a request and returns a response. This can be more than just a function, and Django provides an example of some classes which can be used as views. These allow you to structure your views and reuse code by harnessing inheritance and mixins.

How do you use decorators in class based views in Django?

You need to apply the decorator to the dispatch method of the class based view. This can be done as follows: class ProfileView(View): @youdecorator def dispatch(self,request,*args,**kwargs): return super(ProfileView,self). dispatch(request,*args,**kwargs) //Rest of your code.

What is class based view and function based view?

Class-Based ViewsClass-based views are the alternatives of function-based views. It is implemented in the projects as Python objects instead of functions. Class-based views don't replace function-based views, but they do have certain advantages over function-based views.

What are the HTTP methods supported by the class-based views?

The class-based views (view classes that extend the View class) get built-in responses to unsupported methods (POST, PUT, PATCH, etc.) and get support for the OPTIONS HTTP method too. All that is required to support other HTTP methods is to implement the same named method on the view class.

What is a class-based view?

Class based views simplify the use by separating GET, POST requests for a view. They do not replace function-based views, but have certain differences and advantages when compared to function-based views:

What is the difference between class-based and generic view classes?

The class-based views (view classes that extend the Viewclass) get built-in responses to unsupported methods (POST, PUT, PATCH, etc.) and get support for the OPTIONSHTTP method too. All that is required to support other HTTP methods is to implement the same named method on the view class. There are also generic class-based views.

What is a class based view in Python?

Using Class Based Views At its core, a class-based view allows you to respond to different HTTP request methods with different class instance methods, instead of with conditionally branching code inside a single view function. So where the code to handle HTTP GET in a view function would look something like: Python3


2 Answers

The default behaviour of the FormView class is to display an unbound form for GET requests, and bind the form for POST (or PUT) requests. If the bound form is valid, then the form_valid method is called, which simply redirects to the success url (defined by the success_url attribute or the get_success_url method.

This matches the example quite well. You need to override the form_valid method to create the new User, before calling the superclass method to redirect to the success url.

class CreateUser(FormView):     template_name = 'registration/register.html'     success_url = '/accounts/register/success/'     form_class = RegistrationForm      def form_valid(self, form):         user = User.objects.create_user(                 username=form.cleaned_data['username'],                 password=form.cleaned_data['password1'],                 email=form.cleaned_data['email']         )         return super(CreateUser, self).form_valid(form) 

Your first example does not match the flow of FormView so well, because you aren't processing a form with POST data, and you don't do anything when the form is valid.

I might try extending TemplateView, and putting all the logic in get_context_data. Once you get that working, you could factor the code that parses the GET data and returns the bookmarks into its own method. You could look at extending ListView, but I don't think there's any real advantage unless you want to paginate results.

like image 183
Alasdair Avatar answered Oct 09 '22 22:10

Alasdair


Note, an answer here (Updating context data in FormView form_valid method?) solved this problem like this:

class ContextFormView(FormView):     template_name = 'some_template.html'     success_url = '...'     form_class = ClassOfTheForm      def get(self, request, *args, **kwargs):         form_class = self.get_form_class()         form = self.get_form(form_class)         context = self.get_context_data(**kwargs)         context['form'] = form         return self.render_to_response(context)      def post(self, request, *args, **kwargs):         form_class = self.get_form_class()         form = self.get_form(form_class)         if form.is_valid():             return self.form_valid(form, **kwargs)         else:             return self.form_invalid(form, **kwargs)       def form_invalid(self, form, **kwargs):         context = self.get_context_data(**kwargs)         context['form'] = form         # here you can add things like:         context[show_results] = False         return self.render_to_response(context)      def form_valid(self, form, **kwargs):         context = self.get_context_data(**kwargs)         context['form'] = form         # here you can add things like:         context[show_results] = True         return self.render_to_response(context) 

This worked perfect for me. (same issue as in this question)

As stated above, this is not my solution, if you want to give somebody credits, go to the answer in the link and give this person the credits! (Updating context data in FormView form_valid method?)

like image 36
michel.iamit Avatar answered Oct 10 '22 00:10

michel.iamit