12

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)
javanix
  • 1,270
  • 3
  • 24
  • 40
  • Could you show us the `profile` view? Maybe something's fishy there. Since, after login, successful redirection is performed. But, in the profile page, an additional redirection is occurring according to your debug output. It'd be much appreciated to share your `profile` view also, in order to investigate further in this matter. – zaidfazil Jul 12 '17 at 04:43
  • If you can post your form and profile view code , that would be beneficial for us to solve your problem. – Aniket Pawar Jul 12 '17 at 05:47
  • Have you [enabled sessions](https://docs.djangoproject.com/en/1.11/topics/http/sessions/#enabling-sessions) ? Do you use same versions of Django ? What are the differences in `settings.py` ? Also as already suggested, code for `LoginForm` and `profile` view could help. – Michael Rigoni Jul 12 '17 at 14:28
  • Are Django versions of production and local same? – Muhammad Fahad Manzoor Jul 15 '17 at 15:33
  • `LoginForm` doesn't have anything exciting in it, I added the `profile` view though it doesn't have anything interesting on it. Django is `1.9.1` on both test and prod instances. – javanix Jul 17 '17 at 01:58
  • I just noticed another interesting thing that you mentioned in your posting. You wrote that production is working fine with gunicorn but not with runserver, can you try running the code using gunicorn on your local setup? Have you double checked the credentials by logging user in using django shell? – Muhammad Fahad Manzoor Jul 17 '17 at 11:52
  • Same behavior with gunicorn running locally. – javanix Jul 17 '17 at 17:38
  • And what about the credentials confirmation? Did it go as expected? – Muhammad Fahad Manzoor Jul 17 '17 at 19:12
  • I think you're on the right track - the default 'authenticate' method imported via django.contrib.auth does not accept any non-keyword args (eg, passing in the request obj throws an exception) and settings.py defined a custom AUTHENTICATION_BACKEND. Do methods decorated with `@login_required` need to have something set on the actual `request` object? – javanix Jul 17 '17 at 21:15

6 Answers6

7
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.
    ...
Erdenezul
  • 597
  • 1
  • 7
  • 10
  • I updated the login code as specified to auth first - still having the same behavior. I don't understand where the `GET /profile` 302 is coming from. – javanix Jul 17 '17 at 17:39
  • I saw your last edit that you override authentication backend. Why you override autheticate method if it doesn't differ from original one? In the django code, there is two authenticate method in seperate class that might be the reason for running different between production and development. – Erdenezul Jul 18 '17 at 02:02
  • production one using the django's authenticate and development one using the your customized authenticate. I am giving thought based on your all code. – Erdenezul Jul 18 '17 at 02:04
4

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')
Exprator
  • 26,992
  • 6
  • 47
  • 59
2

Try modifying:

  profile_page = HttpResponseRedirect('profile')

to:

  profile_page = HttpResponseRedirect(reverse('profile'))
yusuf.oguntola
  • 492
  • 3
  • 10
1

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)
Mauricio Cortazar
  • 4,049
  • 2
  • 17
  • 27
1

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

Community
  • 1
  • 1
Aloy A Sen
  • 764
  • 5
  • 9
1

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?

Fabien
  • 4,862
  • 2
  • 19
  • 33