Django Rest Framework如何反序化外键关系?

时间:2017-05-07 00:37:05

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

在教程中,序列化器字段和模型字段之间存在这种松散的一对一映射。我可以预期,如果序列化程序字段和模型字段都是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方法,如何传递正确的数据以便知道如何序列化?我只是抓住整个User对象,保存它,它还会完成剩下的工作吗? serializers.save()在视图中是否与serializers.py中的serializers.save()相同?

1 个答案:

答案 0 :(得分:2)

不是100%确定我已经理解了你的要求,但如果问题是:

  

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

然后答案是:

  

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

Django文档中的ForeignKey field reference部分解释了ORM的这一部分是如何工作的,"Database Representation"子部分可能涉及您感兴趣的特定位。

例如,对于User作为Deck模型中相关字段的情况,基础表可能看起来像这样(假设是postgresql):

myapp_deck
    id         int
    created    timestamp
    name       varChar
    user_id    int

Deck -> User关系由存储pk的数据库映射,用于user_id表中myapp_deck字段中的相关用户对象。

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

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

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

如果要覆盖序列化程序中的自定义“保存”方法,则要覆盖的方法相应地为create()update()(请参阅DRF文档中的Serializer "Saving instances"部分)。< / p>

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

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

注意:这假设相关pk对象的User通过序列化程序传递,验证正常,并且在validated_data字典中可用