【问题标题】:How does Django Rest Framework deserialize foreign key relationships?Django Rest Framework 如何反序列化外键关系?
【发布时间】:2017-10-05 05:16:38
【问题描述】:

在本教程中,序列化器字段和模型字段之间存在这种松散的一对一映射。我可以预期,如果序列化器字段和模型字段都是 CharFields,它将在反序列化为模型实例时保存一串字符:

models.py:

    class Deck(models.Model):
        created = models.DateTimeField(auto_now_add=True)
        name = models.CharField(max_length=100, unique=True, blank=False, null=False)

serializers.py:

class DeckSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Deck
        fields = ('url', 'id', 'created', 'name')
        extra_kwargs = {
            'url': {
                'view_name': 'goals:deck-detail',
            }
        }

但是当我尝试建立关系时,序列化器字段是 ReadOnlyField,据我了解,它本质上是一个 Charfield,但模型字段是 ForeignKeyField,更令人困惑的是,我好像在保存一个当我覆盖 perform_create 时视图中的对象:

models.py:

class Deck(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    name = models.CharField(max_length=100, unique=True, blank=False, null=False)
    user = models.ForeignKey('users.User', related_name='decks', on_delete=models.CASCADE, null=False)

serializers.py:

class DeckSerializer(serializers.HyperlinkedModelSerializer):
    user = serializers.ReadOnlyField(source='user.username')

    class Meta:
        model = Deck
        fields = ('url', 'id', 'created', 'name', 'user')
        extra_kwargs = {
            'url': {
                'view_name': 'goals:deck-detail',
            }
        }

views.py:

class DeckList(generics.ListCreateAPIView):
    serializer_class = DeckSerializer

    def get_queryset(self):
        return Deck.objects.all().filter(user__username=self.request.user)

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

这里发生了什么?在处理关系时,为什么我要通过在视图中覆盖 perform_create、在序列化程序中声明 CharField 以及在模型中声明关系字段来保存对象?

在我的理解中缺少什么,或者引擎盖下到底发生了什么,使得用户字段 (ForeignKey) 可以表示为字符串但保存为对象?

编辑:

如果我在视图中覆盖 serializer.save(user=user) 并且 serializers.py 的用户字段为

user = serializers.CharField(read_only=True) 

我想覆盖 serializers.py 中的 save 方法,如何传递正确的数据以便它知道如何序列化?我是否只是抓取整个用户对象,保存它,然后它会完成其余的工作?视图中的 serializers.save() 覆盖是否与 serializers.py 中的 serializers.save() 相同?

【问题讨论】:

    标签: python django api serialization django-rest-framework


    【解决方案1】:

    不是 100% 确定我已理解您的问题,但如果问题是:

    当 ForeignKey 字段保存在 Django ORM 中时,幕后发生了什么?

    那么答案是:

    关系作为(例如)int 字段保存在数据库中,该字段存储相关对象的主键。

    Django 文档中的 ForeignKey field reference 部分解释了这部分 ORM 的工作原理,"Database Representation" 小节可能涉及您感兴趣的特定位。

    例如,对于 UserDeck 模型中的相关字段的情况,基础表可能如下所示(假设 postgresql):

    myapp_deck
        id         int
        created    timestamp
        name       varChar
        user_id    int
    

    Deck -> User 关系由 DB 映射,该 DB 将相关 User 对象的 pk 存储在 myapp_deck 表的 user_id 字段中。

    因此,所有 Django(以及因此,DRF)需要做的更改 Deck 模型中的 User 是将 myapp_deck 表中的 user_id 更改为不同 User 的 PK对象。

    希望这会有所帮助,如果我错过了您的问题的重点,请告诉我。


    已编辑以添加自定义 .create() 方法的示例

    如果您想重写序列化程序中的自定义“保存”方法,则要重写的方法是 create()update()(请参阅 DRF 文档中的 Serializer "Saving instances" 部分)。

    这方面的一个例子可能是:

    class DeckSerializer(serializers.HyperlinkedModelSerializer):
        user = serializers.ReadOnlyField(source='user.username')
    
        ... Rest of your serializer code ...
    
        def create(self, validated_data, **kwargs):
            user_obj = User.objects.get(pk=validated_data["user"])
            deck = Deck.objects.create(
                name=validated_data["name"],
                user=user_obj,
            )
            return deck
    

    注意:这假定相关User 对象的pk 已通过序列化程序,验证OK,并且在validated_data 字典中可用。

    【讨论】:

    • 这对我有更多帮助,我不知道它在 Django 中存储为 int 字段。我没有得到关于 DRF 中的序列化程序如何为外键做的高级概述。使用像 'name': 'bob' 这样的普通传入字段以 json 形式出现,我可以理解视图传递给该字段的序列化程序,并且该字段将具有类似名称:serializers.CharField(),它需要一个字符串和作为字符串反序列化为模型实例,因为模型字段也将具有 name = models.CharField()。
    • 但是有了外键,视图在perform_create中传入一个对象,序列化器取一个CharField,模型存储一个ForeignKey字段,应该是一个int字段?我想我想问的是序列化程序如何进行这种从对象到字符串到 int 和返回的神奇转换/映射?或者它只是被接受的魔法发生在引擎盖下?如果我想覆盖序列化程序中的保存方法怎么办?如果我只是从用户对象中保存一个字符串属性,它怎么知道选择正确的用户对象?我不认为我可以在序列化程序中序列化整个对象
    • 我已经编辑了我的答案,以展示自定义 create() 方法的示例。希望这能让你看到“幕后”发生了什么。你的代码在做你想做的事吗?
    • 代码执行我想要的,你的解释是有道理的,但我现在不明白的一件事是“用户”的序列化器字段验证。如果 Django 将外键关系保存为 int_field,字段验证不应该因为使用 CharField(ReadOnlyField)而失败吗?或者它正在做的是在“user_id”中保存一个 int 字段,但将“user”保留为 CharField?
    • 没关系,我想现在一切都开始了。 ReadOnlyField 经过验证,仅用于序列化而不是反序列化,如果我想让它可写,我会像你说的那样编写自定义 create()。
    猜你喜欢
    • 1970-01-01
    • 2015-12-13
    • 2016-12-18
    • 2018-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-19
    相关资源
    最近更新 更多