【问题标题】:How to update nested serializers with file field如何使用文件字段更新嵌套序列化程序
【发布时间】:2019-01-21 14:56:03
【问题描述】:

我有 2 个模型:ProfileImage。与具有模型的图像相关的配置文件字段“徽标”。ForeignKey() 构造。我想使用更新请求(带有 JSON 有效负载的补丁)更新我的个人资料记录。

我该怎么做?

我已尝试发送此 JSON

{
    "name": "TestName",
    "company": "myCompany",
    "phone": "33222111",
    "website": "site.com"
}

没关系,记录已更新。但!在图像模型中,我有 models.ImageField()。我应该如何通过另一个序列化程序处理这个字段?

然后我尝试发送这个 JSON(数据库中现有图像记录的 122 个 id)

REQUEST:
{
   "logo": 122
}

ANSWER:
{
    "logo": {
        "non_field_errors": [
            "Invalid data. Expected a dictionary, but got int."
        ]
    }
}

好的,所以,我认为我应该发送存在记录的对象

REQUEST:
{
   "logo": {
        "id": 122,
        "uuid": "bf9ba033-208f-47e0-86e5-93c44e05a616",
        "created": "2018-12-20T12:54:57.178910Z",
        "original_name": "hello.png",
        "filetype": "png",
        "file": "http://localhost/upload/img/0a9lg1apnebb.png",
        "owner": 1
   }
}

ANSWER:
{
    "logo": {
        "file": [
            "The submitted data was not a file. Check the encoding type on the form."
        ]
    }
}

这是我的两个模型和序列化程序

class Image(models.Model):
    id = models.AutoField(primary_key=True)
    uuid = models.UUIDField(primary_key=False, default=uuid.uuid4, editable=False)
    created = models.DateTimeField(auto_now_add = True)
    original_name = models.CharField(max_length = 256, default=None)
    filetype = models.CharField(max_length = 10, default=None)
    file = models.ImageField(upload_to=update_img_filename,
                         default="static/noimg.jpg")
    owner = models.ForeignKey(User, related_name="images",
                          on_delete=models.CASCADE, null=True)


class Profile(models.Model):
    user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
    name = models.CharField(max_length=256, blank=True, null=True)
    company = models.CharField(max_length=256, blank=True, null=True)
    phone = models.CharField(max_length=256, blank=True, null=True)
    website = models.CharField(max_length=256, blank=True, null=True)
    logo = models.ForeignKey(Image, related_name="profile_logo",
                                on_delete=models.SET_NULL, null=True,
                                blank=True, default=None)
    is_admin = models.BooleanField(default=False)
    owner = models.ForeignKey(User, related_name="profiles",
                          on_delete=models.CASCADE, null=True)

class ProfileSerializer(serializers.ModelSerializer):
    logo = ImageSerializer()

    class Meta:
        model = Profile
        fields = '__all__'
        extra_kwargs = {
            'owner': {'read_only': True},
            'user': {'read_only': True},
            'is_admin': {'read_only': True},
        }

    def create(self, validated_data):
        return Profile.objects.create(**validated_data)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = '__all__'
        extra_kwargs = {
            'owner': {'read_only': True}
        }

    def validate(self, data):
        extension = str(data['file']).split(".")[-1].lower()
        original_name = str(data['file'])

        if 'original_name' not in data:
            data['original_name'] = original_name
        if 'filetype' not in data:
            data['filetype'] = extension

        return data

    def create(self, validated_data):
        return Image.objects.create(**validated_data)

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    在数据库中,模型Profile 将具有logo_id 与模型Image 的映射。 如果您想与现有徽标建立关系,请尝试json

    {
        "name": "TestName",
        "company": "myCompany",
        "phone": "33222111",
        "website": "site.com",
        "logo: 122
    }
    

    序列化器:

     class ProfileSerializer(serializers.ModelSerializer):
        logo = ImageSerializer()
    
        class Meta:
            model = Profile
            fields = '__all__'
            extra_kwargs = {
                'owner': {'read_only': True},
                'user': {'read_only': True},
                'is_admin': {'read_only': True},
            }
        def create(self, validated_data):
            logo_id = validated_data.pop('logo')
            profile = Profile.objects.create(**validated_data)
            logo_instance = Image.objects.get(pk=logo_id)
            profile.logo = logo_instance
            profile.save()
    
        def update(self, instance, validated_data):
            nested_serializer = self.fields['logo']
            nested_instance = instance.profile
            nested_data = validated_data.pop('logo')
            nested_serializer.update(nested_instance, nested_data)
            return super(ProfileSerializer, self).update(instance, validated_data)
    

    【讨论】:

    • 您能否提供更多有关“logo_id 与模型图像的映射”的详细信息?
    • 你有 logo = models.ForeignKey(Image, related_name="profile_logo"...) 并且 django 将在数据库 logo_id 中的表 Profile 中自动创建。是2型号的关系。我知道您的问题,您希望 JSON 可以创建或更新 Profile 中存在的 Image 中的一个数据。而你存在的Imageid=122。然后你只需要在 json 中添加logo_id: 122,django 就会知道你想要创建或更新 Profile 与现有的Imageid =122
    • 不幸的是,这在我的情况下不起作用 :( 带有负载的请求:{ "name": "newName", "logo_id": 122 } 返回相同的记录,没有徽标字段更改(并且没有任何错误)。只有字段“名称”被更改.
    • 对不起,改变 json 有 'logo:122' 就足够了。你的问题是logo = ImageSerializer()。如果你有这个,django 会知道logo 是一个具有 type = dict 的字段。您必须在创建和更新过程中控制此logo 手册。请尝试我的更新答案
    • 谢谢!我试试看!
    【解决方案2】:

    如果您只需要从现有图像中设置图像,您可以这样做:

    class ProfileSerializer(serializers.ModelSerializer):
        logo = ImageSerializer()
    
        class Meta:
            model = Profile
            fields = '__all__'
            extra_kwargs = {
                'owner': {'read_only': True},
                'user': {'read_only': True},
                'is_admin': {'read_only': True},
            }
    
    class ImageSerializer(serializers.ModelSerializer):
    
        def to_internal_value(self, data):
            return Image.objects.get(pk=data)
    
        class Meta:
            model = Image
            fields = '__all__'
            extra_kwargs = {
                'owner': {'read_only': True}
            }
    ...
    

    然后就可以发送json了:

    {
        "name": "TestName",
        "company": "myCompany",
        "phone": "33222111",
        "website": "site.com"
        "logo": 122
    }
    

    并且图像将被设置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-12
      • 1970-01-01
      • 2019-02-19
      • 2019-11-14
      • 2016-02-09
      • 2018-11-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多