Django - logout view customisation fail - python

Although the original question is cleared out, there's a new related question which is quite interesting: (please let me know if my theory is remotely correct...)
Please checkout my urlpatterns below:
from django.contrib.auth import views as auth_views
from boutique.models import Category
from django.urls import path, include
from . import views
app_name = 'users'
urlpatterns = [
# to customise login view
path('login/', auth_views.LoginView.as_view(extra_context = {'categories': Category.objects.get_categories_with_item()})),
# path('login/', views.NewLoginView.as_view()),
# to customise default logout view
path('logout/', auth_views.LogoutView.as_view(), {'categories': Category.objects.get_categories_with_item()}),
# include django authentication urls
path('', include('django.contrib.auth.urls')),
As you probably noticed that there are two different ways to pass in extra_context, the interesting thing is: The method used on LogoutView can not be used on LoginView!! However, The method used on LoginView does work on LogoutView.
I think the possible explanation and yet to be confirmed by you lot is that they inherit different views:
LoginView:
class LoginView(SuccessURLAllowedHostsMixin, FormView):
"""
Display the login form and handle the login action.
"""
form_class = AuthenticationForm
authentication_form = None
redirect_field_name = REDIRECT_FIELD_NAME
template_name = 'registration/login.html'
redirect_authenticated_user = False
extra_context = None
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
current_site = get_current_site(self.request)
context.update({
self.redirect_field_name: self.get_redirect_url(),
'site': current_site,
'site_name': current_site.name,
**(self.extra_context or {})
})
return context
LogoutView:
class LogoutView(SuccessURLAllowedHostsMixin, TemplateView):
"""
Log out the user and display the 'You are logged out' message.
"""
next_page = None
redirect_field_name = REDIRECT_FIELD_NAME
template_name = 'registration/logged_out.html'
extra_context = None
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
current_site = get_current_site(self.request)
context.update({
'site': current_site,
'site_name': current_site.name,
'title': _('Logged out'),
**(self.extra_context or {})
})
return context
I think the reason that LoginView has to pass the extra_context as a positional argument into .as_view() is that it doesn't inherit from TemplateView:
class TemplateView(TemplateResponseMixin, ContextMixin, View):
"""
Render a template. Pass keyword arguments from the URLconf to the context.
"""
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
which has a get method to get context data... if i'm not mistaken.
Could anyone confirm this?
I'm quite new to python and django framework. Thanks a lot!!!
The Original question has been answered
Please checkout the answers in the comments session --
I'm fairly new to Django and in order to pass in more context to logout view I tried 2 methods to customise the logout view:
Method 1:
from django.contrib.auth import views as auth_views
from boutique.models import Category
app_name = 'users'
urlpatterns = [
...
path('logout/', auth_views.LogoutView.as_view(), {'categories': Category.objects.all()}),
# I also tried this:
# path('logout/', auth_views.LogoutView.as_view({'categories': Category.objects.all()}),
# I also tried this:
# path('logout-customised-url/', auth_views.LogoutView.as_view(), {'categories': Category.objects.all()}),
# This is working tho it wouldn't be automatically redirect to this path when logged out
Method 2:
...
path('logout/', views.NewLogoutView.as_view()),
# NewLogoutView code below:
views.py
from boutique.models import Category
from django.contrib.auth.views import LogoutView
class NewLogoutView(LogoutView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
Still not working, and the outcome is exactly the same: if a customised url is in use such as 'logged-out/' instead of 'logout/', and you type out the url, it renders the correct page with the extra context. However, wouldn't go to the page when users log out normally...
Is there any workaround? Thanks!

You need to pass extra_context in your urls.py inorder to send extra context to logout view. read more about passing extra context to a view from in url definition.
path(
'logout/',
auth_views.LogoutView.as_view(),
{'extra_context':{'categories': Category.objects.all()}}
),

Got it!! The problem was in the order of my urlpatterns, originally it was like this:
urlpatterns = [
# include django authentication urls
path('', include('django.contrib.auth.urls')),
# to customise default logout view
path('logout/', auth_views.LogoutView.as_view(), {'categories': Category.objects.get_categories_with_item()}),
Based on official doc: Authentication Views, django.contrib.auth.urls includes many url patterns, such as:
accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']
Therefore, they take precedence if they were declared before the customised. So:
urlpatterns = [
# to customise default logout view
path('logout/', auth_views.LogoutView.as_view(), {'categories': Category.objects.get_categories_with_item()}),
# include django authentication urls
path('', include('django.contrib.auth.urls')),
To swap the order, it works.
To the roockies like me:
Method 1 does work. Just pass in the additional context/argument like it states in the official doc after the view.
Also, I checked django.contrib.auth.views.LogoutView:
class LogoutView(SuccessURLAllowedHostsMixin, TemplateView):
"""
Log out the user and display the 'You are logged out' message.
"""
next_page = None
redirect_field_name = REDIRECT_FIELD_NAME
template_name = 'registration/logged_out.html'
extra_context = None
...
The extra_context is set to None for you to add extra context. Easy to customise if were to subclass it or simply pass in a positional argument into .as_view() -- which means:
urlpatterns = [
# to customise login view
path('login/', auth_views.LoginView.as_view(extra_context = {'categories': Category.objects.get_categories_with_item()})),
]
`Loginview` has the same the same convenient settings...

Related

The view blog.views.BlogViews didn't return an HttpResponse object. It returned None instead

I'm having a problem with the views for my blog main page. I've read a lot of problems with the same error but couldn't find a solution that i thought matched with my problem.
I am trying to emulate my old code with a new View so that i can customise it further in the future.
path('', ListView.as_view(
queryset=Post.objects.filter(posted_date__lte=now).order_by("-posted_date")[:25],
template_name="blog/blog.html")),
i was previously using just the above url pattern to display my blog posts but im moving to a view:
def BlogViews(TemplateView):
def get(self, request):
posts = Post.objects.all().order_by("-posted_date")[:25]
args = {
'posts' : posts,
}
return render(request, 'blog/blog.html', args)
and the new url
path('', BlogViews, name='blog'),
However this view is not working and returns the error in the title.
I'm importing TemplateView, Post and render correctly i believe.
TemplateView is a class-based-view, so you subclass it with class.
class BlogViews(TemplateView):
def get(self, request):
posts = Post.objects.all().order_by("-posted_date")[:25]
args = {
'posts' : posts,
}
return render(request, 'blog/blog.html', args)
Then use .as_view() in the URL pattern.
path('', BlogViews.as_view(), name='blog'),
Avoid overriding get or post for class-based views. You end up losing the functionality of the parent class or duplicating it. You can take your ListView.as_view(...) as the starting point for the view, for example:
class BlogView(ListView):
template_name="blog/blog.html"
def get_queryset(self):
# Use get_queryset instead of queryset because you want timezone.now()
# to be called when the view runs, not when the server starts
return Post.objects.filter(posted_date__lte=timezone.now()).order_by("-posted_date")[:25]
In your case, it might be easier to use a function-based-view:
def blog_view(request):
posts = Post.objects.all().order_by("-posted_date")[:25]
args = {
'posts' : posts,
}
return render(request, 'blog/blog.html', args)
Then include it in the URLs with:
path('', blog_view, name='blog'),

my Django DayArchiveView is not detected in urls.py

I created several date-based views in Django, and while views for year and month function as expected, the view to display days is not detected. For instance, if I try to get /balance/2018/, or /balance/2018/04/ the views will be displayed, while /balance/2018/04/02 will fail. I understand that the problem must be with urls.py configuration, but I just cannot find it in documentation. I also tried passing day_format='%d' to as_view method, without any results.
my urls.py file
from django.urls import path, re_path
from .views import ArticleYearArchiveView, ArticleMonthArchiveView, ArticleDayArchiveView
from . import views
urlpatterns = [
path('', views.index, name='index'),
path(r'<int:year>/<int:month:>/<int:day>/', views.ArticleDayArchiveView.as_view(day_format='%d'), name='show_day'),
path(r'<int:year>/<int:month>/', views.ArticleMonthArchiveView.as_view(month_format='%m'), name='show_month'),
path(r'<int:year>/', views.ArticleYearArchiveView.as_view(), name='show_year'),
]
my views.py file
from django.shortcuts import render
from django.views.generic.dates import YearArchiveView, MonthArchiveView, DayArchiveView
from django.http import HttpResponse
from django.db.models import Sum
from .models import Category, Article
import datetime
# Create your views here.
def index(request):
num_of_articles = Article.objects.all().count()
num_of_categories = Category.objects.all().count()
return render(request, 'index.html', context = {
'num_of_articles':num_of_articles,
'num_of_categories':num_of_categories})
class ArticleYearArchiveView(YearArchiveView):
queryset = Article.objects.all()
date_field = 'financial_day'
make_object_list = True
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['summation'] = Article.objects.all().filter(financial_day__year=kwargs['year'].year).aggregate(Sum('amount_of_money'))['amount_of_money__sum']
return context
class ArticleMonthArchiveView(MonthArchiveView):
queryset = Article.objects.all()
date_field = 'financial_day'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['summation'] = Article.objects.all().filter(financial_day__year=kwargs['month'].year).filter(financial_day__month=kwargs['month'].month).aggregate(Sum('amount_of_money'))['amount_of_money__sum']
return context
class ArticleDayArchiveView(DayArchiveView):
queryset = Article.objects.all()
date_field = 'financial_day'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['summation'] = Article.objects.all().filter(financial_day__year=kwargs['day'].year).filter(financial_day__month=kwargs['day'].month).filter(financial_day__day=kwargs['day'].day).aggregate(Sum('amount_of_money'))['amount_of_money__sum']
return context
#Here it'll show ARTICLE DETAILS for a given SLUG url
def show_article(request, article_slug):
response = "You are looking at the article %s."
return HttpResponse(response % article_slug)
Error text is:
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/balance/2018/04/02
Using the URLconf defined in spendings_calculator.urls, Django tried these URL patterns, in this order:
admin/
balance/ [name='index']
balance/ <int:year>/<int:month:>/<int:day>/ [name='show_day']
balance/ <int:year>/<int:month>/ [name='show_month']
balance/ <int:year>/ [name='show_year']
The current path, balance/2018/04/02, didn't match any of these.
You have a stray : after month - <int:month:>. Remove it:
path(r'<int:year>/<int:month>/<int:day>/', views.ArticleDayArchiveView.as_view(day_format='%d'), name='show_day'),
Also, note that you are defining the URL for balance/2018/04/02/ (with a trailing slash) but you are going to http://localhost:8000/balance/2018/04/02 (without a trailing slash). Once you have fixed the typo above, Django should redirect you to the URL with the trailing slash.

Customizing Login Redirect URL in Django

After the user logs in i want to redirect to the view ('profile:profile_view')
settings.py
LOGIN_REDIRECT_URL = 'profile:profile_view'
I tried the above code but was not getting the result that i needed because iam passing slug into the url
urls.py
url(r'^(?P<slug>[-\w\d]+)/$', ProfileView.as_view(), name='view_profile'),
from here i tried the following.
settings.py
LOGIN_REDIRECT_URL = 'login_redirect'
urls.py
#login_required
def login_redirect(request):
return redirect('profile:profile_view', pk=request.user.pk, name=request.user.username)
Now i do get the username in the terminal but how do i use the username for the following 'localhost:8000/username'
views.py
class ProfileView(DetailView):
template_name = "profile/profile_view.html"
queryset = User.objects.all()
context_object_name = 'profile'
What am i doing wrong here? is there a better way?
Your profile URL accepts only one parameter called slug but you're passing it two parameters called pk and name.
First, change your URL like so:
url(r'^(?P<username>[-\w\d]+)/$', ProfileView.as_view(), name='view_profile'),
Now, correct the login_redirect view to make it look like this:
#login_required
def login_redirect(request):
return redirect('profile:profile_view', username=request.user.username)
You can also come in login in views.py and check and after that use httpresponseredirect to redirect user:
For examplele:
In urls.py :
From . Import views
Url(r'$login/', views.login, name="login"),
Url(r'$profile/', viesm.profile, name="profile"),
In views.py:
From django.shortcuts import get_object_or_404, render
From django.http import HttpResponseRedirect
Def login(request):
*** place to check if exist in your database***
If ***exist***:
User=request.post["username"]
Request.session["username"]=user
Return httpresponseredirect("../profile")
Else:
Return httpresponseredirect("../login")
Def profile(request):
If "user" in request.session:
Return render(request," **path to your template ", { "username" : request.session["username"] })
else:
Return httpresponseredirect("../login")
It should be better i think

Django: Extend context of class based view with Admin context

I have a class based view which just displays a list of configurations.
This view is added to the Django Admin site by using the following code:
#admin.register(ZbxHostConf)
class ZbxHostConfListViewAdmin(admin.ModelAdmin):
review_template = 'admin/admzbxhostconf_list.html'
def get_urls(self):
urls = super(ZbxHostConfListViewAdmin, self).get_urls()
my_urls = patterns('',
(r'^zbxhostconflist/$', self.admin_site.admin_view(self.review)),
)
return my_urls + urls
def review(self, request):
return ZbxHostConfListView.as_view()(request)
The template extends the admin/base_site.html template. I can access the site only after I log in to the Django Admin site. Unfortunately the template cannot access the context data provided by the admin view.
As the Django documentation suggests the context data will be provided directly to the TemplateResponse function:
def my_view(self, request):
# ...
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else you want in the context...
key=value,
)
return TemplateResponse(request, "sometemplate.html", context)
For function-based views there is the possibility of the extra_context argument but class-based views don't provide this argument. I guess I have to modify the get_context_data function but I don't really understand how I can provide the admin context data to the get_context_data function of my class based view. Any suggestions?
This might not be a correct answer, but I believed you could try something like this.
#!/usr/bin/python3
from django.contrib import admin
class MyTemplateView(TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(admin.site.each_context(self.request))
return context

Django, redirect all non-authenticated users to landing page

I have a django website with many urls and views. Now I have asked to redirect all non-authenticated users to a certain landing page. So, all views must check if user.is_authenticated() and return to a new set of landing pages.
Can it be done in a pretty way, instead of messing with my views.py/urls.py that much?
There is a simpler way to do this, just add the "login_url" parameter to #login_required and if the user is not login he will be redirected to the login page. You can find it here
from django.contrib.auth.decorators import login_required
#login_required(login_url='/accounts/login/')
def my_view(request):
...
You can use Middleware.
Something like this will check user auth every request:
class AuthRequiredMiddleware(object):
def process_request(self, request):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('landing_page')) # or http response
return None
Docs: process_request
Also, don't forget to enable it in settings.py
MIDDLEWARE_CLASSES = (
...
'path.to.your.AuthRequiredMiddleware',
)
see the docs for login required decorator
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
another option is to add it to your urls.py patterns, see this answer
urlpatterns = patterns('',
(r'^foo/$', login_required(direct_to_template), {'template': 'foo_index.html'}),
)
As of Django 1.10, the custom middleware classes must implement the new style syntax. You can use the following class to verify that the user is logged in while trying to access any views.
from django.shortcuts import HttpResponseRedirect
class AuthRequiredMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
if not request.user.is_authenticated: # in Django > 3 this is a boolean
return HttpResponseRedirect('login')
# Code to be executed for each request/response after
# the view is called.
return response
You can avoid specifying login_url by setting LOGIN_URL.
Therefore, in settings.py add:
LOGIN_URL = '<some_url>'
And in your views.py annotate relevant functions with only #login_required:
#login_required
def some_view_function(request):
If you need to redirect within a view function, you can do so with:
return redirect_to_login(request.get_full_path())
This can be done with middleware.
I've found a really nifty djangosnippet that does exactly what you are asking for. You can find it here, and it looks like:
from django.http import HttpResponseRedirect
from django.conf import settings
from re import compile
EXEMPT_URLS = [compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]
class LoginRequiredMiddleware:
"""
Middleware that requires a user to be authenticated to view any page other
than LOGIN_URL. Exemptions to this requirement can optionally be specified
in settings via a list of regular expressions in LOGIN_EXEMPT_URLS (which
you can copy from your urls.py).
Requires authentication middleware and template context processors to be
loaded. You'll get an error if they aren't.
"""
def process_request(self, request):
assert hasattr(request, 'user'), "The Login Required middleware\
requires authentication middleware to be installed. Edit your\
MIDDLEWARE_CLASSES setting to insert\
'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\
work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\
'django.core.context_processors.auth'."
if not request.user.is_authenticated():
path = request.path_info.lstrip('/')
if not any(m.match(path) for m in EXEMPT_URLS):
return HttpResponseRedirect(settings.LOGIN_URL)
All you have to do is to save the file as middleware.py and include the class in you're settings.py, i.e.
MIDDLEWARE_CLASSES += ('projectname.common.middleware.RequireLoginMiddleware',)
You can also define a LOGIN_URL in settings.py, so that you'll be redirected to your custom login page. The default LOGIN_URL is '/accounts/login/'.
Maybe too late but in django 1.9+ it's too easy.
Django introduced Login Required mixin for generic classes and this a great example
here by William S. Vincent
simply in your view add LoginRequiredMixin as parent class
from django.contrib.auth.mixins import LoginRequiredMixin
class BlogUpdateView(LoginRequiredMixin, UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body']
Also you can use login_required decorator for method request
from django.contrib.auth.decorators import login_required
#login_required(login_url='/login/')
def home(request):
return render(request, "home.html")
It's showing like this: http://127.0.0.1:1235/login/?next=/home/
I'm using class based views and I couldn't get any of these solutions to work for what I needed so I'll post my working solution here.
class ManagementPageView(TemplateView):
# Make a dispatch method to handle authentication
def dispatch(self, *args, **kwargs):
# Check if user is authenticated
if not self.request.user.is_authenticated:
# Redirect them to the home page if not
return redirect('home')
# Render the template if they are
return render(self.request, 'management.html')

Categories

Resources