Django redirect from inside a view - python

I'm using django 1.11.8.
I need to perform some check on the requested view and inside it redirect the user to another Http page, but this code fail, as the redirect is ignored.
What am I missing?
class ThisCreateView(CreateView):
def get_form_kwargs(self):
if self.kwargs['x'] == 1:
return redirect(reverse('this_app:this_list'))
return kwargs

You can't return a redirect from the get_form_kwargs method. Its job is to return a dictionary of kwargs for the form.
You could move this check to the dispatch method.
def dispatch(self, request, *args, **kwargs):
if kwargs['x'] == 1:
# note you don't need to call reverse when you use redirect
return redirect('this_app:this_list')
return super(ThisCreateView, self).dispatch(*args, **kwargs)

Solution from #Alasdair worked perfectly for me albeit with a small change:
return super(ThisCreateView, self).dispatch(request, *args, **kwargs)

Related

Django: Redirect from function doesn't work

Anyone can tell me, why my redirect doesn't work? I can see "SHOULD REDIRECT 2" in my terminal, but somehow the redirect never happens. Redirect import is there.
helpers.py
def get_reserved_items_or_redirect(request):
session_order_reference = request.session.get('order_reference')
if request.session.get('order_reference'):
reserved_items = ReservedItem.objects.filter(
order_reference=session_order_reference
)
print("session_order_reference: ", session_order_reference)
if not reserved_items:
print( "SHOULD REDIRECT 1")
return redirect('website:index')
else:
print( "SHOULD REDIRECT 2")
return redirect('website:index')
views.py
class CheckoutView(TemplateView):
# To check make an order
template_name = "checkout/checkout_new.html"
# make it a decorator
def dispatch(self, request, *args, **kwargs):
get_reserved_items_or_redirect(request)
return super().dispatch(request, *args, **kwargs)
In your CheckoutView, you only call get_reserved_items_or_redirect(request).
To make it works, just refactor your dispatch method to return the return value of this method.
def dispatch(self, request, *args, **kwargs):
if some_value: # Check if you need to perform redirect
return get_reserved_items_or_redirect(request)
else: # If redirect is not required
return super().dispatch(request, *args, **kwargs)

What is dispatch used for in django?

I have been trying to wrap my head around the dispatch method, particularly in Django. However, I cannot seem to figure out exactly what it does. I tried to gain an understanding from the Django docs but didn't find them to informative on this topic. Per my understanding it is a listener that listens to all events happening on a page but I am not sure if this is the case?
class OrderDetail(DetailView):
model = Order
def **dispatch**(self, request, *args, **kwargs):
try:
user_check_id = self.request.session.get("user_checkout_id")
user_checkout = UserCheckout.objects.get(id=user_check_id)
except UserCheckout.DoesNotExist:
user_checkout = UserCheckout.objects.get(user=request.user)
except:
user_checkout = None
obj = self.get_object()
if obj.user == user_checkout and user_checkout is not None:
return super(OrderDetail, self).dispatch(request, *args, **kwargs)
else:
raise Http404
The dispatch method takes in the request and ultimately returns the response. Normally, it returns a response by calling (ie, dispatching to) another method like get. Think of it as a middleman between requests and responses.
Normally, it simply decides what method in the class (e.g. get(),post(), etc) should be used (ie, dispatched) based on the HTTP method that was used in the request. Something like
def dispatch(self, request, *args, **kwargs):
if request.method == 'GET':
return self.get(*args, **kwargs)
elif request.method == 'POST':
return self.post(*args, **kwargs)
elif #... and so on
You can use your own dispatch method to change this behavior to call whatever methods you want that should return the HTTP response or even 'intercept' and modify the arguments that ultimately reach those methods. For example, you might use this to block/filter certain kinds of requests or even inject arguments...
def dispatch(self, request, *args, **kwargs):
"""Updates the keyword args to always have 'foo' with the value 'bar'"""
if 'foo' in kwargs:
# Block requests that attempt to provide their own foo value
return HttpResponse(status_code=400)
kwargs.update({'foo': 'bar'}) # inject the foo value
# now process dispatch as it otherwise normally would
return super().dispatch(request, *args, **kwargs)
But the key concept is that it's the entry point for requests and ultimately responsible for returning the response.
When a request url matches a url in your urls.py file, django passes that request to the view you specified. The request can only be passed to callable functions. This is why when using class-based views, you use the as_view() method. The as_view() method returns a function that can be called.
This function then creates an instance of the view class and calls it's dispatch() method. The dispatch method then looks at the request and decides whether the GET or POST method of the view class should handle the request.

Django redirectView returns None

I'm trying to use redirect view in django, but I keep getting this error:
The view gp_accountant.gp_taxes.views.TaxRateDeleteView didn't return an HttpResponse object. It returned None instead.
I've based my code on this question.
Anyone knows where the problem lies?
This is my url file (path: get-paid/gp_accountant/gp_taxes/urls.py):
app_name = 'gp_taxes'
urlpatterns = [
url(r'^$', TaxesListView.as_view(), name='list'),
url(
r'^delete_rate/(?P<pk>\d+)/$',
TaxRateDeleteView.as_view(pattern_name='accountant:gp_taxes:update'),
name='delete_rate'
),
]
The TaxRateDeleteView:
class TaxRateDeleteView(RedirectView):
def dispatch(self, request, *args, **kwargs):
TaxRate.objects.get(id=int(kwargs['pk'])).delete()
#FazilZaid almost correct, you need to return last line of his answer. Problem is that your dispatch doesn't return anything in general it should return HttpResponseRedirect so to make it work with super call you need to provide success_url to your view
class TaxRateDeleteView(RedirectView):
success_url = # <- your url here
def dispatch(self, request, *args, **kwargs):
TaxRate.objects.get(id=int(kwargs['pk'])).delete()
return super(TaxRateDeleteView,self).dispatch(request, *args, **kwargs)
Also as per this comment by #Alasdair
Using a redirect view for deleting objects is a bad idea. You should not be deleting objects with get requests like this.
You should use https://docs.djangoproject.com/en/1.11/ref/class-based-views/generic-editing/#django.views.generic.edit.DeleteView which is right way to delete object, instead of RedirectView
Edit your view,
class TaxRateDeleteView(RedirectView):
def dispatch(self, request, *args, **kwargs):
TaxRate.objects.get(id=int(kwargs['pk'])).delete()
return super(TaxRateDeleteView,self).dispatch(request, *args, **kwargs)
Call super on dispatch method of your view.
You shouldn't be overriding the dispatch method.
Try something like this instead:
class TaxRateDeleteView(RedirectView):
def get_redirect_url(self, *args, **kwargs):
TaxRate.objects.get(id=int(kwargs['pk'])).delete()
return reverse('delete_rate')

Redirect using CBV's in Django

I believe this is a simple one, just can't spot the solution. I have a view that does a bit of work on the server then passes the user back to another view, typically the original calling view.
The way I'm rendering it now, the url isn't redirected, ie it's the url of the original receiving view. So in the case the user refreshes, they'll run that server code again.
class CountSomethingView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
# so some counting
view = MyDetailView.as_view()
return view(request, *args, **kwargs)
I strongly recommend not overriding get or post methods. Instead, override dispatch. So, to expand on Platinum Azure's answer:
class CountSomethingView(LoginRequiredMixin, RedirectView):
permanent = False
def get_redirect_url(self, **kwargs):
url = you_can_define_the_url_however_you_want(**kwargs)
return url
def dispatch(self, request, *args, **kwargs):
# do something
return super(CountSomethingView, self).dispatch(request, *args, **kwargs)
when a user does an action and I need to redirect him to the same page, first of all I use a templateView to display a simple "thanks" (for example) then provide a link to go back to the previous page with a simple {% url %}
for example :
from django.views.generic import CreateView, TemplateView
from django.http import HttpResponseRedirect
class UserServiceCreateView(CreateView):
form_class = UserServiceForm
template_name = "services/add_service.html"
def form_valid(self, form):
[...]
return HttpResponseRedirect('/service/add/thanks/')
class UserServiceAddedTemplateView(TemplateView):
template_name = "services/thanks_service.html"
def get_context_data(self, **kw):
context = super(UserServiceAddedTemplateView, self).\
get_context_data(**kw)
context['sentance'] = 'Your service has been successfully created'
return context
in the template thanks_service.html i use {% url %} to go back to the expected page
Hope this can help
Performing a redirect in a Django Class Based View is easy.
Simply do a return redirect('your url goes here').
However, I believe this isn't what you want to do.
I see you're using get().
Normally, when speaking about HTTP, a GET request is seldom followed by a redirect.
A POST request is usually followed by a redirect because when the user goes backwards you wouldn't want to submit the same data again.
So what do you want to do?
What I think you want to do is this:
def get(self, request, *args, **kwargs):
return render_to_response('your template', data)
or even better
def get(self, request, *args, **kwargs):
return render(request, self.template_name, data)
If you're creating or updating a model, consider inheriting from CreateView or UpdateView and specifying a success_url.
If you're really doing a redirect off of an HTTP GET action, you can inherit from RedirectView and override the get method (optionally also specifying permanent = False):
class CountSomethingView(LoginRequiredMixin, RedirectView):
permanent = False
def get(self, request, *args, **kwargs):
# do something
return super(CountSomethingView, self).get(self, request, *args, **kwargs)
Note that it's really bad practice to have a get action with side-effects (unless it's just populating a cache or modifying non-essential data). In most cases, you should consider using a form-based or model-form-based view, such as CreateView or UpdateView as suggested above.

Render Django view class to either string or response

I have a template that I want to be able to both serve directly and embed in arbitrary other templates in my Django application. I tried to create a view class for it that looks like this:
class TemplateView(View):
def get(self, request):
context = self._create_context(request)
return render_to_response('template.html', context)
def get_string(self, request):
context = self._create_context(request)
return render_to_string('template.html', context)
def _create_context(self, request):
context = {}
# Complex context initialization logic...
return context
I've wired get to my Django URLs. However, I haven't been able to figure out how to instantiate TemplateView so that I can call get_string from other views.
There must be a better way to go about doing this. Ideas?
Update: I've seen some folks talking about making a request internally and using response.content, which would save me from having to write the get_string method. So, perhaps a better question is: How do I make a request to TemplateView from another view?
I'd follow in django's CBV pattern: it determines via dispatch what method to return. By default based on request.method. Why not based on any other argument passed to dispatch()?
So subclass dispatch and give it a way to determine whether or not to return get_string.
def dispatch(self, request, *args, **kwargs):
if 'as_string' in kwargs:
return self.get_string(request)
return super(TemplateView, self).dispatch(request, *args, **kwargs)
response = TemplateView.as_view()(request, as_string=True)

Categories