簡體   English   中英

DRF 序列化程序中的“動態”字段

[英]'Dynamic' fields in DRF serializers

我的目標是建立一個端點,它可以使用 GenericForeignKey 創建模型對象。 由於模型還包括 ContentType,因此我們將引用的模型的實際類型在對象創建之前是未知的。

我將提供一個例子:

我有一個“Like”模型,它可以引用一組其他模型,如“Book”、“Author”。

class Like(models.Model):
    created = models.DateTimeField()
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

序列化器可能如下所示:

class LikeSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Like
        fields = ('id', 'created', )

我想要實現的是根據請求中傳遞的鍵來確定 Like 的類型。 問題是如果沒有在Serializer器字段中明確指定,DRF 不會從請求中傳遞這些密鑰。 例如,POST 請求正文包含:

{
    "book":2
}

我想做下一個

def restore_object(self, attrs, instance=None)
    if attrs.get('book', None) is not None:
        # create Like instance with Book contenttype
    elif attrs.get('author', None) is not None:
        # create Like instance with Author contenttype

在這種情況下,將執行第一個 if 子句。 可以看到,根據請求傳入的key確定的類型,沒有指定特殊的Field。

有什么辦法可以做到這一點?

謝謝

每當您的視圖被調用時,您可以嘗試實例化您的序列化器,方法是將其包裝在一個函數中(您創建一個序列化器工廠):

def like_serializer_factory(type_of_like):
    if type_of_like == 'book':
        class LikeSerializer(serializers.ModelSerializer):
            class Meta:
                model = models.Like
                fields = ('id', 'created', )
            def restore_object(self, attrs, instance=None):
                # create Like instance with Book contenttype
    elif type_of_like == 'author':
        class LikeSerializer(serializers.ModelSerializer):
            class Meta:
                model = models.Like
                fields = ('id', 'created', )
            def restore_object(self, attrs, instance=None):
                # create Like instance with Author contenttype
    return LikeSerializer

然后在您的視圖中覆蓋此方法:

def get_serializer_class(self):
    return like_serializer_factory(type_of_like)

解決方案 1

基本上,您可以在 GenericAPIView 類上添加一個名為 get_context_serializer 的方法。默認情況下,您的視圖、請求和格式類將傳遞給您的序列化程序 DRF 代碼以獲得 get_context_serializer

def get_serializer_context(self):
    """
    Extra context provided to the serializer class.
    """
    return {
        'request': self.request,
        'format': self.format_kwarg,
        'view': self
    }

你可以像這樣在你的視圖上覆蓋它

def get_serializer_context(self):
    data = super().get_serializer_context()
    # Get the book from post and add to context
    data['book'] = self.request.POST.get('book')
    return data

並在您的序列化程序類上使用它

def restore_object(self, attrs, instance=None):
    # Get book from context to use
    book = self.context.get('book', None)
    author = attrs.get('author', None)
    if book is not None:
        # create Like instance with Book contenttype
        pass
    elif author is not None:
        # create Like instance with Author contenttype
        pass

解決方案 2

在您的序列化程序上添加一個字段

class LikeSerializer(serializers.ModelSerializer):
    # New field and should be write only, else it will be
    # return as a serializer data
    book = serializers.IntegerField(write_only=True)
    class Meta:
        model = models.Like
        fields = ('id', 'created', )
    
    def save(self, **kwargs):
        # Remove book from validated data, so the serializer does
        # not try to save it
        self.validated_data.pop('book', None)
        
        # Call model serializer save method
        return super().save(**kwargs)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM