【问题标题】:Django rest framework: many to many through model write-ableDjango rest框架:多对多通过模型可写
【发布时间】:2021-11-20 07:21:33
【问题描述】:

我有一个Order 模型和Item 模型。每个订单由多个项目组成。我通过名为OrderItem 的模型连接关系。下面是我的代码

型号:

class Order(models.Model):
    PAYMENT_TYPES = [
        ('COD', 'Cash On Delivery'),
        ('STRIPE', 'Stripe Credit/Debit'),
        ('PAYPAL', 'Paypal')
    ]

    STATUSES = [
        (1, 'Process'),
        (2, 'Completed'),
        (3, 'Hold')
    ]

    number = models.CharField(max_length=255)
    total = models.FloatField(null=True)
    credits_issued = models.FloatField(null=True)
    paid = models.FloatField(null=True)
    expected_delivery = models.DateTimeField(null=True)
    payment_type = models.CharField(max_length=255, choices=PAYMENT_TYPES, null=True)
    date = models.DateTimeField(default=now)
    status = models.CharField(max_length=2, choices=STATUSES)
    note = models.CharField(max_length=255, null=True)
    ordered_by = models.ForeignKey(User, on_delete=models.CASCADE)
    location = models.ForeignKey(Location, on_delete=models.CASCADE)
    vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
    items = models.ManyToManyField(Item, through='OrderItem', related_name='orders')

    class Meta:
        app_label = 'InventoryApp'
        db_table = 'order'

class Item(models.Model):
    STATUSES = [
        (1, 'Active'),
        (0, 'Inactive')
    ]
    DEFAULT_STATUS = 1

    name = models.CharField(max_length=255)
    quantity = models.IntegerField(null=True)
    last_bought_price = models.FloatField(null=True)
    order_by = models.DateTimeField(null=True)
    file_name = models.CharField(max_length=255, null=True)
    status = models.CharField(max_length=2, choices=STATUSES)
    category = models.ForeignKey(ItemCategory, on_delete=models.CASCADE)

    class Meta:
        app_label = 'InventoryApp'
        db_table = 'item'


class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    quantity = models.IntegerField()
    price = models.FloatField()

    class Meta:
        app_label = 'InventoryApp'
        db_table = 'order_item'
        unique_together = [['order', 'item']]

我想知道如何为可写的直通模型制作序列化程序。我已经编写了序列化程序,但它不起作用。

序列化器:

class OrderSerializer(serializers.ModelSerializer):
    items = OrderItemSerializer(many=True)

    class Meta:
        model = Order
        fields = ['id','number', 'total', 'credits_issued', 'paid', 'expected_delivery', 'payment_type', 'date', 'status', 'note', 'ordered_by', 'location', 'vendor', 'items']
        extra_kwargs = {
            'ordered_by': {'required': True, 'read_only': False},
            'location': {'required': True, 'read_only': False},
            'vendor': {'required': True, 'read_only': False},
            'items': {
                'required': True,
                'read_only': False
            }
        }

    def create(self, validated_data):
        items = validated_data.pop('items')
        order = Order.objects.create(**validated_data)
        for item_data in items:
            item = item_data.pop('item')
            OrderItem.objects.create(order=order, item=item, **item_data)
        return order


    def validate_items(self, value):
        if len(value) < 1:
            raise serializers.ValidationError("Order should have at least 1 item")
        return value

    def to_internal_value(self, data):
        if 'items' in data:
            if not isinstance(data.get('items', []), list):
                raise serializers.ValidationError({
                    'items': ['Expected a list of items but got type {input_type}'.format(input_type=type(data.get('items')).__name__)]
                })

        return super().to_internal_value(data)


class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ['id', 'name', 'quantity', 'last_bought_price', 'order_by', 'file_name', 'status', 'category']
        extra_kwargs = {
            'category': { 'required': True, 'read_only': False }
        }


class OrderItemSerializer(serializers.ModelSerializer):
    item = ItemSerializer(read_only=False, required=True)

    class Meta:
        model = OrderItem
        fields = ['item', 'quantity', 'price']
        extra_kwargs = {
        }

编辑: 实际上我正在尝试的是,我想将对象列表发送到订单 API 端点并创建 OrderItems(通过模型)。举个例子:

{
    "number": "ap001",
    "status": "1",
    "ordered_by": 1,
    "location": "1",
    "vendor": 1,
    "items": [
        {
            "item": 1,
            "quantity": 2,
            "price": "10"
        },
        {
            "item": 2,
            "quantity": 3,
            "price": "15"
        }
    ]
}

items数组由item组成,它是Item表的pk。 quantityprice 用于直通模型。所以当我发送这种数据时,它应该通过模型保存数据。

编辑

Environment:


Request Method: POST
Request URL: http://127.0.0.1:8000/orders/

Django Version: 3.2.9
Python Version: 3.9.9
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.staticfiles',
 'rest_framework',
 'InventoryApp.apps.InventoryappConfig']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback (most recent call last):
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\fields.py", line 457, in get_attribute
    return get_attribute(instance, self.source_attrs)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\fields.py", line 97, in get_attribute
    instance = getattr(instance, attr)

During handling of the above exception ('Item' object has no attribute 'item'), another exception occurred:
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\mixins.py", line 20, in create
    headers = self.get_success_headers(serializer.data)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\serializers.py", line 548, in data
    ret = super().data
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\serializers.py", line 246, in data
    self._data = self.to_representation(self.instance)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\serializers.py", line 515, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\serializers.py", line 663, in to_representation
    return [
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\serializers.py", line 664, in <listcomp>
    self.child.to_representation(item) for item in iterable
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\serializers.py", line 502, in to_representation
    attribute = field.get_attribute(instance)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\relations.py", line 190, in get_attribute
    return super().get_attribute(instance)
  File "C:\Users\Ahamed Arshad\Desktop\proj-inventory\env\lib\site-packages\rest_framework\fields.py", line 490, in get_attribute
    raise type(exc)(msg)

Exception Type: AttributeError at /orders/
Exception Value: Got AttributeError when attempting to get a value for field `item` on serializer `OrderItemSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Item` instance.
Original exception text was: 'Item' object has no attribute 'item'.

感谢您的帮助。提前致谢。

【问题讨论】:

  • 什么不起作用?你遇到了什么错误?
  • @BrianDestura 我可以通过自定义创建和更新方法保存数据。但毕竟当我收到回复时,它说Order object has no attribute item 我已经编辑了帖子。请看一下
  • 你能分享整个回溯吗?
  • @BrianDestura 添加了它。

标签: django django-models django-rest-framework django-serializer


【解决方案1】:

使用OrderItemSerializer 的当前实现,您的 API 期望的数据实际上类似于:

{
    "number": "ap001",
    "status": "1",
    "ordered_by": 1,
    "location": "1",
    "vendor": 1,
    "items": [
        {
            "item": {
                "name": "someitem",
                "quantity": 1,
                ...other item fields...
            },
            "quantity": 2,
            "price": "10"
        },
        {
            "item": {
                "name": "someotheritem",
                "quantity": 2,
                ...other item fields...
            },
            "quantity": 3,
            "price": "15"
        }
    ]
}

为了只支持item的整数/主键,你可以使用PrimaryKeyRelatedField:

class OrderItemSerializer(serializers.ModelSerializer):
    item = serializers.PrimaryKeyRelatedField(read_only=False, required=True, queryset=Item.objects.all())

【讨论】:

  • 是的,当我尝试将这些数据插入到表中时。但仍然得到那个属性问题Item' object has no attribute 'item'
  • 嗯,你是否在任何地方覆盖了to_representation
  • 不,我没有覆盖它。
  • Got AttributeError when attempting to get a value for field **item** on serializer **OrderItemSerializer**. The serializer field might be named incorrectly and not match any attribute or key on the **Item** instance. Original exception text was: 'Item' object has no attribute 'item'.
  • 你可以试试把items = OrderItemSerializer(many=True)改成items = OrderItemSerializer(many=True, source='orderitem_set')
【解决方案2】:

感谢@briandestura。在他的帮助下,我找到了答案。我将更新的模型和序列化程序发布给将来需要它的任何人。因为我花了 3 天时间才让它发挥作用。

模型.py

class Item(models.Model):
    STATUSES = [
        (1, 'Active'),
        (0, 'Inactive')
    ]
    DEFAULT_STATUS = 1

    name = models.CharField(max_length=255)
    quantity = models.IntegerField(null=True)
    last_bought_price = models.FloatField(null=True)
    order_by = models.DateTimeField(null=True)
    file_name = models.CharField(max_length=255, null=True)
    status = models.CharField(max_length=2, choices=STATUSES)
    category = models.ForeignKey(ItemCategory, on_delete=models.CASCADE)

    class Meta:
        app_label = 'InventoryApp'
        db_table = 'item'


class Order(models.Model):
    PAYMENT_TYPES = [
        ('COD', 'Cash On Delivery'),
        ('STRIPE', 'Stripe Credit/Debit'),
        ('PAYPAL', 'Paypal')
    ]

    STATUSES = [
        (1, 'Process'),
        (2, 'Completed'),
        (3, 'Hold')
    ]

    number = models.CharField(max_length=255)
    total = models.FloatField(null=True)
    credits_issued = models.FloatField(null=True)
    paid = models.FloatField(null=True)
    expected_delivery = models.DateTimeField(null=True)
    payment_type = models.CharField(max_length=255, choices=PAYMENT_TYPES, null=True)
    date = models.DateTimeField(default=now)
    status = models.CharField(max_length=2, choices=STATUSES)
    note = models.CharField(max_length=255, null=True)
    ordered_by = models.ForeignKey(User, on_delete=models.CASCADE)
    location = models.ForeignKey(Location, on_delete=models.CASCADE)
    vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
    items = models.ManyToManyField(Item, through='OrderItem', related_name='orders')

    class Meta:
        app_label = 'InventoryApp'
        db_table = 'order'


class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    quantity = models.IntegerField()
    price = models.FloatField()

    class Meta:
        app_label = 'InventoryApp'
        db_table = 'order_item'
        unique_together = [['order', 'item']]

序列化器.py

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ['id', 'name', 'quantity', 'last_bought_price', 'order_by', 'file_name', 'status', 'category']
        extra_kwargs = {
            'category': { 'required': True, 'read_only': False }
        }


class OrderSerializer(serializers.ModelSerializer):
    items = OrderItemSerializer(many=True, source='orderitem_set')

    class Meta:
        model = Order
        fields = ['id','number', 'total', 'credits_issued', 'paid', 'expected_delivery', 'payment_type', 'date', 'status', 'note', 'ordered_by', 'location', 'vendor', 'items']
        extra_kwargs = {
            'ordered_by': {'required': True, 'read_only': False},
            'location': {'required': True, 'read_only': False},
            'vendor': {'required': True, 'read_only': False},
            'items': {'required': True, 'read_only': False, 'many': True, 'queryset': OrderItem.objects.all()}
        }

    def create(self, validated_data):
        print(validated_data)
        items = []
        if 'orderitem_set' in validated_data:
            items = validated_data.pop('orderitem_set')
        order = Order.objects.create(**validated_data)
        for item_data in items:
            OrderItem.objects.create(order=order, item=item_data['item'], quantity=item_data['quantity'], price=item_data['price'])
        return order


    def validate_items(self, value):
        if len(value) < 1:
            raise serializers.ValidationError("Order should have at least 1 item")
        return value

    def to_internal_value(self, data):
        if 'items' in data:
            if not isinstance(data.get('items', []), list):
                raise serializers.ValidationError({
                    'items': ['Expected a list of items but got type {input_type}'.format(input_type=type(data.get('items')).__name__)]
                })
        return super().to_internal_value(data)


class OrderItemSerializer(serializers.ModelSerializer):
    item = serializers.PrimaryKeyRelatedField(read_only=False, required=True, queryset=Item.objects.all())

    class Meta:
        model = OrderItem
        fields = ['item', 'quantity', 'price']
        extra_kwargs = {
        }

【讨论】:

    猜你喜欢
    • 2023-03-18
    • 2013-01-22
    • 2016-05-30
    • 2019-03-06
    • 2018-10-15
    • 2018-09-13
    • 2012-12-04
    • 2016-04-14
    • 2020-11-18
    相关资源
    最近更新 更多