Python Forum

Full Version: Django credit card redaction app - - MultiValueDictKeyError
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I’m trying to run a basic Django app which redacts a 16 digit number entered by the user. I had a it running a few minutes ago. I’m not sure what I changed, but now I am getting a MultiValueDictKeyError. I’ve triple checked every variable. The only dictionary in my project is in views.py at line 7. As far as I can tell, it’s all accurate and positioned correctly. I don’t recall changing this dictionary or any other operator around this line. I’m stumped. What would you people suggest? Have a look at my setup.

Here is the error and traceback in full: https://pastebin.com/QwSrmAJx

urls.py:
from django.conf.urls import  include, url
from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', views.home, name='home'),
]
views.py:
from django.http import HttpResponse
from django.shortcuts import render

def home(request):
    number = request.GET['ccEntry']
    redacted_num = 'xxxx xxxx xxxx {}'.format(number[-4:])
    return render(request, 'home.html', {'number':number, 'redacted_num':redacted_num})
Template (home.html):
<html>
 <head>
  <title> CC Redact </title>
 </head>
   <body>
<center>
<form action="{% url 'home' %}" method="get">
  <div> 
    <h3>Enter your fake Chuckee Cheese Neptune credit card number!</h3>
    <input type="number" id="number" name="ccEntry" required placeholder=" " pattern=".{16,16}"/>
      <div class="requirements"> Must be a 16 digit number. </div>
    <input type="submit" value="Redact!!!"/>
  </div>
</form>
    <h3>Original Card Number:</h3>
    {{ number }}
    <h3>Redacted Card Number:</h3>
    {{ redacted_num }}
    <h3> Did this work?</h3>    
</center>
</body>
</html>
Here is the error, traceback and Request information:

MultiValueDictKeyError at /
'ccEntry'
Request Method:
GET
Request URL:
http://127.0.0.1:8000/
Django Version:
2.0.2
Exception Type:
MultiValueDictKeyError
Exception Value:
'ccEntry'
Exception Location:
/home/<user>/.local/lib/python3.7/site-packages/django/utils/datastructures.py in __getitem__, line 79
Python Executable:
/usr/sbin/python
Python Version:
3.7.2
Python Path:
['/home/<user>/dev/projects/python/2018-and-2019/cel2fah-original_with_CC-redact-project-_Django202/first_project_attempt',
 '/usr/lib/python37.zip',
 '/usr/lib/python3.7',
 '/usr/lib/python3.7/lib-dynload',
 '/home/<user>/.local/lib/python3.7/site-packages',
 '/usr/lib/python3.7/site-packages',
 '/usr/lib/python3.7/site-packages/setuptools-40.6.2-py3.7.egg']
Server time:

Thu, 7 Mar 2019 17:39:40 +0000

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

Django Version: 2.0.2
Python Version: 3.7.2
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']

Traceback:
File "/home/<user>/.local/lib/python3.7/site-packages/django/utils/datastructures.py" in __getitem__
  77.             list_ = super().__getitem__(key)

During handling of the above exception ('ccEntry'), another exception occurred:

File "/home/<user>/.local/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  35.             response = get_response(request)

File "/home/<user>/.local/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  128.                 response = self.process_exception_by_middleware(e, request)

File "/home/<user>/.local/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  126.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/<user>/dev/projects/python/2018-and-2019/cel2fah-original_with_CC-redact-project-_Django202/first_project_attempt/first_project_attempt/views.py" in home
  5.     number = request.GET['ccEntry']

File "/home/<user>/.local/lib/python3.7/site-packages/django/utils/datastructures.py" in __getitem__
  79.             raise MultiValueDictKeyError(key)

Exception Type: MultiValueDictKeyError at /
Exception Value: 'ccEntry'

Request information
USER
AnonymousUser
GET
No GET data
POST
No POST data
FILES
No FILES data
I’ve double checked to make sure that I include my data in my form element as you can see in the above template. The data is present as “ccEntry” in both home.html and in views.py.

In my home.html template, even though the instructor instructor in my Udemy course doesn’t use it, I added a method="get" attribute to my HTML form tag. This didn’t change anything.

I also looked up “Django request.GET”. Based on that SO answer, In my views.py I changed the line (where I declare the number variable) from number = request.GET['ccEntry'] to number = request.GET.get['ccEntry', None]. That gives a different error (a type error this time): https://pastebin.com/L81LVtzi

This is a long shot, but I thought I would share a link to my source code hosted on GitHub with a requirements.txt included. If any of you would like to test this out yourself, I am accepting pull requests. Here it is: https://github.com/Angeles4four/CC_Redact
I’ve discovered a ‘hack’ to correct the issue. I committed and pushed my changes up to my GitHub repo for those curious enough to try it out yourself.

My problem now is that I don’t really understand why it works. Here I will explain what I do know and then my ask would be for you people to fill in the gaps in my explanation. First I share my working code.

Here is my views.py which includes the relevant function I use:

from django.http import HttpResponse
from django.shortcuts import render

def home(request):
   if 'ccEntry' in request.GET:
       number = request.GET['ccEntry']
       redacted_num = 'xxxx xxxx xxxx {}'.format(number[-4:])
       return render(request, 'home.html', {'number':number, 'redacted_num':redacted_num})
   else:
       return render(request, 'home.html')
Here is my lucid, cerebral explanation of the above Python code in plain English: At line 4 I am defining the home function. Then there is a conditional referring to the ‘ccEntry’ string inside the template. If ccEntry is present in the GET request, then a number variable is declared based on the user input on the webpage. Then a redacted_num variable is declared which will appear as a string (‘xxxx xxxx xxxx ’ with the last 4 characters lopped off (the slice)). If all of the above is in order, then the render function will be returned:
  • with the standard request,
  • with reference to the home.html template,
  • along with a dictionary with a ‘number’ string matching up with the number variable. Ditto for the redact_num.
However if the above condition is false, then the render function will return the standard request and home.html template without a dictionary. That’s pretty much everything I understand.

I’m not sure why a conditional as it appears above is necessary.

Would someone here care to explain, if you can?
Here’s the explanation for why this hack works the way it does. It’s simple. If a user lands on the webpage and the ccEntry parameter is not present, it throws MultiValueDictKeyError. So the conditional checks for the presence of the parameter.

The forum member over at Django user Google group clarifies in greater detail:

Quote:When a user requests your home page's URL the first time in order to load your home.html file, the GET request they send to your server does not contain the 'ccEntry' parameter, which raised the MultiValueDictKeyError whenever your view was executing. However, in your edited view, you take care of that by first checking whether the GET request contains 'ccEntry', which is why it is working correctly now.