How can I not use Django's admin login view? - django

I created my own view for login. However if a user goes directly to /admin it brings them to the admin login page and doesn't use my custom view. How can I make it redirect to the login view used for everything not /admin?

From http://djangosnippets.org/snippets/2127/—wrap the admin login page with login_required. For example, in urls.py:
from django.contrib.auth.decorators import login_required
from django.contrib import admin
admin.autodiscover()
admin.site.login = login_required(admin.site.login)
You probably already have the middle two lines and maybe even the first line; adding that fourth line will cause anything that would have hit the admin.site.login function to redirect to your LOGIN_URL with the appropriate next parameter.

While #Isaac's solution should reject majority of malicious bots, it doesn't provide protection for professional penetrating. As a logged in user gets the following message when trying to login to admin:
We should instead use the admin decorator to reject all non-privileged users:
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib import admin
[ ... ]
admin.site.login = staff_member_required(admin.site.login, login_url=settings.LOGIN_URL)
To the best of my knowledge, the decorator was added in 1.9.

I found that the answer above does not respect the "next" query parameter correctly.
An easy way to solve this problem is to use a simple redirect. In your site's urls file, immediately before including the admin urls, put a line like this:
url(r'^admin/login$', RedirectView.as_view(pattern_name='my_login_page', permanent=True, query_string=True))

Holá
I found a very simple solution.
Just tell django that the url for admin login is handle by your own login view
You just need to modify the urls.py fle of the project (note, not the application one)
In your PROJECT folder locate the file urls.py.
Add this line to the imports section
from your_app_name import views
Locate this line
url(r'^admin/', include(admin.site.urls))
Add above that line the following
url(r'^admin/login/', views.your_login_view),
This is an example
from django.conf.urls import include, url
from django.contrib import admin
from your_app import views
urlpatterns = [
url(r'^your_app_start/', include('your_app.urls',namespace="your_app_name")),
url(r'^admin/login/', views.your_app_login),
url(r'^admin/', include(admin.site.urls)),
]

http://blog.montylounge.com/2009/07/5/customizing-django-admin-branding/
(web archive)
I'm trying to solve exactly this problem and I found the solution at this guys blog. Basically, override the admin template and use your own template. In short, just make a file called login.html in /path-to-project/templates/admin/ and it will replace the admin login page. You can copy the original (django/contrib/admin/templates/login.html) and modify a line or two. If you want to scrap the default login page entirely you can do something like this:
{% extends "my-login-page.html" %}
There it is. One line in one file. Django is amazing.

I had the same issue, tried to use the accepted answer, but has the same issue as pointed in the comment above.
Then I've did something bit different, pasting here if this would be helpful to someone.
def staff_or_404(u):
if u.is_active:
if u.is_staff:
return True
raise Http404()
return False
admin.site.login = user_passes_test(
staff_or_404,
)(admin.site.login)
The idea is that if the user is login, and tried to access the admin, then he gets 404. Otherwise, it will force you to the normal login page (unless you are already logged in)

In your ROOT_URLCONF file (by default, it's urls.py in the project's root folder), is there a line like this:
urlpatterns = patterns('',
...
(r'^admin/', include(admin.site.urls)),
...
)
If so, you'd want to replace include(admin.site.urls) with the custom view you created:
(r'^admin/', 'myapp.views.myloginview'),
or if your app has its own urls.py, you could include it like this:
(r'^admin/', include(myapp.urls)),

This is my solution with custom AdminSite class:
class AdminSite(admin.AdminSite):
def _is_login_redirect(self, response):
if isinstance(response, HttpResponseRedirect):
login_url = reverse('admin:login', current_app=self.name)
response_url = urllib.parse.urlparse(response.url).path
return login_url == response_url
else:
return False
def admin_view(self, view, cacheable=False):
inner = super().admin_view(view, cacheable)
def wrapper(request, *args, **kwargs):
response = inner(request, *args, **kwargs)
if self._is_login_redirect(response):
if request.user.is_authenticated():
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL)
else:
return redirect_to_login(request.get_full_path(), reverse('accounts_login'))
else:
return response
return wrapper

You can redirect admin login url to the auth login view :
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('your_app.urls')),
path('accounts/', include('django.contrib.auth.urls')),
path('admin/login/', RedirectView.as_view(url='/accounts/login/?next=/admin/', permanent=True)),
path('admin/', admin.site.urls),
]

As of August 2020, django.contrib.admin.sites.AdminSite has a login_template attribute. So you can just subclass AdminSite and specify a custom template i.e.,
class MyAdminSite(AdminSite):
login_template = 'my_login_template.html'
my_admin_site = MyAdminSite()
Then just use my_admin_site everywhere instead of admin.site.

Related

Django: how to route URL requests to django.contrib.admin functions

I would like Django to return the same response for requests to /myapp/add as /admin/myapp/mymodel/add.
myproject/myapp/models.py defines the model and myapp/admin.py registers with django.contrib.admin.
myproject/myapp/models.py:
from django.db import models
class MyModel(models.Model):
...
myproject/myapp/admin.py:
from django.contrib import admin
from .models import MyModel
admin.site.register(MyModel)
I am stuck on how to route the request to django.contrib.admin in the project's urlpatterns:
myproject/myproject/urls.py:
urlpatterns = [
url(r'^$', views.home_page, name='home'),
url(r'^admin/', admin.site.urls),
url(r'^myapp/add', ??????),
]
From printing the return from resolve('/admin/myapp/mymodel/add/') this looks like part of the answer:
ResolverMatch(func=django.contrib.admin.options.add_view, args=(), kwargs={}, url_name=myapp_mymodel_add, app_names=['admin'], namespaces=['admin'])
Well let me say that seems like a weird thing to do, but anyway:
in the file: django.contrib.admin.options.py we see:
class ModelAdmin(BaseModelAdmin):
we see that the add view returns
_changeform_view()
which uses the template
django/contrib/admin/templates/change_form.html
So you would want to render that template in your view.
but it would be missing a bunch of context items,
so you would basically have to re-implement the django.admin.options._changeform_view
and then copy the template django/contrib/admin/templates/change_form.html to your apps' template directory
def admin_add(request):
# admin.changeform_view code here
return render(request, "myapp/change_form.html", {context{)
ps. I assume the admin site view assumes the user is the "superuser" and not a normal user so you would want to account for that as well..

How to redirect the user back to the login page after logout in django/allauth?

I installed a very basic project on django with the allauth module. It works well...except that i have a 404 page when i try to logout using http://localhost:8000/accounts/logout/.
The urls.py
urlpatterns = [
url(r'^accounts/', include('allauth.urls')),
url(r'^aboutus/$', TemplateView.as_view(template_name="aboutus.html"), name="aboutus"),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
]
and in settings.py i set ACCOUNT_LOGOUT_ON_GET= True to avoid the logout confirmation.
My goal is to set path: login page-->aboutus:click on logout button--> login page.
I read on internet a lot of stuff but it still does not work.
Please help
solution in the setting file : ACCOUNT_LOGOUT_REDIRECT_URL ="/accounts/login"
If I understand you right, there is an answer for your question in official docs. Basically you just need to write your own adapter, it may look like:
# project/settings.py:
ACCOUNT_ADAPTER = 'project.users.adapter.MyAccountAdapter'
# project/users/adapter.py:
from django.conf import settings
from allauth.account.adapter import DefaultAccountAdapter
class MyAccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
path = "/accounts/{username}/"
return path.format(username=request.user.username)

How to redirect to another page

Well, I have my own session and authentication routines in my app. When a user goes to app/login/ page, if he or she is not loggen in, then he/she sees a form to submit, otherwise I want to redirect the user to the root page app/. login view looks like this:
def index(request):
if sessionCheck(request):
# here I want to redirect one level above to app/
pass
else:
template = loader.get_template('login/login.html')
... other code which is not important
In PHP, I think, I would do it like this:
header("Location: /");
exit;
But how should I do exactly the same thing in Django?
You can use the redirect shortcut function:
from django.shortcuts import redirect
return redirect('some-view-name')
You can also specify a URL apart from a named view. So if your index url does not have a name, you can specify '/'.
This shortcut function is essentially the same as:
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
return HttpResponseRedirect(reverse('some-view-name'))
In order to have a named view, in your urls.py you have to provide a name argument. For instance:
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^some-view$', views.something, name='some-view-name'),
]
More on that on Django tutorial, Django URL dispatcher.
You can use redirect
if sessionCheck(request):
redirect(your_url)
else:
...

How to bypass django admin index and go to a specific application page?

I've got a Django project with only one app.
I don't need the site admin (same as my app admin), so I would like to bypass the site admin and go directly to my app admin, i.e. go directly to mysite/admin/myapp/ after loging in and removing Home in the breadcrumb.
How can I do that?
Django Url Dispatcher scans all defined url definitions and redirect the request to the first matching url definition. At this point, url ordering is important, in urls.py file of your project root:
urlpatterns += patterns('',
...
(r'^admin/', include(admin.site.urls)),
...
)
You can define an url redirection form the admin index page url to a custom view
urlpatterns += patterns('',
...
(r'^admin/$', 'myapp.views.home'),
(r'^admin/', include(admin.site.urls)),
...
)
So, if the request url is your admin index page, then your custom view will be called, if it is an admin page (that starts with admin/) but not admin index page, then admin.site.urls will be executed...
in your myapp.views write a simple redirection view:
from django.http import HttpResponseRedirect
def home(request):
return HttpResponseRedirect("/myapp/")
What you can do is override the admin index page to redirect to app specific admin page.
As stated by #FallenAngel, you need to update the urls.py to have your view for admin index page. That view can redirect to inner app admin page.
from django.http import HttpResponseRedirect
from django.core import urlresolvers
def home(request):
return HttpResponseRedirect(urlresolvers.reverse('admin:app_list', args=("myapp",)))
You can comment it out in the INSTALLED_APPS if you don't want that in your project. Just to remove it from admin you need to unregister. Check it out here.
Another hack is, you can override the admin url mysite/admin/ to simply redirect to mysite/admin/myapp/ in which you don't need to define your own redirect view. Django DRY rocks there. :)
from django.http import HttpResponseRedirect
urlpatterns = patterns('',
url(r'^mysite/admin/$', lambda x: HttpResponseRedirect('/mysite/admin/myapp/')),
url(r'^mysite/admin/', include(admin.site.urls)),
)
For more customization you need to change the admin index page by customizing the admin templates. Doc is here.
https://docs.djangoproject.com/en/1.3/intro/tutorial02/#customize-the-admin-index-page
The cleanest and easiest way is to use the generic RedirectView in urls.py:
from django.core.urlresolvers import reverse_lazy
from django.views.generic import RedirectView
urlpatterns = patterns('',
url(r'^admin/$', RedirectView.as_view(url=reverse_lazy('admin:app_list',
args=('myapp',)))),
url(r'^admin/', include(admin.site.urls)),
)

Django: Redirect logged in users from login page

I want to set up my site so that if a user hits the /login page and they are already logged in, it will redirect them to the homepage. If they are not logged in then it will display normally. How can I do this since the login code is built into Django?
I'm assuming you're currently using the built-in login view, with
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
or something similar in your urls.
You can write your own login view that wraps the default one. It will check if the user is already logged in (through is_authenticated attribute official documentation) and redirect if he is, and use the default view otherwise.
something like:
from django.contrib.auth.views import login
def custom_login(request):
if request.user.is_authenticated:
return HttpResponseRedirect(...)
else:
return login(request)
and of course change your urls accordingly:
(r'^accounts/login/$', custom_login),
The Django 1.10 way
For Django 1.10, released in August 2016, a new parameter named redirect_authenticated_user was added to the login() function based view present in django.contrib.auth [1].
Example
Suppose we have a Django application with a file named views.py and another file named urls.py. The urls.py file will contain some Python code like this:
#
# Django 1.10 way
#
from django.contrib.auth import views as auth_views
from . import views as app_views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', auth_views.login, name='login',
kwargs={'redirect_authenticated_user': True}),
url(r'^dashboard/', app_views.Dashboard.as_view(), name='dashboard'),
url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'),
]
From that file, the relevant part within the urlpatterns variable definition is the following, which uses the already mentioned redirect_authenticated_user parameter with a True value:
url(r'^login/', auth_views.login, name='login',
kwargs={'redirect_authenticated_user': True}),
Take note that the default value of the redirect_authenticated_user parameter is False.
The Django 1.11 way
For Django 1.11, released in April 2017, the LoginView class based view superseded the login() function based view [2], which gives you two options to choose from:
Use the same Django 1.10 way just described before, which is a positive thing because your current code will continue working fine. If you tell Python interpreter to display warnings, by for example running in a console terminal the command python -Wd manage.py runserver in your Django project directory and then going with a web browser to your login page, you would see in that same console terminal a warning message like this:
/usr/local/lib/python3.6/site-packages/django/contrib/auth/views.py:54:
RemovedInDjango21Warning: The login() view is superseded by the
class-based LoginView().
Use the new Django 1.11 way, which will make your code more modern and compatible with future Django releases. With this option, the example given before will now look like the following one:
Example
We again suppose that we have a Django application with a file named views.py and another file named urls.py. The urls.py file will contain some Python code like this:
#
# Django 1.11 way
#
from django.contrib.auth import views as auth_views
from . import views as app_views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/',
auth_views.LoginView.as_view(redirect_authenticated_user=True),
name='login'),
url(r'^dashboard/', app_views.Dashboard.as_view(), name='dashboard'),
url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'),
]
From that file, the relevant part within the urlpatterns variable definition is the following, which again uses the already mentioned redirect_authenticated_user parameter with a True value, but passing it as an argument to the as_view method of the LoginView class:
url(r'^login/',
auth_views.LoginView.as_view(redirect_authenticated_user=False),
name='login'),
Take note that here the default value of the redirect_authenticated_user parameter is also False.
References
[1] Relevant section in Django 1.10 release notes at https://docs.djangoproject.com/en/dev/releases/1.10/#django-contrib-auth
[2] Relevant section in Django 1.11 release notes at https://docs.djangoproject.com/en/1.11/releases/1.11/#django-contrib-auth
anonymous_required decorator
For class based views
Code:
from django.shortcuts import redirect
def anonymous_required(func):
def as_view(request, *args, **kwargs):
redirect_to = kwargs.get('next', settings.LOGIN_REDIRECT_URL )
if request.user.is_authenticated():
return redirect(redirect_to)
response = func(request, *args, **kwargs)
return response
return as_view
Usage:
url(r'^/?$',
anonymous_required(auth_views.login),
),
url(r'^register/?$',
anonymous_required(RegistrationView.as_view()),
name='auth.views.register'
),
# Could be used to decorate the dispatch function of the view instead of the url
For view functions
From http://blog.motane.lu/2010/01/06/django-anonymous_required-decorator/
Code:
from django.http import HttpResponseRedirect
def anonymous_required( view_function, redirect_to = None ):
return AnonymousRequired( view_function, redirect_to )
class AnonymousRequired( object ):
def __init__( self, view_function, redirect_to ):
if redirect_to is None:
from django.conf import settings
redirect_to = settings.LOGIN_REDIRECT_URL
self.view_function = view_function
self.redirect_to = redirect_to
def __call__( self, request, *args, **kwargs ):
if request.user is not None and request.user.is_authenticated():
return HttpResponseRedirect( self.redirect_to )
return self.view_function( request, *args, **kwargs )
Usage:
#anonymous_required
def my_view( request ):
return render_to_response( 'my-view.html' )
For Django 2.x, in your urls.py:
from django.contrib.auth import views as auth_views
from django.urls import path
urlpatterns = [
path('login/', auth_views.LoginView.as_view(redirect_authenticated_user=True), name='login'),
]
Add this decorator above your login view to redirect to /home if a user is already logged in
#user_passes_test(lambda user: not user.username, login_url='/home', redirect_field_name=None)
and don't forget to import the decorator
from django.contrib.auth.decorators import user_passes_test
Since class based views (CBVs) is on the rise. This approach will help you redirect to another url when accessing view for non authenticated users only.
In my example the sign-up page overriding the dispatch() method.
class Signup(CreateView):
template_name = 'sign-up.html'
def dispatch(self, *args, **kwargs):
if self.request.user.is_authenticated:
return redirect('path/to/desired/url')
return super().dispatch(*args, **kwargs)
Cheers!
https://docs.djangoproject.com/en/3.1/topics/auth/default/#all-authentication-views
Add the redirect route in settings
LOGIN_URL = 'login'
And in the URLs add redirect_authenticated_user=True to LoginView
path('login/', auth_views.LoginView.as_view(template_name='users/login.html',redirect_authenticated_user=True), name='login')
I know this is a pretty old question, but I'll add my technique in case anyone else needs it:
myproject/myapp/views/misc.py
from django.contrib.auth.views import login as contrib_login, logout as contrib_logout
from django.shortcuts import redirect
from django.conf import settings
def login(request, **kwargs):
if request.user.is_authenticated():
return redirect(settings.LOGIN_REDIRECT_URL)
else:
return contrib_login(request, **kwargs)
logout = contrib_logout
myproject/myapp/urls.py
from django.conf.urls import patterns, url
urlpatterns = patterns('myapp.views.misc',
url(r'^login/$', 'login', {'template_name': 'myapp/login.html'}, name='login'),
url(r'^logout/$', 'logout', {'template_name': 'myapp/logout.html'}, name='logout'),
)
...
Assuming that you are done setting up built-in Django user authentication (and using decorators), add this in your settings.py:
LOGIN_REDIRECT_URL = '/welcome/'
NOTE: '/welcome/' here is the URL of the homepage. It is up to you what to replace it with.
All you have to do is set the "root" url to the homepage view. Since the homepage view is already restricted for logged on users, it'll automatically redirect anonymous users to the login page.
Kepp the url as it is.
And add something like:
(r'^$', 'my_project.my_app.views.homepage'),