how to configure redirect in DeleteView - django

when i try to delete some comment it first take me to the comment_delete_confirm.html, then redirect to the page what linked in success_url = '/blog/'. Problem become when i change success_url to something like 'post-detail' (becose i want after comment_delete_confirm return to the post), it cannot find this page, becose in the brauser url it looks like that: '127.0.0.1:8000/blog/post/18/comment_delete/post-detail'
this is my views.py and urls.py files:
class CommentDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Comment
success_url = 'post-detail'
# only the author can delete his post
# if not author try to delete post it gives 403 forbidden
def test_func(self):
comment = self.get_object()
if self.request.user == comment.user:
return True
return False
urlpatterns = [
path('', PostListView.as_view(), name='blog-home'),
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
path('post/new/', PostCreateView.as_view(), name='post-create'),
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
path('post/<int:pk>/comment/', add_comment, name='comment-create'),
path('post/<int:pk>/comment_update/', comment_update, name='comment-update'),
path('post/<int:pk>/comment_delete/', CommentDeleteView.as_view(), name='comment-delete')
]

You need to use the actual URL, not the name. You can use reverse_lazy for this.
from django.urls import reverse_lazy
class CommentDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Comment
success_url = reverse_lazy('post-detail')
Edit
If you need dynamic data, you can override the get_success_url method instead of defining the attribute directly. There you have access to self.object (because the method is called before the deletion is actually processed). Assuming your comment object has a post field which is a foreign key to Post:
def get_success_url(self):
return reverse('post-detail', kwargs={'pk': self.object.post_id})

Related

LoginRequiredMixin with redirect_field_name not redirecting

Using the LoginRequiredMixin it does not route to the 'redirect_field_name' I specify. Even though I see it appear in the url.
I've tried putting both urls ('user/update/') and named urls ('accounts:profile-update') in 'redirect_field_name' with the 'LoginRequiredMixin' but neither seem to make the redirect work.
I can get a redirect to work when I use the below, in src/settings.py
LOGIN_REDIRECT_URL = '/user/'
But I want to have custom redirects for different views.
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
# http://127.0.0.1:8000/user/update/
login_url = '/login/'
redirect_field_name = 'accounts:profile-update'
# redirect_field_name = '/user/update/'
# neither works above
model = Profile
fields = ['first_name', 'last_name', 'mobile_phone',]
template_name = 'accounts/profile_update.html'
success_url = reverse_lazy('accounts:my-profile-detail')
# PK required in UpdateView, making context['object']
def get_object(self, queryset=None):
if self.request.user.is_authenticated:
queryset = Profile.objects.get(user=self.request.user)
return queryset
# accounts/urls.py
app_name = 'accounts'
urlpatterns = [
# ... some more urls ...
path('update/', ProfileUpdateView.as_view(), name='profile-update'),
]
# src/urls.py
urlpatterns = [
path('', include('django.contrib.auth.urls')),
path('admin/', admin.site.urls),
path('user/', include('accounts.urls')),
]
# src/settings.py
# LOGIN_REDIRECT_URL = '/user/'
# If I uncomment this, it works, but all my login's redirect only to this URL if I use LoginRequiredMixin :(```
You have misunderstood what redirect_field_name does.
The redirect_field_name controls the name of the field, not the URL that you redirect to. For example setting redirect_field_name = 'nextpage' means that the LoginRequiredMixin will redirect to /login/?nextpage=/user/update/ instead of /login/?next=/user/update/.
You don't usually want to override redirect_field_name. It's easier to stick with the default value next.
The LoginRequiredMixin should automatically redirect to the previous page after logging in. If it doesn't after you have removed redirect_field_name and tried again, then the problem could be in your login view or template.

Django CreateView redirect to UpdateView if already have an object created

I have tried how to check(using get() function) inside CreateView. so when we try to access CreateView URL, this view will check is there an object has been created. if yes, it will redirect to that object UpdateView URL. but the problem is I don't know how to reverse it.
urls.py
app_name = 'product'
urlpatterns = [
url(r'^$', pglist, name='list'),
url(r'^create/$', pcreate, name='create'),
url(r'^(?P<slug>[\w-]+)/$', pdetail, name='detail'),
url(r'^(?P<slug>[\w-]+)/update/$', pupdate, name='update'),
url(r'^redire/$', ered, name='redire'),
views.py
CreateView class
def get(self, *args, **kwargs):
if self.model.objects.get(user=self.request.user):
return redirect("product:update", kwargs={'slug': ??? I HAVE NO IDEA HOW TO DOING THIS PART ???slug})
else:
return redirect("product:create")
if I change the line into ==> return redirect("pages:update"), CreateView URL show
NoReverseMatch at /create/
Reverse for 'update' with no arguments not found. 1 pattern(s) tried: ['(?P<slug>[\\w-]+)/update/$']
so, what it should be?
return redirect("product:update", kwargs={'slug': ??? I HAVE NO IDEA HOW TO DOING THIS PART ???slug})
Well I would hazard a guess you have a slug field on your model, and you could provide the slug to the reverse method's kwargs like so:
return redirect(
"product:update",
kwargs={'slug': self.model.objects.get(user=self.request.user).slug}
)

How to test get_success_url in CreateView in Django

I am trying to write a test for the get_success_url method in a CreateView, to make sure it redirects to the newly created page. But the response status code is 405 instead of 302 as I expected.
views.py
class BlogCreate(CreateView):
model = Blog
fields = [‘author’, 'title', ’post’]
def get_success_url(self):
return reverse_lazy('blog:blog-detail', kwargs={'slug': self.object.slug})
class BlogList(ListView):
model = Blog
ordering = ["-created"]
class BlogDetail(DetailView):
model = Blog
config/urls.py
from django.conf.urls import include, url
urlpatterns = [
url(r'^blog/', include('blog.url', namespace='blog')),
blog/urls.py
from django.conf.urls import url
from .views import BlogCreate, BlogList, BlogDetail, BlogEdit, BlogDelete
urlpatterns = [
url(r'^(?P<slug>[-\w]+)/$', BlogDetail.as_view(), name='blog-detail'),
url(r'^(?P<slug>[-\w]+)/edit$', BlogEdit.as_view(), name='blog-edit'),
url(r'^(?P<slug>[-\w]+)/delete$', BlogDelete.as_view(), name='blog-delete'),
url(r'^new$', BlogCreate.as_view(), name='blog-create'),
url(r'^$', BlogList.as_view(), name='blog-list'),
]
tests.py
class BlogCreateTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='john', password='123')
def test_create_success_url(self):
post = {‘author’: self.user,
'title': ‘new blog’,
‘article’: ‘text’,
}
response = self.client.post('/blog/new/', post)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, 'blog/new-blog/‘)
Are you trying to do a client.post() in your tests?
Remove the trailing slash from /blog/new/

Django DetailView occurs an error?

I Implemented very simple DetailView in Django 1.9.5:
class PostDetailView(DetailView):
Model = Post
template_name = "post/detail.html"
urls.py
from django.conf.urls import url
from chacha_dabang.views import *
urlpatterns = [
url(r'^$', PostListView.as_view(), name="post_list"),
url(r'^new/$', post_new, name="post_new"),
url(r'^(?P<pk>\d+)/$', PostDetailView.as_view(), name="post_detail"),
url(r'^(?P<pk>\d+)/edit$', post_edit, name="post_edit"),
url(r'^(?P<pk>\d+)/delete$', post_delete, name="post_delete"),
url(r'^(?P<pk>\d+)/comment/new/$', comment_new, name="comment_new"),
url(r'^(?P<pk>\d+)/comment/(?P<comment_pk>\d+)/edit$', comment_edit, name="comment_edit"),
]
Errors :
I don't know why it says I have to override query_set(). (As I Know, DetailView automatically set query according to pk)
If I used Function Based View like below,
def post_detail(request, pk):
post = Post.objects.get(pk=pk)
return render(
request,
'post/detail.html',
{
'post': post,
}
)
It totally works fine. Need your helps.
You capitalized model. It is not Model, it should be model.

Setting up Mimetype when using TemplateView in Django

Does anyboy know how do I set the desired mimetype when using TemplateView, as in:
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html')),
In this case, I want to set the mimtype as "text/plain"
For Django >= 1.5
TemplateView accepts a content_type argument.
Coping example from #Meilo
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html', content_type='text/plain')),
For Django < 1.5
I think that just calling TemplateView.as_view() is not posible but maybe i missed it (from the source),
but you can do your own class
class TextTemplateView(TemplateView):
def render_to_response(self, context, **response_kwargs):
response_kwargs['content_type'] = 'text/plain'
return super(TemplateView, self).render_to_response(context, **response_kwargs)
You can take a look to:
django.template.response => TemplateResponse
django.views.generic.base => TemplateView
And if you need something more dynamic:
from django.utils.decorators import classonlymethod
class ContentTypeTemplateView(TemplateView):
#classonlymethod
def as_view(cls, content_type='text/plain', **initargs):
setattr(cls, 'content_type', content_type)
return super(ContentTypeTemplateView, cls).as_view(**initargs)
def render_to_response(self, context, **response_kwargs):
response_kwargs['content_type'] = self.content_type
return super(ContentTypeTemplateView, self).render_to_response(context, **response_kwargs)
urlpatterns = patterns('',
url(r'^$', ContentTypeTemplateView.as_view(content_type='text/plain',
template_name='staticpages/test.html'),
name='index'),
)
Using a Mixin
from django.core.exceptions import ImproperlyConfigured
class ContentTypeMixin(object):
content_type = None
def render_to_response(self, context, **response_kwargs):
if not self.content_type:
raise ImproperlyConfigured(
"MimeTypeMixin rquires a definition of content_type")
response_kwargs['content_type'] = self.content_type
return super(ContentTypeMixin, self).render_to_response(context,
**response_kwargs)
class MyTxtView(ContentTypeMixin, TemplateView):
content_type = 'text/plain'
....
In Django 1.5 the content_type argument in the TemplateView adds the same functionality that was in the function-based view before. That makes it easier to set the desired mimetype:
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html', content_type='text/plain')),
I know that you ask for setting a content type with TemplateView, but I will give you different answer which I think that will be more clean and can be used in Django versions lower than 1.5.
url(r'^robots\.txt$', 'django.shortcuts.render', kwargs={
'template_name': 'robots.txt',
'content_type': 'text/plain',
})
With this approach you don't need to import anything or to subclass TemplateView and make ugly overwrites of some methods. You can simply use the old technique with function based views.
If you don't want to extend the TemplateView, you can extend the TemplateResponse to set the mimetype:
from django.template.response import TemplateResponse
class TextResponse(TemplateResponse):
def __init__(self, *args, **kwargs):
kwargs['mimetype'] = 'text/plain'
return super(TextResponse, self).__init__(*args, **kwargs)
Then pass it as the template_class to the TemplateView
urlpatterns = patterns('django.views.generic.simple',
(r'^robots\.txt$', TemplateView.as_view(template_name='robots.txt', response_class=TextResponse)),
)
The best way to do it is to subclass TemplateView and override the render_to_response() method:
class StaticPagesTest(TemplateView):
template_name = 'staticpages/test.html'
def render_to_response(self, context, **kwargs):
return super(StaticPagesTest, self).render_to_response(context,
mimetype='text/plain', **kwargs)
I know this is solved for 1.5, but the application I am working in is 1.4.
I had an issue with two url patterns in a row using sacabuche's answer:
url(r'^playlist1\.m3u$', ContentTypeTemplateView.as_view(template_name='playlist1.m3u', content_type='audio/x-mpegurl')),
url(r'^playlist2\.pls$', ContentTypeTemplateView.as_view(template_name='playlist2.pls', content_type='audio/x-scpls'))
I found playlist1 would return the correct template, but with playlist2's content type! Playlist2 was ok. Adding a 3rd url pattern with a content-type of 'foo' would cause all playlist views to return with content-type 'foo'.
I ended up using the render method instead with good results:
urls:
url(r'^playlist1\.m3u$', 'content_type_to_template', {'template_name': 'playlist1.m3u', 'content_type': 'audio/x-mpegurl'}),
url(r'^playlist2\.pls$', 'content_type_to_template', {'template_name': 'playlist2.pls', 'content_type':'audio/x-scpls'})
views:
from django.shortcuts import render
def content_type_to_template(request, template_name='', content_type='text/plain'):
return render(request, template_name, content_type=content_type)
A simple example of how change content type of a TemplateView:
#views.py
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = "home/index.html"
content_type = 'text/html'
# urls.py
url(r'^home/$', HomeView.as_view(), name='home_page'),
url(r'^test/(?P<template>.*)', lambda request, template: TemplateView.as_view(template_name=template)(request)),