我的 django 序列化程序中数据丢失(文件)

问题描述 投票:0回答:1

我有一个问题,我的序列化器中缺少一些数据(在验证和创建函数中)。 我的模特:

class PostsImage(models.Model):
    image = models.ImageField(upload_to="posts/images")
    post = models.ForeignKey(
        "socialmedia.Post", on_delete=models.CASCADE, related_name="posts_images"
    )

    class Meta:
        verbose_name = "PostsImage"
        verbose_name_plural = "PostsImages"

    def __str__(self):
        return self.image


class PostsAttachedFile(models.Model):
    attached = models.FileField(upload_to="posts/attached")
    post = models.ForeignKey(
        "socialmedia.Post",
        on_delete=models.CASCADE,
        related_name="posts_attached_files",
    )

    class Meta:
        verbose_name = "PostsAttachedFile"
        verbose_name_plural = "PostsAttachedFiles"

    def __str__(self):
        return self.attached


class Post(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    business_sector = models.CharField(max_length=50, choices=BUSINESS_SECTOR_CHOICES)
    author = models.ForeignKey(
        "users.User", on_delete=models.CASCADE, related_name="posts"
    )
    created_at = models.DateTimeField(auto_now_add=True)
    valid_until = models.DateTimeField()
    location_type = models.CharField(max_length=20, choices=LOCATION_CHOICES)
    location = models.CharField(max_length=255)
    location_radius = models.PositiveIntegerField(null=True, blank=True)

    class Meta:
        verbose_name = "Post"
        verbose_name_plural = "Posts"

    def __str__(self):
        return self.title

我的序列化器:

class PostSerializer(serializers.ModelSerializer):
    images = PostsImageSerializer(many=True, source="posts_images", required=False)
    attached_files = PostsAttachedFileSerializer(
        many=True, source="posts_attached_files", required=False
    )

    class Meta:
        model = Post
        fields = "__all__"

    def validate(self, attrs):
        print(attrs)
        author = attrs.get("author")
        location_type = attrs.get("location_type")
        location_radius = attrs.get("location_radius")
        valid_until = attrs.get("valid_until")
        user = self.context["request"].user

        if author and author != user:
            raise serializers.ValidationError(
                {"author": "You can't create post for another user"}
            )

        if location_type == "local" and not location_radius:
            raise serializers.ValidationError(
                {
                    "location_radius": "You must provide location radius if location type is local",
                }
            )

        if valid_until and valid_until.timestamp() < datetime.now().timestamp():
            raise serializers.ValidationError(
                {"valid_until": "Valid until must be in the future"}
            )
        print("#### validation ok")
        return attrs

    def create(self, validated_data):
        print(validated_data)
        images = None
        attached_files = None
        if "posts_images" in validated_data.keys():
            images = validated_data.pop("posts_images")
        if "posts_attached_files" in validated_data.keys():
            attached_files = validated_data.pop("posts_attached_files")
        post = Post.objects.create(**validated_data)

        images_from_files = self.context["request"].FILES.getlist("posts_images")
        print(images_from_files)
        print(self.context["request"].data)
        if images_from_files:
            for image in images_from_files:
                PostsImage.objects.create(post=post, image=image)
        if attached_files:
            for attached_file in attached_files:
                PostsAttachedFile.objects.create(post=post, attached_file=attached_file)
        return post

    def update(self, instance, validated_data):
        images = None
        attached_files = None
        if "posts_images" in validated_data.keys():
            images = validated_data.pop("posts_images")
        if "posts_attached_files" in validated_data.keys():
            attached_files = validated_data.pop("posts_attached_files")
        instance = super().update(instance, validated_data)
        if images and type(images) == list:
            instance.posts_images.all().delete()
            for image in images:
                PostsImage.objects.create(post=instance, **image)
        if attached_files and type(attached_files) == list:
            instance.posts_attached_files.all().delete()
            for attached_file in attached_files:
                PostsAttachedFile.objects.create(post=instance, **attached_file)
        return instance

我的观点:

class PostModelViewSet(viewsets.ModelViewSet):
    def get_queryset(self):
        return Post.objects.filter(author=self.request.user)

    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticated]

最后是我的测试代码:

class PostModelViewSetTestCase(TestCase):
    def setUp(self):
        self.user = User.objects.create(
            email="[email protected]", password="testpassword"
        )
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)

    def test_create_post(self):
        tmp_image = SimpleUploadedFile(
            "file.jpg", b"file_content", content_type="image/jpg"
        )
        img_path = f"{CURRENT_DIR}/socialmedia/tests/default.png"
        image = SimpleUploadedFile(
            name="test_image.jpg",
            content=open(img_path, "rb").read(),
            content_type="image/jpeg",
        )
        data = {
            "author": self.user.id,
            "location_type": "local",
            "location": "nord",
            "location_radius": 5,
            "valid_until": datetime.now() + timedelta(days=1),
            "content": "content",
            "title": "title",
            "business_sector": "IT",
            "images": [
                {"image": image},
                {"image": image},
                {"image": image},
                {"image": image},
            ],
        }

        response = self.client.post("/socialmedia/posts/", data, format="multipart")
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Post.objects.count(), 1)
        print(PostsImage.objects.all())
        self.assertEqual(PostsImage.objects.count(), 4)
        post = Post.objects.first()
        self.assertEqual(post.author, self.user)
        self.assertEqual(post.posts_images.count(), 4)

在我的测试中,我想创建一个包含多个图像的帖子。如果我删除图像,它工作正常,但如果我保留它们,则会创建 post 对象,但不会创建 PostsImage。因此测试失败,因为没有创建 4 个 PostsImage 对象。

不会创建 PostsImage 对象,因为如果我在序列化程序中进行打印(

print(attrs)
在验证中)

我明白了:

.OrderedDict([('title', 'title'), ('content', 'content'), ('business_sector', 'IT'), ('valid_until', datetime.datetime(2023, 9, 18, 9, 34, 22, 220328, tzinfo=zoneinfo. ZoneInfo(key='Europe/Paris'))), ('location_type', 'local'), ('location', 'nord'), ('location_radius', 5), ('author', <User: >))])

缺少“图片”或“posts_images”。

如果在我的测试中我将

response = self.client.post("/socialmedia/posts/", data, format="multipart")
更改为
response = self.client.post("/socialmedia/posts/", data, format="json")
,我可以在序列化器中看到 posts_images,但它不起作用,因为它们是文件。

所以我尝试使用

images_from_files = self.context["request"].FILES.getlist("posts_images")
但是self.context[“request”].FILES是空的

django django-rest-framework django-testing
1个回答
0
投票

您必须在设置方法中创建任意数量的假图像,每次运行测试时,您的图像都会自动创建。

from django.core.files.uploadedfile import SimpleUploadedFile

        image = BytesIO()
        Image.new('RGB', (100, 100)).save(image, 'JPEG')
        image.seek(0)
        self.image_1 = SimpleUploadedFile('image.JPG', image.getvalue())

这就是创建单个图像的方法。您可以循环执行此操作并创建多个图像。 确保所有数据都应在 setUp 方法中设置,这是编写测试用例的最佳实践。

© www.soinside.com 2019 - 2024. All rights reserved.