Django credit card redaction app - - MultiValueDictKeyError

197 views
Skip to first unread message

drone4four

unread,
Mar 7, 2019, 8:51:24 AM3/7/19
to Django users

Hello!


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


Here is my 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})



home.html template:

<style></style>




<center>

<form action="{% url 'home' %}">

 
<div>  <br>  <br>  <br>  <br>

   
<h3>Enter your fake Chuckee Cheese Neptune credit card number!</h3>

   
<input type="number" id="number" name="ccEntry" required placeholder=" " pattern=".{16,16}"/>

     
<br>

     
<br>

     
<div class="requirements">

       Must be a 16 digit number.

     
</div>

     
<br>

   
<input type="submit" value="Redact!!!"/>

       
</div>

   
</form>

   
<br>

   
<h3>Original Card Number:</h3>

   
<br>

   {{ number }}

   
<br>

   
<h3>Redacted Card Number:</h3>

   
<br>

   {{ redacted_num }}

   
<br>

   
<h3> Did this work?</h3>  

   
<br>

</center>



Sam Taiwo

unread,
Mar 7, 2019, 10:42:25 AM3/7/19
to django...@googlegroups.com
I would be careful how you pass the data on get, somewhere in your front end code you are probably not passing the data through as you expected. Instead of using .GET, use .GET.get to get your data. This will either return the value you want, or none. I think you have made the error of not passing your data to the get header as you didn't explicitly write this on the form element. Check the URL and log the get object in Django to make sure the data is actually appearing in both.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/49da3086-6f3d-415b-b451-7e4c6a6e542c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

drone4four

unread,
Mar 7, 2019, 12:28:51 PM3/7/19
to Django users

Thanks, Sam for the help.


You wrote:


I would be careful how you pass the data on get, somewhere in your front end code you are probably not passing the data through as you expected. Instead of using .GET, use .GET.get to get your data. This will either return the value you want, or none.

I looked up GET.get Django and found an SO post titled, “Django request.GETwhich has quite a few up votes. 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


You also mentioned my front end. I had included the template in my original post. I have since refined it so it is more readable. Here is my improved home.html template:


<html>



<head>

 
<title> Search </title>

</head>

 
<body>


<center>

<form action="{% url 'home' %}">

 
<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>



Sam, you also suggested:


I think you have made the error of not passing your data to the get header as you didn't explicitly write this on the form element.

I did explicitly include my data in my form element as you can see in the above template.



Check the URL and log the get object in Django to make sure the data is actually appearing in both.

The data is present as “ccEntry” in both home.html and in views.py.



Matthew Pava

unread,
Mar 7, 2019, 1:34:17 PM3/7/19
to django...@googlegroups.com

Please just paste the error in the email message. The error message that you have mentions a ‘method’ object, which is not present in the code that you have sent to us.  That would be where the problem is.

drone4four

unread,
Mar 7, 2019, 2:13:17 PM3/7/19
to Django users
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

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

To post to this group, send email to djang...@googlegroups.com.

Deepak Chhitarka

unread,
Mar 7, 2019, 2:50:00 PM3/7/19
to django...@googlegroups.com
In your home.html template, you forgot to write method="get" . Which I think is causing the problem here.

drone4four

unread,
Mar 7, 2019, 4:03:37 PM3/7/19
to Django users

I am taking a course on Udemy and the instructor doesn’t include a method attribute in the form tag.  But as you suggested, Deepak, I went ahead and added method="get" to my form tag in my home.html template. So line 7 now reads: <form action="{% url 'home' %}" method="get">

I am still encountering the MultiValueDictKeyError.


Here is the traceback again:

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 20:11:44 +0000

To post to this group, send email to djang...@googlegroups.com.

drone4four

unread,
Mar 7, 2019, 4:05:28 PM3/7/19
to Django users

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


drone4four

unread,
Mar 14, 2019, 9:56:57 AM3/14/19
to Django users

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 web page. 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?


Ahmed Ishtiaque

unread,
Mar 14, 2019, 2:13:35 PM3/14/19
to django...@googlegroups.com
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. 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

drone4four

unread,
Mar 14, 2019, 7:21:19 PM3/14/19
to Django users

Thank you Ahmed for this clarification. This makes sense.


I have two further questions for you (or anyone else still reading this thread):


  1. Is there a way of checking for the presence of the data with GET which is more Pythonic and aligns with better practices?


  1. On an unrelated note, I attempted to reduce my issue in this testcase as best I could. I think I did an OK job. But would there be a better way for me to ask my question to attract more responses? What would any of you recommend in terms of advice for me to refine and refine the way I ask similar questions in the future on this message board?


Ahmed Ishtiaque

unread,
Mar 15, 2019, 3:48:25 AM3/15/19
to django...@googlegroups.com
Hello drone4four,

Here are my opinionated responses on your questions.

1. What you have in your view is pythonic enough. That's the right way to look up a value in a dict, and it's easy to understand. However, the way I prefer to do things, I'd say that whenever someone enters a credit card number, make a POST request to the view rather than a GET request. This intuitively makes sense to me because I know for sure that the POST request that's being parsed in a certain view should contain exactly the data I want it to contain, rather than trying to figure out from a GET request whether the data is present there. Also, since you're using an example credit card app, I'd point you to paying attention towards your address bar right after you make the GET request. It will contain the credit card number in it. This can be a security issue when you're dealing with forms in a practical sense with django. So, as far as I'm concerned, the more "Django-esque" way is to make your credit card number submission a POST request to the appropriate view.

2. As far as getting responses, I think it's always better the more code you share for us to look at. For example, I just saw that you had a repo setup for people to check out. I think that's a pretty good way to go. Of course, if you have a private repo and don't want to give all your code away, we understand that. In that case generally posting the code like you did before should be fine and we'll try to help you out. 

Hope your experience with django continues to be meaningful in the future!

Ahmed


Reply all
Reply to author
Forward
0 new messages