I am trying to have two different redirects...one for normal login and another for redirect after email confirmation
ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = '/profile'
LOGIN_REDIRECT_URL = '/'
But when I enable login, AUTHENTICATED REDIRECT goes to LOGIN_REDIRECT but when I disable Login it goes to the EMAIL_CONFIRMATION_REDIRECT route.
When I try printing the adapter settings for email_confirmation redirect url below it shows only the LOGIN_REDIRECT
def get_email_confirmation_redirect_url(self, request):
""" The URL to return to after successful e-mail confirmation. """
if request.user.is_authenticated:
if app_settings.EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL:
return \
app_settings.EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL
else:
return self.get_login_redirect_url(request)
else:
return app_settings.EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL
I tried overriding this get_email_confirmation_redirect_url in the adapter but still wont work. It is not picking the REDIRECT before I login and reverify.
Since ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = '/profile' was not working if the user is not logged in, I decided to override DefaultAccountAdapter in Django Allauth. My login was that if the time the user joined the app and the time logged in exceeds a certain threshold, then the redirection would be different. So I created an adapter in my users app as below:
class AccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
expiry = 90 #seconds
assert request.user.is_authenticated
if (request.user.last_login - request.user.date_joined).seconds < expiry:
url = 'profile/'
else:
url = settings.LOGIN_REDIRECT_URL
return resolve_url(url)
I then passed this adapter in my settings.py
Related
But what if I want to check without login. For example, I have one api logout which is having LoginRequired mixin, I want to test that anyone cannot hit logout if hes not logged in. So I created testcase as below.
#pytest.mark.django_db
def test_user_logout_logged_in_user(self, client):
url = reverse('logout')
response = client.get(url)
assert response.status_code == 302
assertRedirects(response, reverse('login'))
but I got this error,
AssertionError: '/auth/login-view/?next=%2Fauth%2Flogout-view%2F' != '/auth/login-view/'
Means I get ?next={something for after login api}.
How do I check that redirected api is login api.
My logout api is as below:
class LogoutView(LoginRequiredMixin, View):
"""
description: This is user logout view.
GET request will log out user and redirects to home page.
"""
def get(self, request):
return LogoutService.logout_user(request=request)
class LogoutService:
#staticmethod
def logout_user(request):
logout(request)
messages.success(request, ACCOUNT_LOGOUT_SUCCESS)
return redirect('home')
Django test for a page that redirects
I tried this answer, but it is for logged-in user. I want to test for un authenticated user.
To test that a view redirects with the "next" parameter correctly you need to add the next get parameter to the login URL when testing
#pytest.mark.django_db
def test_login_required_redirect(self, client):
url = reverse('someview')
response = client.get(url)
assertRedirects(response, f"{reverse('login')}?next={url}")
Don't use LoginRequiredMixin for your logout view, you set next to your logout view this way and that will cause the user to instantly logout after they login - handle the redirect manually in your view
class LogoutView(View):
def get(self, request):
if not request.user.is_authenticated:
return redirect('login')
return LogoutService.logout_user(request=request)
I'm trying to get a local copy of a Django site working. The production site works just fine on login, but my local instance doesn't redirect to the profile page after completing the login form.
This is the login_page view:
def login_page(request):
profile_page = HttpResponseRedirect('profile')
if request.user.is_authenticated():
return profile_page
form = LoginForm(request.POST or None)
if request.POST and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return profile_page
return render(request, 'login.html', {'form': form})
This is what the debug output of the server shows:
Performing system checks...
<function home_page at 0x7f77ad696c08>
System check identified no issues (0 silenced).
July 08, 2017 - 03:21:39
Django version 1.9.1, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[08/Jul/2017 03:21:49] "GET / HTTP/1.1" 200 3276
[08/Jul/2017 03:21:50] "GET /login HTTP/1.1" 200 2370
[08/Jul/2017 03:21:57] "POST /login HTTP/1.1" 302 0
[08/Jul/2017 03:21:57] "GET /profile HTTP/1.1" 302 0
[08/Jul/2017 03:21:57] "GET /login?next=/profile HTTP/1.1" 200 2370
After the above, the browser is left at http://127.0.0.1:8000/login?next=/profile and just displays the standard login page.
Again, identical code is working on the same version of Django in production (though running through gunicorn/nginx instead of django-admin runserver), so it makes me think that there's something in my Django config that I'm missing rather than an actual code problem.
urls.py entries:
from accounts import urls as account_urls
...
url(r'^', include(account_urls)),
accounts/urls.py:
from django.conf.urls import url
import accounts.views
urlpatterns = [
url(r'profile/?$', accounts.views.user_profile_page,
name='user_profile_page'),
Profile view (this never gets triggered AFICT - sticking a breakpoint in there doesn't help):
#login_required
def user_profile_page(request):
"""Returns user home page, with respective user status of surveys."""
print "User profile accessed: %s" % request
// user specific data here
context = {'some': some, 'data': data,
'here': here, }
return render(request, 'accounts/profile.html', context)
Also interesting: resolve_url doesn't seem to do the remapping like I would expect:
(Pdb) resolve_url('/profile')
'/profile'
Shouldn't that point to acccounts/profile or 127.0.0.1:8000/profile or something like that?
This is the AUTHENTICATION_BACKEND's 'authenticate' method that is getting executed (not sure how this differs from standard Django). All of the answers here imply that authenticate needs to accept the request argument - can I update this method to append something here?:
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
if username is not None:
username = username.lower()
user = UserModel._default_manager.get_by_natural_key(username)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
Changed in Django 1.10:
In older versions, when you’re manually logging a user in, you must successfully authenticate the user with authenticate() before you call login(). Now you can set the backend using the new backend argument.
If you using Django<=1.10, you must use authenticate method before you login. Otherwise, you have to feed authentication backend at least in login method.
Here is the code snippet from django docs.
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
try this
from django.shorcuts import redirect
from django.contrib.auth import authenticate
def login_page(request):
profile_page = HttpResponseRedirect('profile')
if request.user.is_authenticated():
return profile_page
form = LoginForm(request.POST or None)
if request.POST and form.is_valid():
user = authenticate(request,username=form.cleaned_data['username'],password=form.cleaned_data['password'])
if user:
login(request, user)
return redirect('profile')
Try modifying:
profile_page = HttpResponseRedirect('profile')
to:
profile_page = HttpResponseRedirect(reverse('profile'))
try with class bassed views
class Login(FormView, View):
template_name = 'login/login.html'
form_class = AuthenticationForm
success_url = reverse_lazy("your_succes:url")
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
return HttpResponseRedirect(self.get_success_url())
else:
return super(Login, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
login(self.request, form.get_user())
return super(Login, self).form_valid(form)
to get your page redirected to some other URL you actually require to use import redirect from Django shortcuts and utilize this to redirect to the required URL this is the ones available(as you already have created a Django website) in urls.py you can Have a look at the youtube video link
https://www.youtube.com/watch?v=aCotgGyS2gc&list=PL6gx4Cwl9DGBlmzzFcLgDhKTTfNLfX1IK&index=35
Another thing You may want to try is using templates as it makes Web developer's life Easier and safer
Answering the Question about gunicorn/nginx the application server has its default route set, you might have done it in the production build, to the profile page when the session info is added.
Also, check with the naming of the page profile it presently does not have any file extensions
you can also try with URL reverse
Instead of HttpResponseRedirect which triggers a HTTP 302, use a HttpResponseTemporaryRedirect to trigger a HTTP 307.
What happens is that 302 does not ensure the replay of the POST request. The reason is as follows:
RFC 1945 and RFC 2068 specify that the client is not allowed to change
the method on the redirected request. However, most existing user
agent implementations treat 302 as if it were a 303 response,
performing a GET on the Location field-value regardless of the
original request method. The status codes 303 and 307 have been added
for servers that wish to make unambiguously clear which kind of
reaction is expected of the client.
What's the difference between a 302 and a 307 redirect?
So I'm using Rdio to login and create users, and wrote a backend to handle its oauth. The first time you try to sign in using Rdio, it creates a user and an attached Rdio user, but it doesn't create a session and return the session cookie.
The flow is like any oauth2 flow: you press a button on my app, it redirects w/ get params to Rdio, and Rdio calls a callback view on my app (along with a code in the GET params). In that callback view, I call authenticate:
class RdioCallbackView(View):
def get(self, request):
""" here, you need to create and auth a django user and create and tie the rdio user's stuff to it """
if request.user.is_authenticated() == False:
try:
rdio_code = request.GET['code']
except KeyError:
return redirect(reverse('login'))
# authenticate
user = auth.authenticate(rdio_code=rdio_code)
if user is not None and user.is_active:
auth.login(request, user)
else:
return render(request, 'home/login.html', {'rdio_url': create_rdio_auth_url(), 'message': "That code didn't seem to work"})
else:
# user exists!
user = request.user
return HttpResponseRedirect(reverse('the-next-view'))
The custom auth backend looks like this:
class RdioBackend(object):
def authenticate(self, rdio_code=None):
token_info = exchange_rdio_code(rdio_code)
try:
access_token = token_info['access_token']
refresh_token = token_info['refresh_token']
except KeyError:
return None
except TypeError:
# the code was probably already used.
return None
rdio_user_dict = get_rdio_user_for_access_token(access_token)
rdio_key = rdio_user_dict['key']
try:
rdio_user = RdioUser.objects.get(rdio_id=rdio_key)
rdio_user.access_token = access_token
rdio_user.refresh_token = refresh_token
rdio_user.save()
user = rdio_user.user
except RdioUser.DoesNotExist:
user = User.objects.create(username=rdio_key)
user.set_unusable_password()
rdio_user = RdioUser.objects.create(
rdio_id = rdio_key,
access_token = access_token,
refresh_token = token_info['refresh_token'],
user = user,
)
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
And that's where things get weird. It doesn't seem to make a new Session object, and definitely doesn't return a session cookie. However, when I go back and do the Rdio login again for a second time, it returns a session cookie, makes the session on the backend, and login and auth work perfectly.
And I think my AUTHENTICATION_BACKENDS settings is right:
AUTHENTICATION_BACKENDS = (
'appname.backend.RdioBackend',
'django.contrib.auth.backends.ModelBackend',
)
Edit: More possibly relevant info:
The views that it's redirecting to have a LoginRequiredMixin:
class LoginRequiredMixin(object):
#classmethod
def as_view(cls, **initkwargs):
view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
return login_required(view)
And in RdioCallbackView, when I change the final line from return HttpResponseRedirect(reverse('the-next-view')) to instead just serve the template directly with return render(request, 'path/to.html', param_dict), it does serve the cookie and make a sessionid, but then it deletes it from the DB and from the browser the moment I navigate away from that screen.
This might be the dumbest bug ever. It turns out that if you create a user without a password, you don't need to call user.set_unusable_password(). And if you do call user.set_unusable_password(), it somehow messes with any auth you do (even AFTER you call that).
So to fix this, I just got rid of the call to user.set_unusable_password() in my custom django auth backend.
I want to authenticate a user such that when he logs into his account and then wants to go back to the login page, he/she should be automatically redirected to the dashboard page. How can I do that?
#login_required
#csrf_exempt
def dashboard(request):
users = GrabhaloUser.objects.exclude(user_id = request.user.id)
if request.is_ajax():
if request.POST.has_key('message'):
selected_users = request.POST.getlist('selected_users[]')
message = request.POST['message']
send_query(request,selected_users,message)
ctx = { 'users' : users }
return render_to_response('dashboard/dashboard.html',ctx, context_instance = RequestContext(request))
login URLS
urlpatterns = patterns('',
url(r'login/',login,kwargs = {'template_name' : 'auth/login.html'}, name = 'grabhalo_login'),
url(r'logout/', logout,kwargs = {'template_name' : 'auth/logout.html'}, name = 'grabhalo_logout'),
url(r'register/','apps.auth.views.register', name = 'grabhalo_register'),
)
Make a function login_page , check the authentication of the user there, if authenticated, redirect it to dashboard, else return to the login page.
Map this function to the login url in urls.py
def login_page(request):
if request.user.is_authenticated():
return redirect('/dashboard/')
else:
return login(request)
And then map this function to the login url.
url(r'login', 'modules.energy.login.views.login_page', name = 'cilantro_login'),
You may try this:
Whenever the user clicks the login page link, the view for the login page is executed. In that view, check if the user is logged in. If the user is logged in,, then redirect him to the dashboard, else display the login page. It is as simple as it is. The sample code:
if request.user.is_authenticated():
#load dashboard
else:
#load login page
i have my login form in the homepage itself i.e. "/". now from there i want to redirect a user to 0.0.0.0:8000/username where 'username' is not static, it i different for different users.
I'm a beginner to Django. Pls explain in dept. Thanks in advance
what you could do is define a home url and a profile url in your urls.py like this.
#urls.py
url(r'^$', 'app.views.home'),
url(r'^(?P<username>\w+)/$', 'app.views.profile'),
now under views.py define 2 views one to render the home page and second to render the profile page
# views.py
import models
from django.shortcuts import render_to_response
from django.templates import RequestContext
from django.contrib.auth import authenticate, login
def home(request):
"""
this is the landing page for your application.
"""
if request.method == 'POST':
username, password = request.POST['username'], request.POST['password']
user = authenticate(username=username, password=password)
if not user is None:
login(request, user)
# send a successful login message here
else:
# Send an Invalid Username or password message here
if request.user.is_authenticated():
# Redirect to profile page
redirect('/%s/' % request.user.username)
else:
# Show the homepage with login form
return render_to_response('home.html', context_instance=RequestContext(request))
def profile(request, username):
"""
This view renders a user's profile
"""
user = user.objects.get(username=username)
render_to_response('profile.html', { 'user' : user})
Now when the first url / is requested it forwards the request to app.views.home which means the home view ===within===> views.py ===within===> app application.
the home view checks if a user is authenticated or not. if a user is authenticated it calls the url /username otherwise it simply renders a template called home.html in your templates directory.
The profile view accepts 2 arguments, 1. request and 2. username. Now, when the profile view is called with the above mentioned arguments it gets the user instance for the username provided and stores it in a user variable and later passes it to the profile.html template.
also please do read through the very easy Poll Application Tutorial on Django Project to get familiar with the power of django.
:)