Return httpresponse outside of post - django

Is there a way to stop execution and return an httpresponse in a function other than post, get, put?
So for example
class MyClass(View):
def post(self, request, *args, **kwargs):
test_some_things()
do_some_other_stuff()
return HttpResponse(..)
def test_some_things(self):
if test_fails:
return HttpResponse(..)
else:
return 1
I want to be able to end execution if test_fails and just return the response. But the above doesn't seem to work...

Although possible, it clearer to separate these things: have one method that tests things and returns the outcome of that (boolean). Then, check whether the tests succeeded and return the response in the view.
This will make your code a lot easier to maintain and test in the long run.
What your propose is possible but it won't lead to the simplest solution to this situation.

Related

Two Ways to Handle a Request - Just Written Differently

I was looking at some different code on github and came across something that was confusing. Is there any differences between the code below?
class RandomView(View):
def get(self, *args, **kwargs):
return render(self.request, 'random_template.html)
Versus
class RandomView(View):
def get(self, request, *args, **kwargs):
return render(request, 'random_template.html)
To me this would do the same thing but to be fair my knowledge is pretty limited. Is one considered bad practice? Or maybe just preference?
They point to the same object. self.request is assigned in the factory
function that is returned from View.as_view(). I would guess that the
reason for passing the request argument explicitly is to make it
easier to convert between class based views and function views. And it
also makes it more clear that methods such as get uses the request
object.
I recommend that you follow the convention from the django source code
and examples in the docs, and use the request that is passed as an
argument instead of self.request.
found this answer on reddit

Explain return super().get(request, *args, **kwargs)

Hi I want to understand this line of code:
return super().get(request, *args, **kwargs)
This line is part of a method in class based view in views.py file , for example
class HomePage(TemplateView):
template_name = "index.html"
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return HttpResponseRedirect(reverse("afterlogin"))
return super().get(request, *args, **kwargs)
I know this method will check if the current user is authenticated and redirect to a different tempalte (afterlogin) in this case if a user is logged in , otherwise it will stay in index.html and then this function will execute this line return super().get(request, *args, **kwargs) anyway {I don't understand this line} .for example, if we were redirect to afterlogin page for example,then what this last line will do then?? .. this is the general idea that I understand but I want to understand the details of the code like:
If anyone can explain:
1- why I have these parameters: (self, request, *args, **kwargs) in get function?
2- when this method : def get(self, request, *args, **kwargs): will be triggered or called?
3-
explain the last line in the get method: return super().get(request, *args, **kwargs) step by step and what is its purpose.
I will be grateful if anyone can explain in details because I am still learning.. Thanks a lot
The super() function represents the parent class of the current one (in this case, this means TemplateView).
In your code, this means that get() will, as you noted, first check if the user is authenticated; if it is, then it will redirect to the afterlogin URL. If the user is not authenticated however, the function will not return, so it will go on and call the get() method of TemplateView (with the same arguments that have been passed to your get()), that will output whatever it would normally output if you hadn't inherited it.
To see what exactly that would be, you can navigate to TemplateView in the source code and read its get() method.
About your second question: when a user navigates to a URL you defined in your project, Django will check the method used for the request (if they're just viewing the page for the first time, this will be GET; another example is POST, which you probably heard before) and call the method of your view (HomePage) with the corresponding name, get(). If you also define a post() method, that will be called when the user requests the page with a POST method, for example by submitting a form with the method="post".
About your first question: request is an HTTP request object that is passed to the function to basically tell them what the user('s browser) has requested, what it wants to do. *args is a list of all other positional arguments that might have been added to the get() call. The same states for **kwargs, which is instead a dictionary of keyword arguments. These can very well be empty, but it's good that we pass them on to the upper level so that we don't loose any functionality that may be needed.
Just as an example, if you go to a url like mysite.com/page/search?=myquery, this would be a GET request, and it would include a keyword argument with key search and value myquery, so your **kwargs would look like: kwargs = {"search": "myquery"}. In the same way, the contents of a form submitted by POST would be available to your method to do what you need to do with them.

How to write a basic try/except in a Django Generic Class View

I'd like to write an except clause that redirects the user if there isn't something in a queryset. Any suggestions welcome. I'm a Python noob, which I get is the issue here.
Here is my current code:
def get_queryset(self):
try:
var = Model.objects.filter(user=self.request.user, done=False)
except:
pass
return var
I want to do something like this:
def get_queryset(self):
try:
var = Model.objects.filter(user=self.request.user, done=False)
except:
redirect('add_view')
return var
A try except block in the get_queryset method isn't really appropriate. Firstly, Model.objects.filter() won't raise an exception if the queryset is empty - it just returns an empty queryset. Secondly, the get_queryset method is meant to return a queryset, not an HttpResponse, so if you try to redirect inside that method, you'll run into problems.
I think you might find it easier to write a function based view. A first attempt might look like this:
from django.shortcuts import render
def my_view(request):
"""
Display all the objects belonging to the user
that are not done, or redirect if there are not any,
"""
objects = Model.objects.filter(user=self.request.user, done=False)
if not objects:
return HttpResponseRedirect("/empty-queryset-url/")
return render(request, 'myapp/template.html', {"objects": objects})
The advantage is that the flow of your function is pretty straight forward. This doesn't have as many features as the ListView generic class based view (it's missing pagination for example), but it is pretty clear to anyone reading your code what the view is doing.
If you really want to use the class based view, you have to dig into the CBV documentation for multiple object mixins and the source code, and find a suitable method to override.
In this case, you'll find that the ListView behaviour is quite different to what you want, because it never redirects. It displays an empty page by default, or a 404 page if you set allow_empty = False. I think you would have to override the get method to look something like this (untested).
class MyView(ListView):
def get_queryset(self):
return Model.objects.filter(user=self.request.user, done=False)
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
if len(self.object_list == 0):
return HttpResponseRedirect("/empty-queryset-url/")
context = self.get_context_data(object_list=self.object_list)
return self.render_to_response(context)
This is purely supplemental to #Alasdair's answer. It should really be a comment, but couldn't be formatted properly that way. Instead of actually redefining get on the ListView, you could override simply with:
class MyView(ListView):
allow_empty = False # Causes 404 to be raised if queryset is empty
def get(self, request, *args, **kwargs):
try:
return super(MyView, self).get(request, *args, **kwargs)
except Http404:
return HttpResponseRedirect("/empty-queryset-url/")
That way, you're not responsible for the entire implementation of get. If Django changes it in the future, you're still good to go.

Django - Is it possible to make a redirect from a function in a view?

I try to call a function which checks a rule and if the rule is not fulfilled I'd like to skip the rest of the view code and just return a HttpResponse Error.
I'd like to put all the escaping logic in a function because I need it at several points all over my project.
I tried to do something like this:
def myView(request):
checkFunction()
And:
def checkFunction():
#do stuff
return HttpResponse(status=403)
But it just don't work (no wonder)...
Any ideas how to do this right?
Thanks
Ron
Your mistake is, in myView function, you call your checkFunction, but you do not use the return value of checkFunction so your return value of checkFunction return HttpResponse(status=403) got lost and never returned within myView.
It might be like:
def myView(request):
result = checkFunction()
if result:
return result
#if no problem, keep running on...
def checkFunction():
#do stuff
if something_goes_wrong:
return HttpResponse(status=403)
# you do not need to return anything if no error occured...
So, if nothing goes wrong, then checkFunction will not return anything, and result will be None and if result: block will not be executed. If you return a response, than your view will return that response (in your situation, HttpResponse(status=403))...
UPDATE: Then you can do this....
def checkFunction(request):
#do stuff
if something_goes_wrong:
return HttpResponse(status=403)
elif some_other_issue:
return HttpResponse(....)
else: #no problems, everything is as expected...
return render_to_response(...) # or any kind of response you want
def myView(request):
return checkFunction(request)
this way, your view will return what your checkFunction returns...
Also, passing request object to your checkFunction might be necessary since you wish to creeate your response there. You might need it.
You can use a decorator for applying a logic to a view with possible redirect(if you can really put your logic outside the view).
def check_my_logic(view):
def wrap(request, *args, **kwargs):
# do some logic here
if all_ok:
return view(request, *args, **kwargs)
else:
return HttpResponse(status=403)
return wrap
and then in views.py:
from mydecorators import check_my_logic
#check_my_logic
def some_view(request):
....

Django: return several views from a single URL without redirection

With function based Django view it was simple to switch between several different views based on a condition, e.g. something like:
def base_view(request):
if some_condition():
return foo_view(request)
else:
return bar_view(request)
I can't find a simple way to do the same with the new class-based generic views. The only way I can think of is to redisrect, which I would like to avoid for various reasons:
def base_view(request):
if some_condition():
return redirect(reverse("name_of_url_to_class-based_view_foo"))
else:
return redirect("/url_to_class-based_view_bar/")
Any suggestions?
This is equivalent to your example with class based views.
class FooView(View):
pass
class BarView(View):
pass
class BaseView(View):
# staticmethod to avoid adding 'self' to the arguments
foo_view = staticmethod(FooView.as_view())
bar_view = staticmethod(BarView.as_view())
def dispatch(self, request, *args, **kwargs):
if some_condition():
return self.foo_view(request, *args, **kwargs)
else:
return self.bar_view(request, *args, **kwargs)
Even though the Django docs do say that the function based generic views are now deprecated I think the only reason to switch would be if you're writing less code.
If you're still set on switching, you'll want to first identify which class based views or mixins are most appropriate (single object, multiple objects, date based, forms, etc.). If the conditional was used to select a function that returns different context data / template to provide to the view, you can push the conditional down into an overridden get_queryset|get_context_data|get_object|get_template_names depending on your use case.
For example,
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(BaseView, self).get_context_data(**kwargs)
# Add in the publisher
if some_condition():
context['some_data'] = ...
else:
context['other_data'] = ...
return context
If all else fails and you're still determined to have class based views, you could probably also override get(self, request, *args, **kwargs) and do the switching there to the appropriate method. The detailed docs are getting better but I've still found myself poking through the source code to figure out how to accomplish what I want.