Модульное тестирование Django ModelForm с ImageField, тесты показывают недопустимую форму - PullRequest
1 голос
/ 06 января 2020

Пытается протестировать Django ModelForm, но тесты не выполняются. Передавая необходимые данные, но все равно отображается «Поле изображения обязательно», а тесты показывают недопустимую форму.

models.py

# models.py

import sys

from PIL import Image
from io import BytesIO

from django.contrib.auth.models import User
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.db import models
from django.utils.translation import ugettext as _


class ProfileImage(models.Model):
    user = models.OneToOneField(User, verbose_name=_("user"), on_delete=models.CASCADE)
    image = models.ImageField(upload_to="profile")
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.user.username

    def save(self, *args, **kwargs):
        if not self.id:
            uploaded_image = Image.open(self.image)
            rgb_image = uploaded_image.convert("RGB")
            output = BytesIO()
            image_resized = rgb_image.resize((300, 300))
            image_resized.save(output, format="JPEG", quality=100)
            output.seek(0)
            self.image = InMemoryUploadedFile(
                output, "ImageField", "{}.jpg".format(self.image.name.split('.')[0]),
                "image/jpeg", sys.getsizeof(output), None
            )
        super().save(*args, **kwargs)

forms.py

# forms.py

from PIL import Image

from django import forms
from django.forms.utils import ErrorList
from django.utils.translation import ugettext as _

from .models import (
    ProfileImage,
)


class ProfileImageForm(forms.ModelForm):
    class Meta:
        model = ProfileImage
        fields = ["image"]

    def clean(self):
        image = self.cleaned_data.get("image")
        if image:
            image = Image.open(image)
            width, height = image.size
            image_format = image.format
            if image_format not in ("PNG", "JPEG", "MPO"):
                msg = _("Unsupported image type. Please upload a *png or *jpg image.")
                self._errors["image"] = ErrorList([msg])
                del image

            if width < 300 or height < 300:
                msg = _("Image is too small! Please upload image of size 300px x 300px or More.")
                self._errors["image"] = ErrorList([msg])
                del image
        return self.cleaned_data

test_forms.py

# test_forms.py

import os
import tempfile

from mixer.backend.django import mixer
import numpy
from PIL import Image

from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase

from base.models import (
    ProfileImage
)
from base.forms import (
    ProfileImageForm,
)


# utils functions
def get_test_image(height=100, width=100):
    """
    Generate image for test purpose

    Args:
        height(int): image height
        width(int): image width

    Returns:
        image(str): image path
    """

    image = tempfile.NamedTemporaryFile(suffix=".png", dir=settings.MEDIA_ROOT, delete=True).name
    imarray = numpy.random.rand(height, width, 3) * 255
    im = Image.fromarray(imarray.astype("uint8")).convert("RGBA")
    im.save(image)
    return image


def delete_test_image(image):
    """
    Delete image generated for test purpose

    Args:
        image(str): image path

    Returns:

    """

    if os.path.exists(image):
        os.remove(image)


class TestFormData(TestCase):
    def setUp(self):
        self.standard_profile_test_image = get_test_image(300, 300)

    # 1st way, Fails
    def test_image_form_valid_1(self):
        image_path = self.standard_profile_test_image
        profile_img_instance = mixer.blend(ProfileImage, image=image_path)
        image = profile_img_instance.image
        form_data = {"image": image}
        form = ProfileImageForm(form_data)
        self.assertTrue(form.is_valid())
        delete_test_image(profile_img_instance.image.path)

    # 2nd way, Fails
    def test_image_form_valid_2(self):
        image_path = self.standard_profile_test_image
        with open(image_path, "rb") as f:
            file_data = f.read()
            file_name = f.name
        form_data = {"image": SimpleUploadedFile(file_name, file_data)}
        form = ProfileImageForm(data=form_data)
        self.assertTrue(form.is_valid())

    def tearDown(self):
        delete_test_image(self.standard_profile_test_image)

Когда я запустил эти два теста, оба из них выдавали ошибки: form is invalid

Форма показывает the image field is required даже при том, что я передал необходимые данные.

>>> form
>>> <ProfileImageForm bound=True, valid=False, fields=(image)>
>>> form.errors
>>> {'image': ['This field is required.']}
>>> form.non_field_errors()
>>> []

1 Ответ

2 голосов
/ 06 января 2020

Данные POST (не файловые поля) должны быть первым аргументом, а данные FILE (файловые поля) должны быть вторыми при использовании форм с полями файла

form = ProfileImageForm({}, {"image": image})
...