Django休息框架:为创建(发布)嵌套序列化程序编写测试时收到KeyError

hkmswyz6  于 2022-12-14  发布在  Go
关注(0)|答案(1)|浏览(135)

我正在尝试编写用于创建销售数据的测试。但是不断得到KeyError:“内容”时运行python manage.py测试。
该测试旨在确保用户可以添加/创建销售数据及其详细信息(嵌套),并以此作为参考,以创建可写嵌套序列化程序
models.py

# abstract base table for transactions
class Base_transaction(models.Model):
    is_paid_off = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    pass

    class Meta:
        abstract = True

# sales table to store surface level sales information
# consist of sales_id as pk, customer_name, sub_total, discount, user_id,
# total, is_paid_off, created_at, updated_at
class Sales(Base_transaction):
    sales_id = models.BigAutoField(
        primary_key=True,
        unique=True
    )
    customer_name = models.CharField(max_length=25, blank=True)
    customer_contact = models.CharField(max_length=13, blank=True)
    user_id = models.ForeignKey(
        User,
        on_delete=models.PROTECT,
        null=True,
        db_column='user_id'
    )

    def __str__(self) -> str:
        return f'{self.sales_id} at {self.created_at} | Lunas={self.is_paid_off}'

    class Meta:
        db_table = 'sales'

# sales_detail table store the detail of sales per sparepart
# consist of sales_detail_id as pk, quantity, individual_price, total_price
# sales_id
class Sales_detail(models.Model):
    sales_detail_id = models.BigAutoField(
        primary_key=True,
        unique=True
    )
    quantity = models.PositiveSmallIntegerField()
    is_grosir = models.BooleanField(default=False)
    sales_id = models.ForeignKey(
        Sales,
        on_delete=models.CASCADE,
        db_column='sales_id'
    )
    sparepart_id = models.ForeignKey(
        'Sparepart',
        on_delete=models.SET_NULL,
        null=True,
        db_column='supplier_id'
    )

    def __str__(self) -> str:
        return f'{self.sales_id} - {self.sparepart_id}'

serializers.py

class SalesDetailSerializers(serializers.ModelSerializer):
    sparepart = serializers.ReadOnlyField(source='sparepart_id.name')

    class Meta:
        model = Sales_detail
        fields = ['sales_detail_id', 'sparepart', 'quantity', 'is_grosir']

class SalesSerializers(serializers.ModelSerializer):
    content = SalesDetailSerializers(many=True, source='sales_detail_set')

    class Meta:
        model = Sales
        fields = ['sales_id', 'customer_name', 'customer_contact', 'is_paid_off', 'content']

    def create(self, validated_data):
        details = validated_data.pop('content')

        sales = Sales.objects.create(**validated_data)
        for detail in details:
            Sales_detail.objects.create(sales_id=sales, **detail)
        return sales

test.py

class SalesAddTestCase(APITestCase):
    sales_url = reverse('sales_add')

    def setUp(self) -> None:
        # Setting up sparepart data
        for i in range(3):
            Sparepart.objects.create(
                name=f'random name{i}',
                partnumber=f'0Y3AD-FY{i}',
                quantity=50,
                motor_type='random m',
                sparepart_type='random s',
                price=5400000,
                grosir_price=5300000,
                brand_id=None
            )

        self.spareparts = Sparepart.objects.all()

        # Creating data that gonna be use as input
        self.data = {
            'customer_name': 'someone',
            'customer_contact': '085634405602',
            'is_paid_off': False,
            'content': [
                {
                    'sparepart': self.spareparts[1].sparepart_id,
                    'quantity': 1,
                    'is_grosir': False,
                },
                {
                    'sparepart': self.spareparts[0].sparepart_id,
                    'quantity': 30,
                    'is_grosir': True,
                }
            ]
        }

    def test_user_successfully_add_sales(self) -> None:
        """
        Ensure user can add new sales data with it's content
        """
        response = self.client.post(self.sales_url, self.data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['customer_name'], 'someone')
        self.assertEqual(len(response.data['content']), 2)

我正在使用泛型的视图。CreateAPIView
预期的响应将与www.example.com相同self.data其中sparepart字段变为该sparepart的名称。下面的示例使用随机数据给予一些上下文

{
'customer_name': 'someone',
'customer_contact': '085456105311',
'is_paid_off': False,
 'content': [
       {
         'sparepart': 'something1', # the name of sparepart 1
         'quantity': 5,
         'is_grosir': False,
       },
       {
          'sparepart': 'something2', # the name of sparepart 2
          'quantity': 3,
          'is_grosir': False,
       }]
}

我尝试将内容中的sparepart更改为= self.spareparts[1],但现在给予 *type**error类型为Sparepart的对象不是JSON可序列化的 *,将其更改为self.spareparts[1].name会给出与之前相同的KeyError(内容)。
我怀疑错误是因为sparepart字段是SalesDetailSerializer中的readonlyfield,然后我将其更改为charfield,但仍然得到相同的KeyError
我真的很困惑是什么使这个错误首先发生。

arknldoa

arknldoa1#

我发现了问题:

  1. SalesDetailSerializerssparepart域为只读,无法用于我用途
  2. SalesSerializers上的content字段使用参数源sales_detail_set。我不知道为什么validated_data中的content键字段变成了sales_detail_set而不是content
    我的解决方案:
    1.因为我已经编写了一个测试,以确保用户可以获得销售清单的详细信息,这需要sparepart字段来显示备件名称,我不能改变它。相反,我创建了另一个名为SalesDetailPostSerializersSalesPostSerializers的序列化程序,就像这样。
class SalesDetailPostSerializers(serializers.ModelSerializer):
    class Meta:
        model = Sales_detail
        fields = ['sales_detail_id', 'sparepart_id', 'quantity', 'is_grosir']

class SalesPostSerializers(serializers.ModelSerializer):
    content = SalesDetailPostSerializers(many=True, source='sales_detail_set')

    class Meta:
        model = Sales
        fields = ['sales_id', 'customer_name', 'customer_contact', 'is_paid_off', 'content']

    def create(self, validated_data):
        details = validated_data.pop('sales_detail_set')
        sales = Sales.objects.create(**validated_data)
        for details in details:
            Sales_detail.objects.create(sales_id=sales, **details)
        return sales

1.在SalesPostSerializer中更改

details = validated_data.pop('content')

结束日期

details = validated_data.pop('sales_detail_set')

然后将www.example.com中self.data内的sparepart密钥名称更改test.py为sparepart_id

'sparepart_id': self.spareparts[1].sparepart_id,

相关问题