使用ImageField进行Django ModelForm的单元测试,测试显示表单无效。

huangapple go评论101阅读模式
英文:

Unit testing Django ModelForm with ImageField, tests show invalid form

问题

尝试测试Django ModelForm,但测试用例失败。尽管传递了所需的数据,但仍然显示“image字段是必需的”,并且测试显示表单无效。

models.py

  1. # models.py
  2. import sys
  3. from PIL import Image
  4. from io import BytesIO
  5. from django.contrib.auth.models import User
  6. from django.core.files.uploadedfile import InMemoryUploadedFile
  7. from django.db import models
  8. from django.utils.translation import ugettext as _
  9. class ProfileImage(models.Model):
  10. user = models.OneToOneField(User, verbose_name=_("user"), on_delete=models.CASCADE)
  11. image = models.ImageField(upload_to="profile")
  12. created_at = models.DateTimeField(auto_now_add=True)
  13. modified_at = models.DateTimeField(auto_now=True)
  14. def __str__(self):
  15. return self.user.username
  16. def save(self, *args, **kwargs):
  17. if not self.id:
  18. uploaded_image = Image.open(self.image)
  19. rgb_image = uploaded_image.convert("RGB")
  20. output = BytesIO()
  21. image_resized = rgb_image.resize((300, 300))
  22. image_resized.save(output, format="JPEG", quality=100)
  23. output.seek(0)
  24. self.image = InMemoryUploadedFile(
  25. output, "ImageField", "{}.jpg".format(self.image.name.split('.')[0]),
  26. "image/jpeg", sys.getsizeof(output), None
  27. )
  28. super().save(*args, **kwargs)

forms.py

  1. # forms.py
  2. from PIL import Image
  3. from django import forms
  4. from django.forms.utils import ErrorList
  5. from django.utils.translation import ugettext as _
  6. from .models import (
  7. ProfileImage,
  8. )
  9. class ProfileImageForm(forms.ModelForm):
  10. class Meta:
  11. model = ProfileImage
  12. fields = ["image"]
  13. def clean(self):
  14. image = self.cleaned_data.get("image")
  15. if image:
  16. image = Image.open(image)
  17. width, height = image.size
  18. image_format = image.format
  19. if image_format not in ("PNG", "JPEG", "MPO"):
  20. msg = _("Unsupported image type. Please upload a *png or *jpg image.")
  21. self._errors["image"] = ErrorList([msg])
  22. del image
  23. if width < 300 or height < 300:
  24. msg = _("Image is too small! Please upload an image of size 300px x 300px or larger.")
  25. self._errors["image"] = ErrorList([msg])
  26. del image
  27. return self.cleaned_data

test_forms.py

  1. # test_forms.py
  2. import os
  3. import tempfile
  4. from mixer.backend.django import mixer
  5. import numpy
  6. from PIL import Image
  7. from django.conf import settings
  8. from django.core.files.uploadedfile import SimpleUploadedFile
  9. from django.test import TestCase
  10. from base.models import (
  11. ProfileImage
  12. )
  13. from base.forms import (
  14. ProfileImageForm,
  15. )
  16. # utils functions
  17. def get_test_image(height=100, width=100):
  18. """
  19. Generate an image for test purposes
  20. Args:
  21. height(int): image height
  22. width(int): image width
  23. Returns:
  24. image(str): image path
  25. """
  26. image = tempfile.NamedTemporaryFile(suffix=".png", dir=settings.MEDIA_ROOT, delete=True).name
  27. imarray = numpy.random.rand(height, width, 3) * 255
  28. im = Image.fromarray(imarray.astype("uint8")).convert("RGBA")
  29. im.save(image)
  30. return image
  31. def delete_test_image(image):
  32. """
  33. Delete an image generated for test purposes
  34. Args:
  35. image(str): image path
  36. """
  37. if os.path.exists(image):
  38. os.remove(image)
  39. class TestFormData(TestCase):
  40. def setUp(self):
  41. self.standard_profile_test_image = get_test_image(300, 300)
  42. # 1st way, Fails
  43. def test_image_form_valid_1(self):
  44. image_path = self.standard_profile_test_image
  45. profile_img_instance = mixer.blend(ProfileImage, image=image_path)
  46. image = profile_img_instance.image
  47. form_data = {"image": image}
  48. form = ProfileImageForm(form_data)
  49. self.assertTrue(form.is_valid())
  50. delete_test_image(profile_img_instance.image.path)
  51. # 2nd way, Fails
  52. def test_image_form_valid_2(self):
  53. image_path = self.standard_profile_test_image
  54. with open(image_path, "rb") as f:
  55. file_data = f.read()
  56. file_name = f.name
  57. form_data = {"image": SimpleUploadedFile(file_name, file_data)}
  58. form = ProfileImageForm(data=form_data)
  59. self.assertTrue(form.is_valid())
  60. def tearDown(self):
  61. delete_test_image(self.standard_profile_test_image)

当我运行这两个测试时,它们都会抛出错误:表单无效

表单显示“image字段是必需的”,尽管我传递了所需的数据。

英文:

Trying to test a Django ModelForm, but the test cases are failing. Passing the required data but, it still shows "The image field is required" and tests show invalid form.

models.py

  1. # models.py
  2. import sys
  3. from PIL import Image
  4. from io import BytesIO
  5. from django.contrib.auth.models import User
  6. from django.core.files.uploadedfile import InMemoryUploadedFile
  7. from django.db import models
  8. from django.utils.translation import ugettext as _
  9. class ProfileImage(models.Model):
  10. user = models.OneToOneField(User, verbose_name=_(&quot;user&quot;), on_delete=models.CASCADE)
  11. image = models.ImageField(upload_to=&quot;profile&quot;)
  12. created_at = models.DateTimeField(auto_now_add=True)
  13. modified_at = models.DateTimeField(auto_now=True)
  14. def __str__(self):
  15. return self.user.username
  16. def save(self, *args, **kwargs):
  17. if not self.id:
  18. uploaded_image = Image.open(self.image)
  19. rgb_image = uploaded_image.convert(&quot;RGB&quot;)
  20. output = BytesIO()
  21. image_resized = rgb_image.resize((300, 300))
  22. image_resized.save(output, format=&quot;JPEG&quot;, quality=100)
  23. output.seek(0)
  24. self.image = InMemoryUploadedFile(
  25. output, &quot;ImageField&quot;, &quot;{}.jpg&quot;.format(self.image.name.split(&#39;.&#39;)[0]),
  26. &quot;image/jpeg&quot;, sys.getsizeof(output), None
  27. )
  28. super().save(*args, **kwargs)

forms.py

  1. # forms.py
  2. from PIL import Image
  3. from django import forms
  4. from django.forms.utils import ErrorList
  5. from django.utils.translation import ugettext as _
  6. from .models import (
  7. ProfileImage,
  8. )
  9. class ProfileImageForm(forms.ModelForm):
  10. class Meta:
  11. model = ProfileImage
  12. fields = [&quot;image&quot;]
  13. def clean(self):
  14. image = self.cleaned_data.get(&quot;image&quot;)
  15. if image:
  16. image = Image.open(image)
  17. width, height = image.size
  18. image_format = image.format
  19. if image_format not in (&quot;PNG&quot;, &quot;JPEG&quot;, &quot;MPO&quot;):
  20. msg = _(&quot;Unsupported image type. Please upload a *png or *jpg image.&quot;)
  21. self._errors[&quot;image&quot;] = ErrorList([msg])
  22. del image
  23. if width &lt; 300 or height &lt; 300:
  24. msg = _(&quot;Image is too small! Please upload image of size 300px x 300px or More.&quot;)
  25. self._errors[&quot;image&quot;] = ErrorList([msg])
  26. del image
  27. return self.cleaned_data

test_forms.py

  1. # test_forms.py
  2. import os
  3. import tempfile
  4. from mixer.backend.django import mixer
  5. import numpy
  6. from PIL import Image
  7. from django.conf import settings
  8. from django.core.files.uploadedfile import SimpleUploadedFile
  9. from django.test import TestCase
  10. from base.models import (
  11. ProfileImage
  12. )
  13. from base.forms import (
  14. ProfileImageForm,
  15. )
  16. # utils functions
  17. def get_test_image(height=100, width=100):
  18. &quot;&quot;&quot;
  19. Generate image for test purpose
  20. Args:
  21. height(int): image height
  22. width(int): image width
  23. Returns:
  24. image(str): image path
  25. &quot;&quot;&quot;
  26. image = tempfile.NamedTemporaryFile(suffix=&quot;.png&quot;, dir=settings.MEDIA_ROOT, delete=True).name
  27. imarray = numpy.random.rand(height, width, 3) * 255
  28. im = Image.fromarray(imarray.astype(&quot;uint8&quot;)).convert(&quot;RGBA&quot;)
  29. im.save(image)
  30. return image
  31. def delete_test_image(image):
  32. &quot;&quot;&quot;
  33. Delete image generated for test purpose
  34. Args:
  35. image(str): image path
  36. Returns:
  37. &quot;&quot;&quot;
  38. if os.path.exists(image):
  39. os.remove(image)
  40. class TestFormData(TestCase):
  41. def setUp(self):
  42. self.standard_profile_test_image = get_test_image(300, 300)
  43. # 1st way, Fails
  44. def test_image_form_valid_1(self):
  45. image_path = self.standard_profile_test_image
  46. profile_img_instance = mixer.blend(ProfileImage, image=image_path)
  47. image = profile_img_instance.image
  48. form_data = {&quot;image&quot;: image}
  49. form = ProfileImageForm(form_data)
  50. self.assertTrue(form.is_valid())
  51. delete_test_image(profile_img_instance.image.path)
  52. # 2nd way, Fails
  53. def test_image_form_valid_2(self):
  54. image_path = self.standard_profile_test_image
  55. with open(image_path, &quot;rb&quot;) as f:
  56. file_data = f.read()
  57. file_name = f.name
  58. form_data = {&quot;image&quot;: SimpleUploadedFile(file_name, file_data)}
  59. form = ProfileImageForm(data=form_data)
  60. self.assertTrue(form.is_valid())
  61. def tearDown(self):
  62. delete_test_image(self.standard_profile_test_image)

When I ran these two tests both of them threw errors: form is invalid

The form shows the image field is required even though I passed the required data.

  1. &gt;&gt;&gt; form
  2. &gt;&gt;&gt; &lt;ProfileImageForm bound=True, valid=False, fields=(image)&gt;
  3. &gt;&gt;&gt; form.errors
  4. &gt;&gt;&gt; {&#39;image&#39;: [&#39;This field is required.&#39;]}
  5. &gt;&gt;&gt; form.non_field_errors()
  6. &gt;&gt;&gt; []

答案1

得分: 3

POST数据(非文件字段)应该是使用带有文件字段的表单时的第一个参数,文件数据(文件字段)应该是第二个参数。

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

POST data (non-file fields) should be the first argument and FILE data (file fields) should be the second when using forms with file fields

  1. form = ProfileImageForm({}, {&quot;image&quot;: image})

huangapple
  • 本文由 发表于 2020年1月6日 15:22:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/59608148.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定