I have found this snippet in the django codebase:
# Add support for browsers which only accept GET and POST for now.
def post(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
What does this mean? Do browsers delete resources with GET / POST requests? Why? Can somebody provide a rationale / history / link for why this might be so?
It's for django.views.generic.edit.DeleteView. Your code is from DeletionMixin, and DeleteView inherit this mixin for delete object.
here's self.delete() code
def delete(self, request, *args, **kwargs):
"""
Call the delete() method on the fetched object and then redirect to the
success URL.
"""
self.object = self.get_object()
success_url = self.get_success_url()
self.object.delete()
return HttpResponseRedirect(success_url)
You can check about DeleteView in docs (here).
Basically, DeleteView receive both get and post for deleting object. That's why
Related
i am currently trying to return one html page from my django rest framework setup:
#action(detail=True)
#renderer_classes((TemplateHTMLRenderer,))
def confirmation(self, request, *args, **kwargs):
user = self.get_object()
print(request.accepted_renderer) -> BrowsableAPIRenderer | WHY ?
// do some business logic
return Response({'user': user}, template_name='confirmation.html')
But browser prints error:
Object of type 'User' is not JSON serializable
So my question is, why does DRF use BrowsableAPIRenderer when i specified TemplateHTMLRenderer?
Can anybody help me out?
TemplateHTMLRenderer is very poorly documented, so i had to ask this question..
Thanks and Greetings!
This seems the renderer_classes decorator is not working properly with CBV. Anyway, I found one workaround/DRF way to do it.
Override the get_renderers() method
class Foo(viewsets.ModelViewSet):
# your code
def get_renderers(self):
if self.action == 'confirmation':
return [TemplateHTMLRenderer()]
else:
return super().get_renderers()
#action(detail=True)
def confirmation(self, request, *args, **kwargs):
user = self.get_object()
return Response({'user': user}, template_name='confirmation.html')
I am using Django DeleteView to delete items in my database. I have use separate template to show the delete confirmation message, but when I press the yes button I get ProtectedError since customer table is linked with Accounts table. Hence I want to handle the ProtectedError and give user another message in the same template.
Here is the code I have used to perform the delete:
class Customer(DeleteView):
#Delete Customers
model = Customer
template_name = 'project_templates/delete_customer.html'
def get_success_url(self):
return reverse('inactive_customers')
It would be really great if someone can suggest me a method to handle this situation.
You should be able to catch the exception. When you look at the DeletionMixin:
https://github.com/django/django/blob/master/django/views/generic/edit.py#L256
You can override the post method and achieve something like:
def post(self, request, *args, **kwargs):
try:
return self.delete(request, *args, **kwargs)
except ProtectedError:
# render the template with your message in the context
# or you can use the messages framework to send the message
Hope this helps.
I had the same issue, and overriding the delete() method worked for me on Django 3.2. I used the messages framework to display the error message - that requires additional setup (see https://docs.djangoproject.com/en/dev/ref/contrib/messages/):
from django.db.models import ProtectedError
from django.contrib import messages
.
.
.
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
success_url = self.get_success_url()
try:
self.object.delete()
except ProtectedError:
messages.error(request, "custom error message")
finally:
return redirect(success_url)
EDIT:
The better solution for me was just using a permissions system, especially since I needed other types of controlled access to objects. I now use Django-guardian to help with object level permissions like this.
Original:
I'm expanding a bit on the standard django book guide by letting users upload stories, as well as having author, publisher, etc. I'm attempting to only let authors (creators) of a story use the updateview, with other users being redirected away.
Modifying get_object in the UpdateStory view set it off, but the traceback goes through my StoryForm init for some reason. The error is 'HttpResponseRedirect' object has no attribute '_meta'
views.py
class UpdateStory(LoginRequiredMixin, UpdateView):
model = Story
template_name = 'stories/story_update.html'
form_class = StoryForm
def get_object(self, queryset=None):
obj = super(UpdateStory, self).get_object()
if not obj.author == self.request.user:
return redirect(obj)
return obj
forms.py
class StoryForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StoryForm,self).__init__(*args, **kwargs)
I'm still new, so it might be obvious, but I've been looking for a couple hours and I'm stumped.
The best approach would be to use another mixin, something like this:
class AuthorRequiredMixin(object):
def dispatch(self, request, *args, **kwargs):
if self.object.author != self.request.user:
return HttpResponseForbidden()
return super(AuthorRequiredMixin, self).dispatch(request, *args, **kwargs)
Of course you can return another HttpResponse, but keep in mind what is the proper use here.
http://ccbv.co.uk/projects/Django/1.5/django.views.generic.edit/UpdateView/
Go through the above link to understand how UpdateView works. get_object is supposed to return the model instance, It is not supposed to return HttpResponseRedirect object, that's why you are getting that error.
Try doing the check in dispatch method like the following.
def dispatch(self, request, *args, **kwargs):
""" Making sure that only authors can update stories """
obj = self.get_object()
if obj.author != self.request.user:
return redirect(obj)
return super(UpdateStory, self).dispatch(request, *args, **kwargs)
PS: I guess it is not recommended to override dispatch. But as you
have to do the check on both get and post methods, overriding dispatch
will be easier.
This specific issue is considered in Django anti-patterns.
We're encouraged to filter the QuerySet to only retrieve objects where the user is the author, as opposed to UserPassesTestMixin.
In OP's case it would actually be quite similar to what they have there
from django.contrib.auth.mixins import LoginRequiredMixin
class UpdateStory(LoginRequiredMixin, UpdateView):
model = Story
# …
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(
author=self.request.user
)
i have a templateview,the code is here so how do
class MyTemplateView(TemplateView):
def get_context_data(self, **kwargs):
context = super(UBaseTemplateView, self).get_context_data(**kwargs)
# i want to redirect another url in here
# how to do it
return context
Well, you would so something like this:
class MyTemplateView(TemplateView):
def get(self, request, *args, **kwargs):
return HttpResponseRedirect('/<your path here>/')
You can learn more about it here, and in more detail here.
If you want to pass on post data, then all you have to do is this:
class MyTemplateView(TemplateView):
def get_context_data(self, **kwargs):
return HttpResponseRedirect(reverse('/<your url here>/', [params]))
You can also do this using the post function.
class MyTemplateView(TemplateView):
def post(self, request, *args, **kwargs):
# do what you want with post data
# you can get access via request.POST.get()
return HttpResponseRedirect('/<your url>/')
# Or use the above example's return statement, if you want to pass along parameters
A more generic way to do this is with dispatch() as described here. More background on what dispatch does is in the Django docs.
The advantage is that this is going to work whatever HTTP method (GET, PUT, POST etc) has been specified on the request, whereas the get() function's only going to get called if the method is GET.
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.