首页 > 解决方案 > django-rest-framework:使用 ENUMS 和 source='get_label_display' 时的模型序列化程序

问题描述

最近我一直在开发我的第一个基于 django 框架的 API,但遇到了一个问题。这是我的代码:

模型.py

from django.db import models
from multiselectfield import MultiSelectField


COUNTRY_LIST = [('PL', 'Poland'), ('DE', 'Germany'), ('FI', 'Finland'), ('DK', 'Denmark'), ('SE', 'Sweden')]

CLIENTS_LIST = [('CP1', 'Company1'), ('CP2', 'Company2'), ('CP3', 'Company3')]

TRANSPORTS_LIST = [('V', 'Vehicle'), ('P', 'Plane'), ('T', 'Train'), ('S', 'Ship')]

STATUS_LIST = [('NW', 'New'), ('WC', 'Wait for confirmation'), ('AC', 'Accepted'), ('CL', 'Closed')]


class Trip(models.Model):

    startDate = models.DateField()
    endDate = models.DateField()
    place = models.CharField(max_length=100)
    country = models.CharField(choices=COUNTRY_LIST, max_length=2)
    client = models.CharField(choices=CLIENTS_LIST, max_length=3, default='NA')
    transport = MultiSelectField(choices=TRANSPORTS_LIST)
    acommodations = models.BooleanField()
    tripStatus = models.CharField(
        choices=STATUS_LIST, max_length=2, default='NW')
    remarks = models.TextField(blank=True)
    employeeId = models.IntegerField()
    tripIdentifier = models.CharField(max_length=10)

序列化程序.py

from rest_framework import serializers
from trips.models import Trip, COUNTRY_LIST, CLIENTS_LIST, TRANSPORTS_LIST, STATUS_LIST

# Trip Serializer


class TripSerializer(serializers.ModelSerializer):

    country = serializers.CharField(source='get_country_display')
    client = serializers.CharField(source='get_client_display')
    transport = serializers.CharField(source='get_transport_display')
    tripStatus = serializers.CharField(source='get_tripStatus_display')

    class Meta:
        model = Trip
        fields = '__all__'

api_views.py

from rest_framework.exceptions import ValidationError
from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveUpdateDestroyAPIView
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter

from trips.serializers import TripSerializer
from trips.models import Trip

import re


class TripList(ListAPIView):
    queryset = Trip.objects.all()
    serializer_class = TripSerializer
    filter_backends = (DjangoFilterBackend, SearchFilter)
    search_fields = ("tripIdentifier", "id")
    filter_fields = ('id', 'tripStatus', 'employeeId')

class TripCreate(CreateAPIView):
    serializer_class = TripSerializer

    def create(self, request, *args, **kwargs):
        return super().create(request, *args, **kwargs)


class TripRetrieveUpdateDelete(RetrieveUpdateDestroyAPIView):
    queryset = Trip.objects.all()
    lookup_field = 'id'
    serializer_class = TripSerializer

    def delete(self, request, *args, **kwargs):
        trip_id = request.data.get('id')
        response = super().delete(request, *args, **kwargs)
        if response.status_code == 204:
            from django.core.cache import cache
            cache.delete('trip_data_{}'.format(trip_id))
        return response

    def update(self, request, *args, **kwargs):
        response = super().update(request, *args, **kwargs)
        if response.status_code == 200:
            from django.core.cache import cache
            trip = response.data
            cache.set('trip_data_{}'.format(trip['id']), {
                'startDate': trip['startDate'],
                'endDate': trip['endDate'],
                'place': trip['place'],
                'employeeId': trip['employeeId'],
            })
        return response

网址.py

from django.conf import settings
from django.conf.urls.static import static
from django.conf.urls import url
from django.contrib import admin
from django.urls import path, include

import employee.views
import employee.api_views

import trips.views
import trips.api_views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('employee', employee.api_views.EmployeeList.as_view()),
    path('employee/new', employee.api_views.EmployeeCreate.as_view()),
    path('employee/<int:id>',
         employee.api_views.EmployeeRetrieveUpdateDelete.as_view()),
    path('trip', trips.api_views.TripList.as_view()),
    path('trip/new', trips.api_views.TripCreate.as_view()),
    path('trip/<int:id>', trips.api_views.TripRetrieveUpdateDelete.as_view())
]

这是一个问题描述:获取、放置和删除请求工作正常。但是当我调用 POST 请求并尝试添加新的 Trip 时,我得到了一个错误:

TypeError at /trip/new
Got a `TypeError` when calling `Trip.objects.create()`. This may be because you have a writable field on the serializer class that is not a valid argument to `Trip.objects.create()`. You may need to make the field read-only, or override the TripSerializer.create() method to handle this correctly.
Original exception was:
 Traceback (most recent call last):
  File "C:\Users\User\.virtualenvs\RadiolineExtranet-iRF8Rd_0\lib\site-packages\rest_framework\serializers.py", line 932, in create
    instance = ModelClass._default_manager.create(**validated_data)
  File "C:\Users\User\.virtualenvs\RadiolineExtranet-iRF8Rd_0\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\User\.virtualenvs\RadiolineExtranet-iRF8Rd_0\lib\site-packages\django\db\models\query.py", line 420, in create
    obj = self.model(**kwargs)
  File "C:\Users\User\.virtualenvs\RadiolineExtranet-iRF8Rd_0\lib\site-packages\django\db\models\base.py", line 501, in __init__
    raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.__name__, kwarg))
TypeError: Trip() got an unexpected keyword argument 'get_country_display'

你能帮我理解一下我的代码有什么问题吗?

标签: pythonrestdjango-rest-framework

解决方案


堆栈跟踪显示它试图创建:self.model(**kwargs)。您告诉它某些字段由模型支持,但在不同的列名中,因此它会尝试保存它们:

serializers.CharField(source='get_country_display')

你可以试试:

  • 利用CharField('get_country_display', read_only=True)
  • 添加read_only_fields=['get_country_display', ...]Meta班级
  • 使用SerializerMethodField只读的,并手动调用该函数

推荐阅读