【问题标题】:set contenttype by name in generic relation in django rest framework在 django rest 框架中按名称在通用关系中设置内容类型
【发布时间】:2014-09-18 04:28:39
【问题描述】:
class Foo(models.Model):
    bar = models.CharField(max_length=300)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')


class FooSerializer(serializers.ModelSerializer):
    class Meta:
       model = Foo

class FooViewSet(viewsets.ModelViewSet):
    model = Foo
    serializer_class = FooSerializer

我现在可以将数据发布到如下所示的视图集:

{
    bar: 'content',
    content_type: 1
    object_id: 5
}

唯一困扰我的是前端必须知道内容类型 ID

相反,我希望能够将 content_types 名称(如“用户”)发布为 content_type,并让后端确定 ID。

【问题讨论】:

    标签: python django django-rest-framework generic-relationship


    【解决方案1】:

    自 DRF 3.x 以来最简单、最简洁的读/写操作方法:

    from django.contrib.contenttypes.models import ContentType
    from rest_framework import serializers
    from .models import Foo
    
    class FooSerializer(serializers.ModelSerializer):
        class Meta:
           model = Foo
    
        content_type = serializers.SlugRelatedField(
            queryset=ContentType.objects.all(),
            slug_field='model',
        )
    

    然后您可以使用模型名称执行 CRUD 操作:

    data = {
        'bar': "content",
        'content_type': "model_name",
        'object_id': 1,
    }
    

    【讨论】:

    • 我认为这样更好——因为端点的调用者可能无法处理泛型类型。通过返回 ID 和类型(以字符串形式),调用者可以通过适当的端点进一步请求内容。
    【解决方案2】:

    您可以自定义 WritableField 以将内容类型 id 映射到 'app_label.model' 字符串:

    class ContentTypeField(serializers.WritableField):
        def field_from_native(self, data, files, field_name, into):
            into[field_name] = self.from_native(data[field_name])
    
        def from_native(self, data):
            app_label, model = data.split('.')
            return ContentType.objects.get(app_label=app_label, model=model)
    
        # If content_type is write_only, there is no need to have field_to_native here.
        def field_to_native(self, obj, field_name):
            if self.write_only:
                return None
            if obj is None:
                return self.empty
            ct = getattr(obj, field_name)
            return '.'.join(ct.natural_key())
    
    
    class FooSerializer(serializers.ModelSerializer):
        content_type = ContentTypeField()
        # ...
    

    您可能需要进行第二次映射以限制内容类型的选择并避免暴露您的应用/模型名称:

    CONTENT_TYPES = {
      'exposed-contenttype': 'app_label.model'
    }
    
    class ContentTypeField(...):
        def from_native(self, data):
            if data not in CONTENT_TYPES:
                raise serializers.ValidationError(...)
            app_label, model = CONTENT_TYPES[data].split('.')
            # ...
    

    【讨论】:

      【解决方案3】:

      DRF 已更改,现在代替 from_native 和 field_to_native 有新方法 - to_internal_value 和 to_representation。

      现在更简单了:

      class ContentTypeField(serializers.Field):
      
          def to_representation(self, obj):
              return obj.model
      
          def to_internal_value(self, data):
              return ContentType.objects.get(model=data)
      

      【讨论】:

        猜你喜欢
        • 2018-06-24
        • 2013-06-01
        • 1970-01-01
        • 2022-01-19
        • 2015-09-03
        • 1970-01-01
        • 2020-10-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多