【问题标题】:DRF expose model field in two separate read-write fields on the REST APIDRF 在 REST API 上的两个单独的读写字段中公开模型字段
【发布时间】:2015-09-04 19:12:32
【问题描述】:

我正在使用 Python 2.7、DRF 3.1.3、Django 1.4.21(我知道它很旧,但它是一个大型代码库,有一天我们会迁移)。实际的商业案例比这更复杂,这里是简化版。

我有一个模型:

class Person(models.Model):
    foo = models.CharField(max_length=2, blank=True, null=True)

在 REST API 上,我想公开两个字段,它们都是从 foo 派生的。这两个字段都应该是可写的,并且当我写入时,这两个字段都会以某种方式设置 foo 字段。 尝试 1:不幸的是 bar 是只读的

class PersonSerializer(serializers.ModelSerializer):
    bar = serializers.SerializerMethodField()

    class Meta:
        model = Person
        fields = ('id', 'foo', 'bar')

    def get_bar(self, obj):
        return get_bar(obj.foo) # not relevant now what get_bar does

尝试 2:尝试以某种方式伪造字段

class PlaceholderCharField(serializers.CharField):
    def to_representation(self, obj):
        pass

    def to_internal_value(self, data):
        pass

class PersonSerializer(serializers.ModelSerializer):
    bar = PlaceholderCharField()

    class Meta:
        model = Person
        fields = ('id', 'foo', 'bar')

    def to_representation(self, instance):
        data = super(PersonSerializer, self).to_representation(instance)
        data['bar'] = get_bar(instance.foo)
        return data

    def to_internal_value(self, data):
        instance = super(PersonSerializer, self).to_internal_value(data)
        if data.has_key('bar') and not data.has_key('foo'):
            instance.foo = get_foo(data['bar']) if data['bar'] else None
        return instance

后一个错误显然是抱怨Person 模型没有 bar 字段。这是真的。如何解决这个问题呢?我可以在序列化程序的to_representationto_internal_value 中处理设置/获取foo 和bar。我只想在DRF 中指定一个字段,该字段仅存在于REST API 端,并且在模型端没有关联的字段。我可以处理转换。并且该字段应该是可读写的,否则我可以用SerializerMethodField 解决它。

【问题讨论】:

    标签: python django rest django-rest-framework


    【解决方案1】:

    稍微调整一下您的第一次尝试,您就可以为该字段设置别名,没问题:

    class PersonSerializer(serializers.ModelSerializer):
        #                 vvvvvvvvv
        bar = serializers.CharField(
           source='foo', required=False
        )# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
        class Meta:
            model = Person
            fields = ('id', 'foo', 'bar')
    

    我仍在使用 DRF 2.4.4 以获得更好的嵌套、多对象嵌套序列化程序支持,但我一直使用该方法为 URL 及其关联对象设置别名,以处理 Angular JS 比较对象的方式它的一些控件。例如

    class SensorSerializer(serializers.HyperlinkedModelSerializer):
        location_obj = SensorLocationSerializer(source='location',read_only=True,required=False)
        class Meta:
            model = Sensor
            fields = ('url', 'id', 'name', 'serial_number', 'location', 
                'location_obj', 'active')
    

    【讨论】:

    • 但是,如果我将“条”明确标记为只读,它将如何被写入?这看起来就像我有一个 SerializerMethodField() 一样。就我而言,到目前为止我遇到的两对字段不是 RelatedFields 或 RelaterPrimaryKeyFields(感谢上帝)。
    • 已编辑。这个表格怎么样,没有read_only 标志?它仍然应该给它起别名。唯一的冲突来自在更新中同时拥有foobar。我不确定哪个会赢。它可能是您的Meta.fields 中列出的最后一个字段。无论如何,我认为您仍然会遇到别名问题,因为您从根本上对字段进行了别名。你可以为这个向后兼容的章节编写一个显式的序列化器和视图。
    • 哦,所以我可以简单地在同一个序列化程序中有两个字段引用同一个源字段?
    • 是的。你可以这样做。当心数据竞赛。
    【解决方案2】:

    我不确定你的用例,这似乎有点奇怪,但你为什么不在你的模型上添加一个属性。例如,

       @property
        def bar(self):
            """
            Used for DRF only.
            """
            return None # or self.foo
    

    然后将其添加到序列化程序中的字段中(无需使用PlaceholderCharField

    fields = ('id', 'foo', 'bar') 
    

    其余的将按照您的方式工作。

    【讨论】:

    • 我正在考虑这个问题。有没有办法向模型添加一个字段,这样它就不会导致架构更改,不会被持久化?
    • 我承认我的用例很奇怪。一切都继承自早期的非 REST API,客户端开发者的时间比我还宝贵。
    • 如果你将上面的属性添加到你的模型中,它不会被持久化到数据库中,也不需要迁移。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 2015-08-16
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 2020-10-11
    • 1970-01-01
    相关资源
    最近更新 更多