首页 > 解决方案 > 从相关模型中读取特定领域

问题描述

我有这个简单的关系设置和工作,但现在我需要修改它并且遇到了问题。以前是“获取与当前用户相关的产品名称”,现在是“获取与当前用户相关的产品名称、开始日期和结束日期”。

对于前者,我通过以下方式得到它:

# models.py
class Products(models.Model):
    id = models.AutoField(primary_key=True)
    code = models.CharField(unique=True, max_length=16)
    name = models.CharField(max_length=255, blank=True, null=True)
    short_name = models.CharField(max_length=128)
    start_date= models.DateField(blank=True, null=True)
    end_date = models.DateField(blank=True, null=True)  
    users = models.ManyToManyField(User, through='UserToProduct')
    stage = models.CharField(max_length=32, blank=True, null=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'Products'

class UserToProduct(models.Model):
    user = models.ForeignKey(User, related_name='user_product', db_column='user_id', null=True, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, related_name='product', db_column='product_id', null=True, on_delete=models.CASCADE)
    stamp = models.DateTimeField(blank=True, null=True) 

    def __str__(self):
        return u'%s' % self.product

    class Meta:
        db_table = 'User_to_Product'
        unique_together = ('user', 'product')
        ordering = ['-product']

并使用以下序列化程序:

# serializers.py
class ProductListSerializer(serializers.ModelSerializer):
    product = serializers.CharField()
    product_id = serializers.IntegerField()

    class Meta:
        model = Products
        fields = [
            'product_id',
            'product'
        ]

class GetHomeSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    product_list = ProductListSerializer(required=False, many=True)

    def validate(self, data):
        if data:
            data['product_list'] = UserToProduct.objects.filter(user=data['id']).prefetch_related('product').filter(product__stage='active')
            return data

现在我正在尝试将我的查询修改为以下内容:

class ProductListSerializer(serializers.ModelSerializer):
    product = serializers.CharField()
    product_id = serializers.IntegerField()
    start_date = serializers.DateField()

    class Meta:
        model = UserToProduct
        fields = [
            'product_id',
            'product',
            'start_date'
        ]

... #stuff in between

data['product_list'] = UserToProduct.objects.filter(user=data['id']).prefetch_related('product').filter(product__stage='active').values('product_id', 'product', 'product__start_date')

并将该字段添加到ProductListSerializer. 我得到错误:

KeyError: "Got KeyError when attempting to get a value for field `start_date` on serializer `ProductListSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `dict` instance.\nOriginal exception text was: 'start_date'."

还尝试了以下方法:

class ProductListSerializer(serializers.ModelSerializer):
    id= serializers.IntegerField()
    name = serializers.CharField()
    start_date = serializers.DateField()

    class Meta:
        model = Products
        fields = [
            'id',
            'name',
            'start_date'
        ]

... #stuff in between

data['product_list'] = UserToProduct.objects.filter(user=data['id']).prefetch_related('product').filter(product__stage='active').values('product__id', 'product__name', 'product__start_date')

这会导致相同的错误,即id出现错误的字段。

尝试替换values().select_related(products__product),这只会导致:

Choices are: user, product

试过了.select_related(product),它说:

Original exception text was: 'UserToProduct' object has no attribute 'name'. 

这里给出了什么?

标签: djangodjango-rest-frameworkdjango-serializer

解决方案


使用source参数将嵌套关系指定为,

class ProductListSerializer(serializers.ModelSerializer):
    product = serializers.CharField()
    product_id = serializers.IntegerField()
    start_date = serializers.DateField(source='product.start_date') # chage is here <<<<<

    class Meta:
        model = UserToProduct
        fields = [
            'product_id',
            'product',
            'start_date'
        ]


而且,你的validate()方法不好,不应该是这样。如果不满足某些特定条件,则validate 方法应该是raise一些。例子:ValidationError

class SampleSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()

    def validate(self, attrs):
        if attrs['age']:  # if age is there
            if attrs['age'] < 18:  # validation check for 
                raise serializers.ValidationError("You are under 18. Not allowed")
        return attrs

    def validate_age(self, age):
        if age < 18:  # validation check for 
            raise serializers.ValidationError("You are under 18. Not allowed")
        return age

推荐阅读