簡體   English   中英

Django REST 框架:反序列化外鍵失敗(多對多)

[英]Django REST framework: deserializing foreign key fails (many-to-many)

我有以下模型:

模型.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")

序列化程序.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__'

視圖.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} 

由於eventserialnr上ForeignKeys EventHost你需要發送的數據已經存在,所以我建議使用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__'

發生這種情況是因為您正在制作EventSerializerHostSerializer ,將傳遞的值序列化為read_only這意味着它們只在有retrieve操作時工作,而不是create

解決這個問題的方法是從EventHostSerializer刪除read_only EventHostSerializer

像下面這樣:

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

    class Meta:
        model = EventHost
        fields = '__all__'

我已經從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

暫無
暫無

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

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