2

Hi I'm trying to upload an image file through a form field using Django and i'm getting django.utils.datastructures.MultiValueDictKeyError when trying to submit the form.

This is my forms.py:

class UserProfileForm(forms.ModelForm):
    type = forms.ModelChoiceField(queryset=UserType.objects.all(), required=True)
    phone = forms.CharField(required=False)
    address = forms.CharField(required=False)
    picture = forms.ImageField(required=False)
    class Meta:
        model = UserProfile # matches data model
        fields = ('type','phone','address','picture') # shown on the form

This is my models.py:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    type = models.ForeignKey(UserType, on_delete=models.CASCADE)
    phone = models.CharField(max_length=10, blank=True)
    address = models.CharField(max_length=128, blank=True)
    picture = models.ImageField(upload_to='profile_images', blank=True)
    def __str__(self):
        return self.user.username

This is my views.py:

def register(request):
    if request.method == 'POST':
        response = {}

        username = request.POST.get('username')
        password1 = request.POST.get('password1')
        password2 = request.POST.get('password2')
        type = request.POST.get('type')
        first_name = request.POST.get('first_name')
        last_name = request.POST.get('last_name')
        email = request.POST.get('email')
        phone = request.POST.get('phone')
        address = request.POST.get('address')
        picture = request.POST.get('picture')

        data = {'username': username,
                'password1': password1,
                'password2': password2,
                'type': UserType.objects.get(type=type).pk,
                'first_name': first_name,
                'last_name': last_name,
                'email': email,
                'phone': phone,
                'address': address,
                'picture': picture}
        user_form = UserForm(data)
        profile_form = UserProfileForm(data)

        if user_form.is_valid() and profile_form.is_valid():
            user = user_form.save()
            user.set_password(data['password1'])
            user.save()
            profile = profile_form.save(commit=False)
            profile.user = user
            #if 'picture' in request.FILES:
                #profile.picture = request.FILES['picture']
            #picture = Image.open(StringIO(profile_form.cleaned_data['picture'].read()))
            #picture.save(MEDIA_DIR, "PNG")
            #newPic = Pic(imgfile=request.FILES['picture'])
            newPic=request.FILES['picture']
            newPic.save()
            profile.save()
            return JsonResponse({'response': 'success'})
        else:
            errors =  user_form.errors.copy()
            errors.update(profile_form.errors)
            return JsonResponse({'response': errors})

This is my HTML form:

<form class="uk-form-horizontal uk-margin-small" id="registration_form" 
            method="post" action="{% url 'register' %}" enctype="multipart/form-data">

        <div class="uk-margin">
                <label class="uk-form-label" for="form-horizontal-text">Profile photo</label>
                <div class="uk-form-controls">
                    <div class="uk-width-1-1" uk-form-custom="target: true">
                        <input name="picture" id="id_picture" type="file">
                        <input class="uk-input" type="text" placeholder="Select file" disabled>
                    </div>
                </div>
            </div>

        <div class="uk-margin-top">
                        <button class="uk-button uk-button-primary uk-width-1-1" type="submit" value="Submit" />Register</button>
                    </div>
                </div>
            </div>  

 </form>    

And I'm trying to pass the values of the registration form in an AJAX function:

$(document).on('submit','#registration_form',function(e){
    e.preventDefault();

    //if (password_match){
        $.ajax({
            type:'POST',
            url: "{% url 'register' %}",
            data:{
                username: $('#id_reg_username').val(),
                password1: $('#id_password1').val(),
                password2: $('#id_password2').val(),
                type: $('#id_user_type').val(),
                first_name: $('#id_first_name').val(),
                last_name: $('#id_last_name').val(),
                email: $('#id_email').val(),
                phone: $('#id_phone').val(),
                address: $('#id_address').val(),
                //PICTURE UPLOAD HERE?
                picture: $('#id_picture').val(),
                csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()  

I've tried googling and other solutions on here, I cannot get it to accept and save the image, can someone please help?

Ryan
  • 41
  • 1
  • 4
  • So a) this is not an exact dupe of anything I can find searching, but is a subject well covered in the docs. b) It's unusual to pull values out of `request.POST` in the manner you're doing it to instantiate a form. c) files go in `request.FILES` rather than `request.POST` - but you may have other problems. Basically, review https://docs.djangoproject.com/en/1.11/topics/http/file-uploads/ – Peter DeGlopper Oct 19 '17 at 22:09
  • I'm getting a 'NoneType' object has no attribute save now after changing the picture = request.POST.get('picture') to picture=request.FILES.get('picture') in views.py and tried changing all ImageFields to FileFields.. :( – Ryan Oct 19 '17 at 23:52
  • You're trying to upload files via AJAX, which is a little tricky to do - just passing `$('#id_picture').val()` will not work. See [this question](https://stackoverflow.com/questions/166221/how-can-i-upload-files-asynchronously/8758614#8758614) for how to implement AJAX file upload. – solarissmoke Oct 20 '17 at 02:05

4 Answers4

0

use FILES instead. e.g: photo = request.FILES['photo']

connelblaze
  • 779
  • 1
  • 10
  • 19
0

This can also happen when you provide an input file field and the user submit the form without uploading any photo,. especially if you did not set the input file required attribute.

You will need to write a function in your view to test that a photo was uploaded!

Psalms Kalu
  • 95
  • 1
  • 5
0

You can use the get() method of request.data to extract incoming values like so

    def create(self, request, *args, **kwargs):
    course_owner = User.objects.get(email=request.auth.get('user_id'))
    image = request.data.get('image')
    video = request.data.get('video')
    image_upload = cloudinary.uploader.upload(image)
    video_upload = cloudinary.uploader.upload(video)
    request.data['course_owner'] = course_owner.id
    request.data['image'] = image_upload.get('url')
    request.data['video'] = video_upload.get('url')
    serialized = CourseModelSerializer(data=request.data)
SanRaph
  • 369
  • 3
  • 7
0

The attribute in the form element should be enctype, not encrypt.

<form method="post" enctype="multipart/form-data"></form>
Josef
  • 2,869
  • 2
  • 22
  • 23