How to write separate views for GET and POST - django

First of all I want both views use exact same URL because I don't want to make my URLConf more complicated. I want separate views for GET and POST to make my code cleaner. The code is something like this:
def view2 (request):
# handle POST request, possibly a ajax one
return HTTPRESPONSE(json_data, mimetype="Application/JSON")
def view1 (request):
if method == POST:
view2(request)
# What should I return here???
else:
# handle GET
return render(request, template, context)
My question is about the # What should I return here??? line. If I don't put a return there, error occurs:
not returning http response
But I already return an HTTP response in view2. How can I make this work?

Another, probably a bit cleaner way would be using class-based views
from django.views.generic import TemplateView
class View1(TemplateView):
def get(self, request, *args, **kwargs):
"""handle get request here"""
def post(self, request, *args, **kwargs):
"""handle post request here"""
def head(self, request, *args, **kwargs):
"""handle head request here. Yes, you can handle any kind of requests, not just get and post"""
Of course you can add common methods, __init__ (which is useless unless you are sure what you are doing), apply login_required (see this SO question) and pretty much everything you can do with django views (e.g. apply middleware, permissions, etc.) and python classes (e.g. inheritance, metaclasses/decorators, etc.)
Also, there's a whole bunch of generic class based view coming with Django to address common situations like list page, details page, edit page, etc.

You need to return the results of view2:
def view1 (request):
if request.method == 'POST':
return view2(request)
else:
# handle GET
return render(request, template, context)

Related

Django Custom Permission method or decorator

I am many views and more than one user type. I want some views that can be seen by specific user types and other users cant see this.
For example, only company see this views and for this i did that like this below:
#login_required
def only_company_can_view(request):
if not Company.objects.filter(owner_id=request.user.id).exists():
return HttpResponse('Permission Denied. Only Company can see this')
# > rest of the logic
return render(request, 'template.html')
and above this working very well and solves my problem but i don't like this. coz, i don't want to write every time for the rest of the views for the company related views.
So i am finding a solution so that i can use decorator or something else that are best practice
Can anyone help me in this case?
You can wrap the logic in a decorator:
from django.core.exceptions import PermissionDenied
from functools import wraps
def requires_company(view):
#wraps(view)
def _view(request, *args, **kwargs):
if not Company.objects.filter(owner_id=request.user.id).exists():
raise PermissionDenied
return view(request, *args, **kwargs)
return _view
Then you use the decorator with:
#login_required
#requires_company
def only_company_can_view(request):
# … rest of the logic …
return render(request, 'template.html')

How do I test Django views when they don't return values

I'm new to Django. I've worked through some tutorials, written some models, views, forms etc. but I don't understand how they can be tested since either nothing is returned, or whatever is returned is so tightly bound it doesn't make sense in terms of the test.
For example here are two view classes:
class ListBlogPostView(ListView):
model = BlogPost
template_name = 'app/blogpost_list.html'
class CreateBlogPostView(CreateView):
model = BlogPost
template_name = 'app/blogpost_edit.html'
form_class = BlogPostForm
def get_success_url(self):
return reverse('blogpost-list')
def get_context_data(self, **kwargs):
context = super(CreateBlogPostView, self).get_context_data(**kwargs)
context['action'] = reverse('blogpost-new')
return context
The first, ListBlogPostView, doesn't return anything. How can I ever check that this is working correctly?
The second has a couple of functions but their return values are not things I can test with an assert
How could I ever use a TDD approach to Django?
I'm used to using nunit and MS 'unit' tests in Visual Studio, mocking etc
Actually, Django's generic class-based views are not called generic for no reason.
You can view the source of the both ListView and CreateView.
The ListView has a GET method handler:
class BaseListView(MultipleObjectMixin, View):
"""A base view for displaying a list of objects."""
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
# ...
context = self.get_context_data()
return self.render_to_response(context)
Which returns a valid Django response and can be tested in tests.py.
If you look at the CreateView, it inherits from BaseCreateView (just like ListView inherits from BaseListView):
class BaseCreateView(ModelFormMixin, ProcessFormView):
"""
Base view for creating an new object instance.
Using this base class requires subclassing to provide a response mixin.
"""
def get(self, request, *args, **kwargs):
# ...
def post(self, request, *args, **kwargs):
# ...
Which also inherits from ProcessFormView:
class ProcessFormView(View):
"""Render a form on GET and processes it on POST."""
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
The GET request will result in a valid response. As you see, the POST method handler here returns either self.form_valid(form) or self.form_invalid(form) depending on the form status.
You can see the source of these two methods in ViewMixin:
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
"""If the form is invalid, render the invalid form."""
return self.render_to_response(self.get_context_data(form=form))
Both of these methods return a valid testable Django responses.
In conclusion, both of your ListBlogPostView and CreateBlogPostView can be directly tested in tests.py. You just need to have a more detailed look at the implementation of Django's generic views. The power of open-source!
Testing view unlike function most of the time will not have a return value. The way that I go about doing it is to assert the html response.
So for the ListBlogPostView it depends on what is in the blogpost_list.html template.
A general view test should look like this:
class ListBlogPostViewTest(TestCase):
def test_blogpost_list_view(self):
response = self.client.get(reverse('blogpost-list'))
html = response.content.decode('utf8')
self.assertTrue(html.startswith('<html>'))
self.assertIn('<title>BlogPost lists</title>', html)
self.assertTrue(html.endswith('</html>'))
For view that have context you can actually check if it is being retrieved and passed correctly to view.
blogPost = BlogPost.object.get(id=1)
self.assertEqual(response.context['blogPost'].name, blogPost.name)
How could I ever use a TDD approach to Django?
As for TDD, you just have to test the html view first before creating it. It really depend on how detail you like to test and find the balance for it. I prefer to test mainly on the context being set and important html element is in the view.
You can still surely test lot of parameters -
status code on get and post request
variables in context data (such as form)
assert template used
creation of object on post request in case of create view
check for permissions using status codes
The thing is tests on django views are technically integration tests. As long as your tests are granular enough, means you don't test code for forms or models in views, I don't see any problem provided you follow Classical TDD.

Django: Handing POST and GET requests in view

I have a django app that processes both POST and GET requests.
What is "the best" way to design the view? Should I have separate methods to handle each type of request? Or should I just use one method? Or should the methods be dependent on the functionality?
Thanks in advance.
You could use function based views or Class based views:
In the first case:
# function based views
def my_view(request):
if request.method == 'POST':
# Handle post method
else: # request.method == 'GET'
# Handle get method
In the second case:
# Class based views
class MyView(View): # Use the view that fix your needs
def get(self, request, *args, **kwargs):
# Handle get method
return HttpResponse()
def post(self, request, *args, **kwargs):
# Handle post method
return HttpResponse()

Get request.session from a class-based generic view

Is there a way to get request.session from inside a class-based view?
For instance, I have
from django.views.generic.edit import FormView
class CreateProfileView(FormView):
def form_valid(self, form):
# --> would like to save form contents to session here
return redirect(self.get_success_url())
The only thing I can think of would be override as_view by adding
def as_view(self, request, *args, **kwargs):
self.session = request.session
super(CreateProfileView, self).as_view(request, *args, **kwargs)
to the class. But that seems ugly. Is there another way?
You have access to self.request from anywhere within the class (and therefore self.request.session)
https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-display/#dynamic-filtering
The key part to making this work is that when class-based views are called, various useful things are stored on self; as well as the request (self.request) this includes the positional (self.args) and name-based (self.kwargs) arguments captured according to the URLconf.

Redirect from Generic View DetailView in Django

I'm using Django's class based DetailView generic view to look up an object for display. Under certain circumstances, rather than displaying the object, I wish to back out and issue a HTTP rediect instead. I can't see how I go about doing this. It's for when a user hits an object in my app, but without using the canonical URL. So, for example, on StackOverflow URLs take the form:
http://stackoverflow.com/<content_type>/<pk>/<seo_friendly_slug>
eg:
http://stackoverflow.com/questions/5661806/django-debug-toolbar-with-django-cms-and-django-1-3
You can actually type anything as the seo_friendly_slug part and it will redirect you to the correct canonical URL for the object looked up via the PK.
I wish to do the same in my DetailView. Retrieve the object, check that it's the canonical URL, and if not redirect to the item's get_absolute_url URL.
I can't return an HttpResponseRedirect in get_object, as it's expecting the looked up object. I can't seem to return it from get_context_data, as it's just expecting context data.
Maybe I just need to write a manual view, but I wondered if anyone knew if it was possible?
Thanks!
Ludo.
This isn't a natural fit for DetailView. To do this you need to override the get method of BaseDetailView, which looks like:
class BaseDetailView(SingleObjectMixin, View):
def get(self, request, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
So in your class you'd need to provide a new get method which did the URL check between fetching the object and setting up the context. Something like:
def get(self, request, **kwargs):
self.object = self.get_object()
if self.request.path != self.object.get_absolute_url():
return HttpResponseRedirect(self.object.get_absolute_url())
else:
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
As you end up overriding so much of the functionality it becomes questionable whether it's worth actually using a generic view for this, but youknow.
Developing on Rolo's answer and comments, I came up with the following generic view to serve this purpose:
from django import http
from django.views import generic
class CanonicalDetailView(generic.DetailView):
"""
A DetailView which redirects to the absolute_url, if necessary.
"""
def get_object(self, *args, **kwargs):
# Return any previously-cached object
if getattr(self, 'object', None):
return self.object
return super(CanonicalDetailView, self).get_object(*args, **kwargs)
def get(self, *args, **kwargs):
# Make sure to use the canonical URL
self.object = self.get_object()
obj_url = self.object.get_absolute_url()
if self.request.path != obj_url:
return http.HttpResponsePermanentRedirect(obj_url)
return super(CanonicalDetailView, self).get(*args, **kwargs);
This is used in the same manner as the normal DetailView, and should work for any model which implements get_absolute_url correctly.