Django – after login, redirect user to his custom page –> mysite.com/username
Question:
By default after login django redirects the user to an accounts/profile page or if you edit the LOGIN_REDIRECT_URL you can send the user to another page you specify in the settings.py.
This is great but I would like the user (after login) to be redirected to a custom page where the link to that page would look something like this: mysite.com/username
. So the default accounts/profile or the LOGIN_REDIRECT_URL settings would not work in this case since both are somehow static. In my case the username
section of the address changes for every user.
Any ideas how I can make it so when the user is logged in would go to a custom user page that has user’s name in the address like: mysite.com/username
?
Answers:
You can authenticate and log the user in as stated here: https://docs.djangoproject.com/en/dev/topics/auth/default/#how-to-log-a-user-in
This will give you access to the User object from which you can get the username and then do a HttpResponseRedirect to the custom URL.
A simpler approach relies on redirection from the page LOGIN_REDIRECT_URL. The key thing to realize is that the user information is automatically included in the request.
Suppose:
LOGIN_REDIRECT_URL = '/profiles/home'
and you have configured a urlpattern:
(r'^profiles/home', home),
Then, all you need to write for the view home()
is:
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth.decorators import login_required
@login_required
def home(request):
return HttpResponseRedirect(
reverse(NAME_OF_PROFILE_VIEW,
args=[request.user.username]))
where NAME_OF_PROFILE_VIEW
is the name of the callback that you are using. With django-profiles, NAME_OF_PROFILE_VIEW
can be ‘profiles_profile_detail’.
Got into django recently and been looking into a solution to that and found a method that might be useful.
So for example, if using allouth the default redirect is accounts/profile.
Make a view that solely redirects to a location of choice using the username field like so:
def profile(request):
name=request.user.username
return redirect('-----choose where-----' + name + '/')
Then create a view that captures it in one of your apps, for example:
def profile(request, name):
user = get_object_or_404(User, username=name)
return render(request, 'myproject/user.html', {'profile': user})
Where the urlpatterns capture would look like this:
url(r'^(?P<name>.+)/$', views.profile, name='user')
Works well for me.
Yes! In your settings.py define the following
LOGIN_REDIRECT_URL = '/your-path'
And have ‘/your-path’ be a simple View that looks up self.request.user
and does whatever logic it needs to return a HttpResponseRedirect
object.
A better way might be to define a simple URL like '/simple'
that does the lookup logic there. The URL looks more beautiful, saves you some work, etc.
If you’re using Django’s built-in LoginView
, it takes next
as context, which is “The URL to redirect to after successful login
. This may contain a query string, too.” (see docs)
Also from the docs:
“If login is successful, the view redirects to the URL specified in next
. If next isn’t provided, it redirects to settings.LOGIN_REDIRECT_URL
(which defaults to /accounts/profile/).”
Example code:
urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
from account.forms import LoginForm # optional form to pass to view
urlpatterns = [
...
# --------------- login url/view -------------------
path('account/login/', auth_views.LoginView.as_view(
template_name='login.html',
authentication_form=LoginForm,
extra_context={
# option 1: provide full path
'next': '/account/my_custom_url/',
# option 2: just provide the name of the url
# 'next': 'custom_url_name',
},
), name='login'),
...
]
login.html
...
<form method="post" action="{% url 'login' %}">
...
{# option 1 #}
<input type="hidden" name="next" value="{{ next }}">
{# option 2 #}
{# <input type="hidden" name="next" value="{% url next %}"> #}
</form>
When using Class based views, another option is to use the dispatch method.
https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/
Example Code:
Settings.py
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'home'
urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
urlpatterns = [
path('', HomeView.as_view(), name='home'),
path('login/', auth_views.LoginView.as_view(),name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]
views.py
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.views.generic import View
from django.shortcuts import redirect
@method_decorator([login_required], name='dispatch')
class HomeView(View):
model = models.User
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect('login')
elif some-logic:
return redirect('some-page') #needs defined as valid url
return super(HomeView, self).dispatch(request, *args, **kwargs)
{% if redirect_field_value == None %}
<input type="hidden" name="{{ redirect_field_name }}" value="/LOGIN_REDIRECT_URL(instead of typing in settings.py file)/">
{% else %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{redirect_field_value}}">
{% endif %}
Place this code in the login html page. For Example if you are directly accessing the login page (Example: /accounts/login/) then the ‘next’ value will be None that is after login its doesnot know to which page you have to redirect until you specify ‘LOGIN_REDIRECT_URL’ in the settings page (Example: LOGIN_REDIRECT_URL=/dashboard/). If you specify ‘LOGIN_REDIRECT_URL’ in settings page as example shown then after login it will redirect to ‘/dashboard/’ url.
If you need to access another url which also required login to view (For Example: /items/). So when you access ‘localhost:8000/items/’ as it required login to view it will redirect to ‘localhost:8000/accounts/login/?next=/items/’. Here the value of ‘next’ will be ‘items’ but the problem is as you specified ‘LOGIN_REDIRECT_URL’ in settings page it always redirect to /dashboard/ url after login.
So the above html code helps to redirects if ‘next’ value is None then it will go to page that we specified as value for the hidden input. if ‘next’ is not None then it will redirects based on the value of ‘next’
I actually prefer the method @Stu and others mentioned, which is to create a new URL and a view for it, so that we can put the redirection logic there. It’s easier and users can also type example.com/profile
in their browser to get redirected to their profile at example.com/profile/username/
.
But another way that doesn’t require defining an additional URL, is to extend the LoginView
and redirect using the URL name for the profile view.
Let’s say we have two apps named accounts
and pages
, and we have defined the profile page in the pages
app, something like this:
# pages/views.py
from django.views.generic import View
from django.http import HttpResponse
class ProfileView(View):
def get(self, request, username):
return HttpResponse('Hello ' + username + '!')
and the URLConf:
# pages/urls.py
from django.urls import path
from .views import ProfileView
app_name = 'pages'
urlpatterns = [
path('profile/<str:username>/', ProfileView.as_view(), name='profile')
]
Note that we must define app_name = 'pages'
as we’ll need it to refer to the URLs for the pages
app.
Then, in the accounts
app, we extend the LoginView
and add the redirection logic:
# accounts/views.py
from django.contrib.auth.views import LoginView
from django.urls import reverse
class CustomLoginView(LoginView):
def form_valid(self, form):
self.success_url_kwargs = {'username': form.cleaned_data['username']}
return super().form_valid(form)
def get_success_url(self):
# 'pages:profile' refers to the 'profile' path in the 'pages' app.
return reverse('pages:profile', kwargs=self.success_url_kwargs)
# accounts/urls.py
from django.urls import path, include
from .views import CustomLoginView
urlpatterns = [
path('accounts/login/', CustomLoginView.as_view(), name='login'),
path('accounts/', include('django.contrib.auth.urls'))
]
After a successful login, the form_valid()
method gets called, and we add the parameters we want to use in the profile URL to the custom class attribute success_url_kwargs
. Finally, calling return super().form_valid(form)
first logs the user in, and then redirects to the URL given by the get_success_url()
method (see the code), which we define to return the custom URL we want.
Note that we included the URLs for the auth
app after our custom login URL, so that the first one is resolved when we want to access accounts/login/
.
Also, if we needed to access the user model instance, we can call form.get_user()
. This can be useful in certain cases. For example, if the user logs in with email and we need his username, we can do form.get_user().username
.
By default after login django redirects the user to an accounts/profile page or if you edit the LOGIN_REDIRECT_URL you can send the user to another page you specify in the settings.py.
This is great but I would like the user (after login) to be redirected to a custom page where the link to that page would look something like this: mysite.com/username
. So the default accounts/profile or the LOGIN_REDIRECT_URL settings would not work in this case since both are somehow static. In my case the username
section of the address changes for every user.
Any ideas how I can make it so when the user is logged in would go to a custom user page that has user’s name in the address like: mysite.com/username
?
You can authenticate and log the user in as stated here: https://docs.djangoproject.com/en/dev/topics/auth/default/#how-to-log-a-user-in
This will give you access to the User object from which you can get the username and then do a HttpResponseRedirect to the custom URL.
A simpler approach relies on redirection from the page LOGIN_REDIRECT_URL. The key thing to realize is that the user information is automatically included in the request.
Suppose:
LOGIN_REDIRECT_URL = '/profiles/home'
and you have configured a urlpattern:
(r'^profiles/home', home),
Then, all you need to write for the view home()
is:
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth.decorators import login_required
@login_required
def home(request):
return HttpResponseRedirect(
reverse(NAME_OF_PROFILE_VIEW,
args=[request.user.username]))
where NAME_OF_PROFILE_VIEW
is the name of the callback that you are using. With django-profiles, NAME_OF_PROFILE_VIEW
can be ‘profiles_profile_detail’.
Got into django recently and been looking into a solution to that and found a method that might be useful.
So for example, if using allouth the default redirect is accounts/profile.
Make a view that solely redirects to a location of choice using the username field like so:
def profile(request):
name=request.user.username
return redirect('-----choose where-----' + name + '/')
Then create a view that captures it in one of your apps, for example:
def profile(request, name):
user = get_object_or_404(User, username=name)
return render(request, 'myproject/user.html', {'profile': user})
Where the urlpatterns capture would look like this:
url(r'^(?P<name>.+)/$', views.profile, name='user')
Works well for me.
Yes! In your settings.py define the following
LOGIN_REDIRECT_URL = '/your-path'
And have ‘/your-path’ be a simple View that looks up self.request.user
and does whatever logic it needs to return a HttpResponseRedirect
object.
A better way might be to define a simple URL like '/simple'
that does the lookup logic there. The URL looks more beautiful, saves you some work, etc.
If you’re using Django’s built-in LoginView
, it takes next
as context, which is “The URL to redirect to after successful login
. This may contain a query string, too.” (see docs)
Also from the docs:
“If login is successful, the view redirects to the URL specified in next
. If next isn’t provided, it redirects to settings.LOGIN_REDIRECT_URL
(which defaults to /accounts/profile/).”
Example code:
urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
from account.forms import LoginForm # optional form to pass to view
urlpatterns = [
...
# --------------- login url/view -------------------
path('account/login/', auth_views.LoginView.as_view(
template_name='login.html',
authentication_form=LoginForm,
extra_context={
# option 1: provide full path
'next': '/account/my_custom_url/',
# option 2: just provide the name of the url
# 'next': 'custom_url_name',
},
), name='login'),
...
]
login.html
...
<form method="post" action="{% url 'login' %}">
...
{# option 1 #}
<input type="hidden" name="next" value="{{ next }}">
{# option 2 #}
{# <input type="hidden" name="next" value="{% url next %}"> #}
</form>
When using Class based views, another option is to use the dispatch method.
https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/
Example Code:
Settings.py
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'home'
urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
urlpatterns = [
path('', HomeView.as_view(), name='home'),
path('login/', auth_views.LoginView.as_view(),name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]
views.py
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.views.generic import View
from django.shortcuts import redirect
@method_decorator([login_required], name='dispatch')
class HomeView(View):
model = models.User
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect('login')
elif some-logic:
return redirect('some-page') #needs defined as valid url
return super(HomeView, self).dispatch(request, *args, **kwargs)
{% if redirect_field_value == None %}
<input type="hidden" name="{{ redirect_field_name }}" value="/LOGIN_REDIRECT_URL(instead of typing in settings.py file)/">
{% else %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{redirect_field_value}}">
{% endif %}
Place this code in the login html page. For Example if you are directly accessing the login page (Example: /accounts/login/) then the ‘next’ value will be None that is after login its doesnot know to which page you have to redirect until you specify ‘LOGIN_REDIRECT_URL’ in the settings page (Example: LOGIN_REDIRECT_URL=/dashboard/). If you specify ‘LOGIN_REDIRECT_URL’ in settings page as example shown then after login it will redirect to ‘/dashboard/’ url.
If you need to access another url which also required login to view (For Example: /items/). So when you access ‘localhost:8000/items/’ as it required login to view it will redirect to ‘localhost:8000/accounts/login/?next=/items/’. Here the value of ‘next’ will be ‘items’ but the problem is as you specified ‘LOGIN_REDIRECT_URL’ in settings page it always redirect to /dashboard/ url after login.
So the above html code helps to redirects if ‘next’ value is None then it will go to page that we specified as value for the hidden input. if ‘next’ is not None then it will redirects based on the value of ‘next’
I actually prefer the method @Stu and others mentioned, which is to create a new URL and a view for it, so that we can put the redirection logic there. It’s easier and users can also type example.com/profile
in their browser to get redirected to their profile at example.com/profile/username/
.
But another way that doesn’t require defining an additional URL, is to extend the LoginView
and redirect using the URL name for the profile view.
Let’s say we have two apps named accounts
and pages
, and we have defined the profile page in the pages
app, something like this:
# pages/views.py
from django.views.generic import View
from django.http import HttpResponse
class ProfileView(View):
def get(self, request, username):
return HttpResponse('Hello ' + username + '!')
and the URLConf:
# pages/urls.py
from django.urls import path
from .views import ProfileView
app_name = 'pages'
urlpatterns = [
path('profile/<str:username>/', ProfileView.as_view(), name='profile')
]
Note that we must define app_name = 'pages'
as we’ll need it to refer to the URLs for the pages
app.
Then, in the accounts
app, we extend the LoginView
and add the redirection logic:
# accounts/views.py
from django.contrib.auth.views import LoginView
from django.urls import reverse
class CustomLoginView(LoginView):
def form_valid(self, form):
self.success_url_kwargs = {'username': form.cleaned_data['username']}
return super().form_valid(form)
def get_success_url(self):
# 'pages:profile' refers to the 'profile' path in the 'pages' app.
return reverse('pages:profile', kwargs=self.success_url_kwargs)
# accounts/urls.py
from django.urls import path, include
from .views import CustomLoginView
urlpatterns = [
path('accounts/login/', CustomLoginView.as_view(), name='login'),
path('accounts/', include('django.contrib.auth.urls'))
]
After a successful login, the form_valid()
method gets called, and we add the parameters we want to use in the profile URL to the custom class attribute success_url_kwargs
. Finally, calling return super().form_valid(form)
first logs the user in, and then redirects to the URL given by the get_success_url()
method (see the code), which we define to return the custom URL we want.
Note that we included the URLs for the auth
app after our custom login URL, so that the first one is resolved when we want to access accounts/login/
.
Also, if we needed to access the user model instance, we can call form.get_user()
. This can be useful in certain cases. For example, if the user logs in with email and we need his username, we can do form.get_user().username
.