【问题标题】:Django REST framework: deserializing foreign key fails (many-to-many)Django REST框架:反序列化外键失败(多对多)
【发布时间】:2020-02-06 09:03:17
【问题描述】:

我有以下型号:

models.py:

class Host(models.Model):
    serialnr = models.IntegerField(primary_key=True)
    ...some other fields...

class Event(models.Model):
    id = models.AutoField(primary_key=True)
    hosts = models.ManyToManyField(Host, through='EventHost')
    ...some other fields...

class EventHost(models.Model):
    serialnr = models.ForeignKey(Host, on_delete=models.PROTECT)
    event = models.ForeignKey(Event, on_delete=models.CASCADE)
    ...some other fields...
    class Meta:
        unique_together = ("serialnr", "event")

serializers.py:

class EventSerializer(serializers.ModelSerializer):
    class Meta:
        model = Event
        fields = '__all__'

class HostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Host
        fields = '__all__'

class EventHostSerializer(serializers.ModelSerializer):
    event =  EventSerializer(read_only=True)
    serialnr = HostSerializer(read_only=True)

    class Meta:
        model = EventHost
        fields = '__all__'

views.py

class EventViewSet(viewsets.ModelViewSet):
    queryset = Event.objects.order_by('-date')
    serializer_class = EventSerializer

class HostViewSet(viewsets.ModelViewSet):
    queryset = Host.objects.order_by('-serialnr')
    serializer_class = HostSerializer

class EventHostViewSet(viewsets.ModelViewSet):
    queryset = EventHost.objects.order_by('-start_date')
    serializer_class = EventHostSerializer

我正在使用 HTTP POST 发送以下 JSON:

{event: {id: 4}, serialnr: {serialnr: 1234}, other_filed: 20} 

但它不是 event_id 和 serialnr_id 没有反序列化,如日志中所示:

psycopg2.errors.NotNullViolation: null value in column "event_id" violates not-null constraint
DETAIL:  Failing row contains (12, 20, null, null).

我可以使用 HTTP GET 读取数据,但无法使用 POST 写入。我应该如何构造适当的序列化程序以使其工作?

当我尝试像下面这样发送 JSON 时,它也失败了:

{event_id: 4, serialnr_id: 1234, other_filed: 20} 

【问题讨论】:

    标签: python django python-3.x django-rest-framework deserialization


    【解决方案1】:

    由于eventserialnrEventHost 上的ForeignKeys,您需要发送已经存在的数据,所以我建议您使用PrimaryKeyRelatedField 这样,如果您发送的ID 不存在于您的数据库。

    您需要像这样发送数据: {event: 4, serialnr: 1234, other_filed: 20}

    并将您的序列化程序更改为:

    from rest_framework.relations import PrimaryKeyRelatedField
    
    
    class EventHostSerializer(serializers.ModelSerializer):
        event = PrimaryKeyRelatedField(queryset=Event.objects.all())
        serialnr = PrimaryKeyRelatedField(queryset=Host.objects.all())
    
        class Meta:
            model = EventHost
            fields = '__all__'
    
        # add this(if needed) to get event/serialnr representation instead of primary keys 
        # might be usefull for you when you retrieve the object back (in list/retrieve operations)
        def to_representation(self, instance):
            ret = super().to_representation(instance)
            ret['event'] = EventSerializer(context=self.context).to_representation(instance.event)
            ret['serialnr'] = HostSerializer(context=self.context).to_representation(instance.serialnr)
            return ret
    

    后期编辑:

    我发现有一个名为 django-extra-fields 的库可以更好地执行此操作。

    https://github.com/Hipo/drf-extra-fields#presentableprimarykeyrelatedfield

    from drf_extra_fields.relations import PresentablePrimaryKeyRelatedField
    
    
    class EventHostSerializer(serializers.ModelSerializer):
        event = PresentablePrimaryKeyRelatedField(
            queryset=Event.objects.all(), presentation_serializer=EventSerializer
        )
        serialnr = PresentablePrimaryKeyRelatedField(
            queryset=Host.objects.all(), presentation_serializer=HostSerializer
        )
    
        class Meta:
            model = EventHost
            fields = '__all__'
    

    【讨论】:

      【解决方案2】:

      发生这种情况是因为您正在制作EventSerializerHostSerializer, 将传递的值序列化为read_only,这意味着它们仅在有retrieve 操作时才起作用,而不是create

      解决这个问题的方法是从您的 EventHostSerializer 中删除 read_only kwarg

      如下:

      class EventHostSerializer(serializers.ModelSerializer):
          event =  EventSerializer() #read_only=True)
          serialnr = HostSerializer() #read_only=True)
      
          class Meta:
              model = EventHost
              fields = '__all__'
      

      【讨论】:

      • 我已经尝试过了,但我得到:错误请求:/api/v1/eventhosts/ [06/Feb/2020 09:45:17] "POST /api/v1/eventhosts/ HTTP /1.1" 400 166 。有没有办法追踪我的请求出了什么问题?我使用嵌套或扁平化的 HTTP POST 发送的正确 JSON 格式应该是什么?
      【解决方案3】:

      我已经从 EventSerializerHostSerializer 中删除了 read_only=True 参数并打开调试以查看问题所在,现在我的 HTTP POST 请求是否尝试创建新主机和新事件。那不是我需要的。我只需要引用 event 和 host 来在 eventhost 表中创建新条目。

      只是为了做笔记。当我使用 django shell 添加新的 eventhost 实例时,它可以工作:

      >>> from myapp.models import Host, Event, EventHost
      >>> from myapp.serializers import HostSerializer, EventSerializer, EventHostSerializer
      myeventhost = EventHost.objects.create(event_id=4, serialnr_id=1234, otherparam=20)
      >>> serializedmyeventhost = EventHostSerializer(myeventhost)
      >>> serializedmyeventhost.data
      {'id': 29, 'event': OrderedDict([('id', 4), ...etc
      

      【讨论】:

        猜你喜欢
        • 2017-01-07
        • 2021-04-08
        • 1970-01-01
        • 2018-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多