251

I'm trying to save a object to my database, but it's throwing a MultiValueDictKeyError error.

The problems lies within the form, the is_private is represented by a checkbox. If the check box is NOT selected, obviously nothing is passed. This is where the error gets chucked.

How do I properly deal with this exception, and catch it?

The line is

is_private = request.POST['is_private']
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
dotty
  • 40,405
  • 66
  • 150
  • 195
  • 2
    A good idea would be to show us the whole error and the trace. Also show us more of that portion of code where the error is raised. – rzetterberg May 05 '11 at 09:43
  • 1
    Can anyone explain why does this error occurs?I have seen this error when i use different Modelviewset in django rest..... – Amrit Jan 05 '17 at 09:20
  • 2
    it means simply: the key 'is_private' doesn't exist! – ThePhi Aug 19 '17 at 05:29

9 Answers9

372

Use the MultiValueDict's get method. This is also present on standard dicts and is a way to fetch a value while providing a default if it does not exist.

is_private = request.POST.get('is_private', False)

Generally,

my_var = dict.get(<key>, <default>)
adamnfish
  • 10,935
  • 4
  • 31
  • 40
107

Choose what is best for you:

1

is_private = request.POST.get('is_private', False);

If is_private key is present in request.POST the is_private variable will be equal to it, if not, then it will be equal to False.

2

if 'is_private' in request.POST:
    is_private = request.POST['is_private']
else:
    is_private = False

3

from django.utils.datastructures import MultiValueDictKeyError
try:
    is_private = request.POST['is_private']
except MultiValueDictKeyError:
    is_private = False
pztrick
  • 3,741
  • 30
  • 35
Silver Light
  • 44,202
  • 36
  • 123
  • 164
  • 19
    Really can't recommend number 3. – Joe May 05 '11 at 09:47
  • 6
    It just seems like an abuse of the exception system. Exceptions should be for handling exceptional behaviour (i.e. behaviour you know that may happen, and must deal with, but that you don't expect in the normal program flow). In this case, the exception will be thrown and caught in 50% of the possible program flows. Added to that is the slow-down. I don't know the details of how it works in Python, but I would imagine an expensive stack-trace would be involved. – Joe May 05 '11 at 09:53
  • 15
    from django.utils.datastructures import MultiValueDictKeyError – Akseli Palén Feb 10 '13 at 22:35
  • 10
    @Joe - In Python this approach is pretty common. If you're catching the exception it won't automatically generate a stacktrace. http://docs.python.org/2/glossary.html#term-eafp – bjudson Sep 22 '13 at 21:29
  • 2
    @bjudson I know, exceptions are used for all kinds of things, not all of them necessarily how the language designers intended. Case in point the outrage caused by `java.io.EOFException`, plenty of disagreement on that. I wouldn't recommend using a try/catch for non-exceptional control flow, even if it works, as it's not how checked exception were designed. I accept that it's an opinion rather than fact. – Joe Sep 23 '13 at 07:27
  • 1
    And I'm not sure that this is a valid application of EAFP anyway. If you always expected to see an `is_private` argument to your Django view, then requesting it and dealing with the exception would be the right way to do it. If, however, it's an optional argument that may or may not be present, then your code should reflect that. – Joe Sep 23 '13 at 08:05
  • 12
    There is nothing wrong with step 3. We call that Easier to ask for forgiveness than permission (EAFP), and it is a highly recommended coding style in Python. Plenty of posts on StackOverflow have even discussed this. – Bobort Sep 19 '17 at 14:56
15

You get that because you're trying to get a key from a dictionary when it's not there. You need to test if it is in there first.

try:

is_private = 'is_private' in request.POST

or

is_private = 'is_private' in request.POST and request.POST['is_private']

depending on the values you're using.

Joe
  • 46,419
  • 33
  • 155
  • 245
8

Another thing to remember is that request.POST['keyword'] refers to the element identified by the specified html name attribute keyword.

So, if your form is:

<form action="/login/" method="POST">
  <input type="text" name="keyword" placeholder="Search query">
  <input type="number" name="results" placeholder="Number of results">
</form>

then, request.POST['keyword'] and request.POST['results'] will contain the value of the input elements keyword and results, respectively.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Leo
  • 449
  • 6
  • 5
  • 1
    This is what solved my error, I thought it was the 'id' element that is fed to the post request, not knowing it was the 'name' value. – oriohac Sep 19 '22 at 16:08
5

Why didn't you try to define is_private in your models as default=False?

class Foo(models.Models):
    is_private = models.BooleanField(default=False)
Drewness
  • 5,004
  • 4
  • 32
  • 50
Edson Dota
  • 383
  • 4
  • 6
4

First check if the request object have the 'is_private' key parameter. Most of the case's this MultiValueDictKeyError occurred for missing key in the dictionary-like request object. Because dictionary is an unordered key, value pair “associative memories” or “associative arrays”

In another word. request.GET or request.POST is a dictionary-like object containing all request parameters. This is specific to Django.

The method get() returns a value for the given key if key is in the dictionary. If key is not available then returns default value None.

You can handle this error by putting :

is_private = request.POST.get('is_private', False);
Community
  • 1
  • 1
Projesh Bhoumik
  • 1,058
  • 14
  • 17
4

For me, this error occurred in my django project because of the following:

  1. I inserted a new hyperlink in my home.html present in templates folder of my project as below:

    <input type="button" value="About" onclick="location.href='{% url 'about' %}'">
  2. In views.py, I had the following definitions of count and about:

   def count(request):
           fulltext = request.GET['fulltext']
           wordlist = fulltext.split()
           worddict = {}
           for word in wordlist:
               if word in worddict:
                   worddict[word] += 1
               else:
                   worddict[word] = 1
                   worddict = sorted(worddict.items(), key = operator.itemgetter(1),reverse=True)
           return render(request,'count.html', 'fulltext':fulltext,'count':len(wordlist),'worddict'::worddict})

   def about(request): 
       return render(request,"about.html")
  1. In urls.py, I had the following url patterns:
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('',views.homepage,name="home"),
        path('eggs',views.eggs),
        path('count/',views.count,name="count"),
        path('about/',views.count,name="about"),
    ]

As can be seen in no. 3 above,in the last url pattern, I was incorrectly calling views.count whereas I needed to call views.about. This line fulltext = request.GET['fulltext'] in count function (which was mistakenly called because of wrong entry in urlpatterns) of views.py threw the multivaluedictkeyerror exception.

Then I changed the last url pattern in urls.py to the correct one i.e. path('about/',views.about,name="about"), and everything worked fine.

Apparently, in general a newbie programmer in django can make the mistake I made of wrongly calling another view function for a url, which might be expecting different set of parameters or passing different set of objects in its render call, rather than the intended behavior.

Hope this helps some newbie programmer to django.

S. Esteves
  • 415
  • 4
  • 14
TNT
  • 480
  • 1
  • 4
  • 11
0

I got 'MultiValueDictKeyError' error while using ajax with Django. Just because of not putting '#' while selecting an element. Like this.

data:{ name : $('id_name').val(),},

then I put the '#' with the id and the problem is solved.

data:{ name : $('#id_name').val(),},
Rubel
  • 1,255
  • 14
  • 18
0

This will insert NULL value if the name is not present in the request

name = request.data.get('name')
bairavand
  • 335
  • 3
  • 11
  • It is not necessary to specify the `None`. The `get` method will return `None` if the key does not exist and there is no default value. Therefore, you can do `name = request.data.get("name")` to obtain the same behavior. – Marc Compte Jun 14 '23 at 11:28
  • Yes, I edited the answer. – bairavand Jun 16 '23 at 12:35