15

I'm trying to build a login page. I'm running Django 1.6.1. I've largely been following the tutorial at www.fir3net.com/Django/django.html. For convenience I will repost a lot of it here.

Error Message:

Request Method: GET
Request URL: http://127.0.0.1:8000/login/

Database In Use: SQLite3 

Django Version: 1.6.1
Python Version: 2.7.4
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.contenttypes',
 'bookmarks')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.RemoteUserMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware')


Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
  114.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/tatenda/Documents/Programming/Django/Progs/django_bookmarks/bookmarks/views.py" in login_user
  17.   username = request.POST['username']
File "/usr/local/lib/python2.7/dist-packages/django/utils/datastructures.py" in __getitem__
  301.             raise MultiValueDictKeyError(repr(key))

Exception Type: MultiValueDictKeyError at /login/
Exception Value: "'username'"

View Code (I understand that several of the import functions called here are superfluous to my immediate problem. Mostly they are redundant code from trying to follow previous tutorials on how to do this):

from django.http import*
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from django.template import Context
from django.template.loader import get_template
from django.http import HttpResponse, Http404
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.contrib.auth.decorators import login_required

def login_user(request):

    username = password = ''
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(username=username, password = password)
    if user is not None:
        if user.is_active:
            login(request, user)
            return HttpResponseRedirect('/main_page/')
    return render_to_response('base.html', context_instance = RequestContext(request))

@login_required(login_url = '/login/')
def main_page(request):
    template = get_template('main_page.html')
    variables = Context ({
        'head_title':'Django Bookmarks',
        'page_title':'Welcome to Django Bookmarks',
        'page_body':'Where you store and share bookmarks!'
    })
    output = template.render(variables)
    return HttpResponse(output)

def user_page(request, username):
    try:
        user = User.objects.get(username=username)
    except:
        raise Http404('Requested user not found.')

    bookmarks = user.bookmark_set.all()
    template = get_template('user_page.html')
    variables = Context({
        'username':username,
        'bookmarks': bookmarks
    })
    output = template.render(variables)
    return HttpResponse(output)

@csrf_exempt
def login_page(request):

    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(username=username, password=password)
    if user is not None:
        if user.is_active:
            login(request,user)
            template = get_template('main_page.html')
            variables = Context ({
                'head_title':'Django Bookmarks',
                'page_title':'Welcome to Django Bookmarks',
                'page_body':'Where you store and share bookmarks!'
            })
            output = template.render(variables)
            return HttpResponse(output)
        else:
            raise Http404('Disabled account, please contact administrator.')
    else:
        raise Http404('Invalid login.')

Info from urls.py file:

from django.conf.urls import patterns, include, url
from django.contrib import admin
from bookmarks.views import*

admin.autodiscover()

urlpatterns = patterns('',
    (r'^$', main_page),
    (r'^user/(\w+)/$', user_page),
    url(r'^login/$', login_user),
)

Base Template Used to Create Login Page (the file is named base.html):
Note - the CSS styling is bootstrap based

<!DOCTYPE html>
<html>
    <head>
        {% block head %}
            <link rel = "stylesheet" href = "style.css" />
            <title> {% block title %}{% endblock %} - My Webpage</title></div>
        {% endblock %}
    </head>
    <body>
        <div id = "content">{% block content %}{% endblock %}</div>
        <div id = "footer">
            {% block footer %}
                &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>
            {% endblock %}
        </div>
    </body>
</html>

HTML Code for the Login Page (the file is named login.html):

{% extends "base.html" %}
{% block main %}
    <div id = "login">
        <form class = "form-horizontal" name = "LoginForm" action = "/login/" method = "post">
        {% csrf_token %}
        {% if next %}
            <input type = "hidden" name = "next" value = "{{ next }}" />
        {% endif %}
        <div class = "control-group">
            <label class = "control-label" for = "username">Username</label>
            <div class = "controls">
                <input type = "text" id = "username" placeholder = "Username">
            </div>
        </div>
        <div class = "control-group">
            <label class = "control-label" for = "password">Password</label>
            <div class = "controls">
                <input type = "password" name = "password" id = "password" placeholder = "Password">
            </div>
        </div>
        <div class = "control-group">
            <div class = "controls">
                <button type = "submit" class = "btn">Login</button>
            </div>
        </div>
        </form>
    </div>
{% endblock %}
yuvi
  • 18,155
  • 8
  • 56
  • 93
PyUnchained
  • 560
  • 1
  • 7
  • 16

5 Answers5

52

I had the same error, and i did this and it worked. Change:

username = request.POST['username']
password = request.POST['password'] 

to:

username = request.POST.get('username')
password = request.POST.get('password')

The above handles both the POST and GET methods that may result. I hope this helped.

Emack333
  • 660
  • 6
  • 11
  • 1
    It took me along time to appreciate that the answer to my question was that there isn't always a real POST dictionary with every request, because GET requests have no keys in the POST dict. Wonder if it's still true in 2.0 now. – PyUnchained Nov 01 '18 at 15:13
  • This answer is applicable even in later versions of Django. Saved me lot of frustration. Thanks! – SeaWarrior404 Feb 11 '19 at 07:22
  • 2
    When I changed the brackets I get "TypeError at /something/ 'QueryDict' object is not callable Request Method: POST Request URL: http://127.0.0.1:8000/something/ Django Version: 2.1.7 Exception Type: TypeError Exception Value: 'QueryDict' object is not callable" – Abhishek Aggarwal Apr 03 '19 at 12:43
  • `print("username is " + username)` gives **username is None** and sending request in Get can read the actual username – ajinzrathod Jul 11 '21 at 11:07
3

When a request resolves to a view that's wrapped with the @login_required decorator, the request is redirected to the specified URL if the user is not logged in. So attempting to resolve your main_page view while not logged in will cause the user's browser to issue a GET request to /login/. However, the view that handles that URL assumes a POST request:

username = request.POST['username']
password = request.POST['password']

The usual approach would be to follow the general pattern for using a form in a view: https://docs.djangoproject.com/en/dev/topics/forms/#using-a-form-in-a-view

Specifically, check the request.method attribute so you can behave appropriately on a GET request and render the form. Or use the built-in views, they're pretty easy to work with.

Peter DeGlopper
  • 36,326
  • 7
  • 90
  • 83
  • Thanks for the help, unfortunately I'm not able to connect to the djangoproject.com domain, even through search engines, so I'm going to assume the site's temporarily down but this has been great for pointing me in the right direction. I'm going to do a bit more looking into it and get back to you. – PyUnchained Dec 23 '13 at 19:20
  • http://www.downforeveryoneorjustme.com/ is useful for checking that kind of thing. – Peter DeGlopper Dec 23 '13 at 22:17
2

I was able to suppress error by following @Emack333 but then, the code on views.py was not working and then upon close inspection, I've found that error was not on view file, rather it was on HTML side.

This error got generated because there was a mismatch of name attribute on the HTML input tag and in your case, it was name attr is missing.

<input type = "text" id = "username" placeholder = "Username" name="username">
1

The real issue is the forms within your template.

You need to use the name= attribute to assign the name of the key instead of the id= attribute.

I just solved this for my own project and realized this was the culprit.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
0

This could also happen if you try to get a value with wrong or non existing key. It's worthy if you check your key name.