ERROR: Cannot create a Meal instance with a FK Store instance in the views.py (Django and Django Rest Framework)

ERROR: Cannot create a Meal instance with a FK Store instance in the views.py (Django and Django Rest Framework)

我正在交付 API REST 使用 Django 和 Django Rest Framework。

Meal 应用程序中出现错误已经好几天了,我无法解决。我不知道如何解决它。

提前致谢。

这是邮递员中显示的错误

{
    "store": {
        "non_field_errors": [
            "Invalid data. Expected a dictionary, but got Store."
        ]
}

Meal/models/meals.py

class Meal(models.Model):
    '''Meals models.'''

    store = models.ForeignKey(
        Store, 
        on_delete=models.CASCADE,
        )

    name = models.CharField(max_length=120)
    slugname = models.SlugField(
        unique=True,
        max_length=120,
        )
    description = models.TextField(max_length=300, blank=True)
    price = models.DecimalField(
        'meal price',
        max_digits=5,
        decimal_places=2
        )
    picture = models.ImageField(
        'meal picture',
        upload_to='meals/pictures/',
        blank=True,
        null=True,
        help_text="Price max up to 9.99"
    )

    # Status
    is_available = models.BooleanField(
        'Meal available in menu',
        default=True,
        help_text='Show is the items is available for customers'
    )

    # Stats
    rating = models.FloatField(
        default=5.0,
        help_text="Meal's rating based on client califications"
    )

Meal/views/meals.py

class MealViewSet(mixins.ListModelMixin,
                    mixins.CreateModelMixin,
                    mixins.RetrieveModelMixin,
                    mixins.DestroyModelMixin,
                    viewsets.GenericViewSet):
    '''Meals view set.
    '''

    serializer_class = MealModelSerializer
    lookup_field = 'slugname'
    search_fields = ('slugname', 'name')


    # Method call every time this MealViewSet is instanced
    def dispatch(self, request, *args, **kwargs):
        '''Verify that the store exists.
        Add the URL input <store_slugname> to the Meal model field store(FK).
        '''

        store_slugname = kwargs['store_slugname']
        self.store = get_object_or_404(Store, store_slugname=store_slugname)

        return super(MealViewSet, self).dispatch(request, *args, **kwargs)


    def get_queryset(self):
        '''Get Store's available meals'''

        return Meal.objects.filter(
            store=self.store,
            is_available=True
        )

    def create(self, request, *args, **kwargs):
        '''Assign Meal to a Store (received in the URL input <store_slugname>)
        '''
        store = self.store # Got from the dispatcher
        request.data['store'] = store
        serializer = MealModelSerializer(
            store,
            data=request.data
        )
        # import pdb; pdb.set_trace()
        serializer.is_valid(raise_exception=True)
        serializer.save()
        data = serializer.data
        return Response(data, status=status.HTTP_201_CREATED)

和Meal/serializers/meals.py

class MealModelSerializer(serializers.ModelSerializer):
    '''Meal model serializer.'''

    store = StoreModelSerializer()

    class Meta:
        """Meta class."""

        model = Meal
        fields = (
            'store',
            'name',
            'slugname',
            'description',
            'price',
            'picture',
            'rating'
        )
        read_only_fields = (
            'rating',
        )

终端中的调试器显示如下: (Store=dominospizza 是有效的且之前创建的商店)

Pdb) store
<Store: dominospizza>
(Pdb) store.__dict__
{'_state': <django.db.models.base.ModelState object at 0x7fd539882c10>, 'id': 1, 'name': 'Peperoni large pizza', 'store_slugname': 'dominospizza', 'about': '', 'picture': '', 'pickup_address': 'Urdesa cerca de casa de Agapito', 'is_active': True, 'is_open': True, 'orders_dispatched': 0, 'reputation': 5.0}
(Pdb) request.data
{'name': 'Peperoni large pizza', 'slugname': 'LPizzaPeperoni', 'price': '9.00', 'store': <Store: dominospizza>}

请尝试在 MealViewSet.create 中更改此设置:

request.data['store'] = store

通过这个:

request.data['store'] = store.id

这是因为我认为默认的 ModelSerializer fks 像主键相关字段一样被解决,然后它期待一个 pk 而不是实例。

首先,

尽量遵循 REST。如果要根据字段值过滤后端实体,请在查询参数中提供它们。您可以使用 django-filters 自动过滤。参考 this.

其次,

您无需重写 ViewSet 的创建方法即可实现。序列化器就是为此而生的。修改序列化器代码如下,

class MealModelSerializer(serializers.ModelSerializer):
    '''Meal model serializer.'''

    store = StoreModelSerializer()
    store_id = models.PrimaryKeyRelatedField(
        queryset=models.Store.objects.all(),
        write_only=True,
        source="store",
        required=False
    )
    store_slogname = models.SlugRelatedField(
        queryset=models.Store.objects.all(),
        write_only=True,
        slug_field="store_slugname"
        source="store",
        required=False
    )

    class Meta:
        """Meta class."""
        model = Meal
        fields = (
            'store',
            'name',
            'slugname',
            'description',
            'price',
            'picture',
            'rating'
        )
        read_only_fields = (
            'rating',
        )

但请记住,标准做法是传递 ID 而不是名称。确保商店字段 store_slugname 是唯一的。

从前端,您可以在 POST、PUT、PATCH 主体中传递 store_id 或 store_slugname。序列化程序自动查找数据库并验证数据。您不需要重写任何方法。

编辑#1:

您可以使用 django-rest-framework-filters 功能 AllLookupsFilter 在前端级别提供所有类型的过滤。

示例:

/api/{version}/meals/?store__store_slugname=<value>
/api/{version}/meals/?store__store_slugname__contains=<value>
/api/{version}/meals/?store__store_slugname__in=<value1,value2,value3>

总而言之,您的视图集将如下所示,

class MealViewSet(
    mixins.ListModelMixin,
    mixins.CreateModelMixin,
    mixins.RetrieveModelMixin,
    mixins.DestroyModelMixin,
    viewsets.GenericViewSet
):
    '''Meals view set.'''

    serializer_class = MealModelSerializer
    lookup_field = 'slugname'
    search_fields = ('slugname', 'name')
    filter_class = MealFilter

希望对您有所帮助!