嵌套序列化程序未显示

IKA*_*min 9 python django django-rest-framework

我正在尝试更新我的 drf 项目中的序列化程序,以便以嵌套方式显示。有问题的两个模型是图像和画廊,图像与画廊相关。

我尝试遵循https://www.django-rest-framework.org/api-guide/relations/#nested-relationships,但我不完全确定为什么它不起作用。

下面是models.py

class Gallery(models.Model):
    title = models.CharField(max_length=30)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    created_on = models.DateTimeField(auto_now_add=True, blank=True)
    modified_on = models.DateTimeField(auto_now=True, blank=True)

    def __str__(self):
        return self.title


class Image(models.Model):
    gallery_id = models.ForeignKey(Gallery, on_delete=models.CASCADE)
    img = models.ImageField(upload_to='images/')
    created_on = models.DateTimeField(auto_now_add=True, blank=True)
Run Code Online (Sandbox Code Playgroud)

序列化程序.py

class ImageSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Image
        fields = ["gallery_id", "img", "created_on", "id"]


class GallerySerializer(serializers.HyperlinkedModelSerializer):
    image = ImageSerializer(many=True, read_only=True)

    def validate(self, data):
        # Check if user id is equal object id before creation or if SuperUser
        request = self.context.get("request")
        if request.user.id != data["author"].id and request.user.is_superuser is not True:
            raise ValidationError("Unauthorized User Post")
        return data

    class Meta:
        model = Gallery
        fields = ["title", "author", "created_on", "modified_on", "image", "id"]
Run Code Online (Sandbox Code Playgroud)

期待的结果是

[
  {
    "title": "test_1",
    "author": "http://127.0.0.1:8000/api/users/2/",
    "created_on": "2019-08-19T09:13:45.107658Z",
    "modified_on": "2019-08-19T09:13:45.107731Z",
    "image": [
      {
        "gallery_id": "http://127.0.0.1:8000/api/galleries/24/",
        "img": "http://127.0.0.1:8000/media/images/angga-tantama-background-art-minimalism.jpg",
        "created_on": "2019-08-20T09:17:31.790901Z",
        "id": 6
      },
      {
        "gallery_id": "http://127.0.0.1:8000/api/galleries/24/",
        "img": "http://127.0.0.1:8000/media/images/art-vector-background-illustration-minimalism-angga-tantam-2.jpg",
        "created_on": "2019-08-20T09:31:40.505035Z",
        "id": 7
      }
    ]
    "id": 24
  },
  {
    "title": "test_2",
    "author": "http://127.0.0.1:8000/api/users/2/",
    "created_on": "2019-08-20T09:42:09.448974Z",
    "modified_on": "2019-08-20T09:42:09.449042Z",
    "id": 27
  }
]
Run Code Online (Sandbox Code Playgroud)

Vai*_*hal 21

image = ImageSerializer(many=True, read_only=True, source='image_set')
Run Code Online (Sandbox Code Playgroud)

或者

image_set = ImageSerializer(many=True, read_only=True)  # use image_set in fields list too.
Run Code Online (Sandbox Code Playgroud)

假设您有一个Gallery与此类似的对象:

g = Gallery.objects.get(pk=1)
Run Code Online (Sandbox Code Playgroud)

现在与给定Galley对象相关的所有图像的查询集将是:

Image.objects.filter(gallery_id=g)  # g is the gallery object
Run Code Online (Sandbox Code Playgroud)

在 Django 中,我们可以将其简化为:

g.image_set  # same as Image.objects.filter(gallery_id=g)
Run Code Online (Sandbox Code Playgroud)

现在的问题是这种魔法image_set从何而来。在 Django ORM 中,如果你可以在模型的 ForeignKey 中使用 related_name 来查询相关对象,如下所示:

gallery_id = models.ForeignKey(Gallery, on_delete=models.CASCADE, related_name='something')
# if you do this in your models.py then to get all images of a gallery you will now do:
g.something
Run Code Online (Sandbox Code Playgroud)

但是,因为你没有指定related_nameForeginKey默认为model name all lowercase + _set,所以在这种情况下:image_set
这是docs的链接。

如果您从模型中为同一模型指定两个 ForeignKey,django 也会要求您添加related_name(在进行迁移时),因为它只能默认一个字段的相关名称。

这称为反向关系。forward_relationship当你做这样的事情时会:

img = Image.objects.get(pk=1)
img.gallery_id  # get the gallery object related to the image
Run Code Online (Sandbox Code Playgroud)

这很简单,就像gallery_id模型中的字段一样。


另外请注意不要用尾随命名您的 ForeignKey 字段_id,它具有误导性。img.gallery_id不是 Gallery 的 id,它是整个 Gallery 对象。Django 在数据库中保存带有尾随 id 的相关字段,因此在您的情况下,数据库中的列名将是gallery_id_id(很可能,可能已在较新版本中更改)。
查询您的字段名称是否为gallery_id

img.gallery_id  # the gallery object
img.gallery_id.id  # actual id of the gallery
img.gallery_id_id  # actual id of the gallery
Run Code Online (Sandbox Code Playgroud)

但是,如果您仅将字段命名为gallery

img.gallery  # the gallery object
img.gallery.id  # actual id of the gallery
img.gallery_id  # actual id of the gallery
Run Code Online (Sandbox Code Playgroud)

这要清楚得多。


所以现在您知道为什么image_set是模型类的属性了。并且 drf 在字段名称中查找模型属性,因此您可以将字段名称与 attribute( image_set)相同,或者使用source序列化程序的参数指定属性。


小智 5

为了使事情正常工作,您需要使用source 关键字指定序列化程序应在何处获取数据

就你而言,这应该可以解决问题。

class GallerySerializer(serializers.HyperlinkedModelSerializer):
    image = ImageSerializer(source="image_set", many=True, read_only=True)

    def validate(self, data):
        # Check if user id is equal object id before creation or if SuperUser
        request = self.context.get("request")
        if request.user.id != data["author"].id and request.user.is_superuser is not True:
            raise ValidationError("Unauthorized User Post")
        return data

    class Meta:
        model = Gallery
        fields = ["title", "author", "created_on", "modified_on", "image", "id"]
Run Code Online (Sandbox Code Playgroud)

如果您在 FK 声明中使用“related_name”,则应使用此反向相关名称。

正如您所期望的许多Image例子,我强烈建议您命名您的字段images而不是image

我还建议您查看权限,而不是使用将逻辑放入序列化器中。