并非所有请求有效负载都会进入序列化程序Django Rest Framework

时间:2016-04-18 16:31:00

标签: django serialization django-rest-framework

DRF新手,请耐心等待我:)

我正在尝试使用Serializer更新对象,由于某种原因,我无法将所有请求的有效负载都提供给Serializer,其中一个字段缺失。我必须说这个字段不是与Serializer链接的模型的一部分,但是查看文档,这似乎不是问题......我可以添加我想要的任何字段。

那就是说,这是我的模特:

class Product(AbstractProduct):
    name = models.CharField(max_length=50, verbose_name=_(u'Name'))
    school = models.ForeignKey('school.School')
    level = models.ForeignKey(
        'school.Level',
        verbose_name=_(u'Level')
    )
    age = IntegerRangeField()
    gender = models.CharField(choices=GENDER_CHOICES, max_length=1, null=True, blank=True, verbose_name=_(u'Gender'))
    num_sessions = models.PositiveSmallIntegerField(verbose_name=_(u'Number of sessions'),
    default=1,
    help_text=_(u"Number of sessions that the product has."),
    )

    addons = models.ManyToManyField('self',
        verbose_name=_(u'Administrators'),
        through='AddonInService',
        symmetrical=False,
        related_name='addon_can_be_used_in'
    )

    class Meta(AbstractProduct.Meta):
        verbose_name_plural = _(u'Products')

这是我的ProductViewSet,没什么特别的,你可以看到......

class ProductAPIViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    @property
    def pagination_class(self):
        if 'offset' in self.request.query_params:
            return CustomLimitOffsetPagination
        else:
            return CustomPageNumberPagination

最后,这是我的Serializer

class AddonSerializer(serializers.ModelSerializer):
    price = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = (
            'id',
            'title',
            'name',
            'slug',
            'description',
            'price',
        )

    def get_price(self, obj):
        Selector = get_class('partner.strategy', 'Selector')
        selector = Selector()
        strategy = selector.strategy()
        stock_info = strategy.fetch_for_product(obj)
        return stock_info.price.incl_tax

class ProductSerializer(serializers.ModelSerializer):
    age = IntegerRangeField()
    addons = AddonSerializer(many=True, read_only=True)
    price = serializers.SerializerMethodField()
    color = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = [
            'id',
            'structure',
            'upc',
            'title',
            'slug',
            'description',
            'rating',
            'date_created',
            'date_updated',
            'is_discountable',
            'name',
            'age',
            'gender',
            'num_sessions',
            'parent',
            'product_class',
            'school',
            'level',
            'school_category',
            'addons',
            'color',
            'price',
        ]

    def get_price(self, obj):
        Selector = get_class('partner.strategy', 'Selector')
        selector = Selector()
        strategy = selector.strategy()
        stock_info = strategy.fetch_for_product(obj)
        return stock_info.price.incl_tax

    def validate(self, data):
        school = data.get('school')
        level = data.get('level')
        view = self.context['view']
        school_id = view.kwargs['parent_lookup_school']
        school = School.objects.get(id=school_id)

        if level and level.school != school:
            raise serializers.ValidationError(
                {'level': _(u'The level must be a level created by the school. ')
             })

        school_category = data.get('school_category')
        if school_category is not None and school_category.school != school:
            raise serializers.ValidationError({'school_category':
             _(u'All the categories must be the categories created by the school.')}
            )

        return data

    def get_color(self, obj):
        try:
            color = obj.school_category.color
        except:
            color = ''
        return color

    def create(self, validated_data):
        product = Product.objects.create(**validated_data)
        return product

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            if attr in 'price':
                print('found price!!!')
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance

好的,现在想象在我的前端我创建了一个修改现有产品的请求,请求有效负载是这样的:

{
    "school": 1,
    "school_category": 1,
    "name": "Surfing board",
    "slug": "object-object",
    "level": 3,
    "num_sessions": 1,
    "age": {
        "upper": 65,
        "lower": 18,
        "bounds": "[)"
    },
    "addons": [{
        "id": 6,
        "title": "Photography",
        "name": "Photography",
        "slug": "",
        "description": "Nice pics",
        "price": 206.87
    }],
    "gender": "F",
    "description": "a nice board",
    "price": 2111
}

因此,我期望在序列化程序更新方法中获得“2111”,但是没有,我什么都没得到。因此,我检查了验证方法,看看到底有什么:

(Pdb) l
85      
86          def validate(self, data):
87              import pdb
88              pdb.set_trace()
89              school = data.get('school')
90  ->          level = data.get('level')
91              view = self.context['view']
92              school_id = view.kwargs['parent_lookup_school']
93              school = School.objects.get(id=school_id)
94      
95              if level and level.school != school:
(Pdb) print data
OrderedDict([(u'slug', u'object-object'), (u'description', u'a nice board'), (u'name', u'Surfing board'), (u'age', NumericRange(18, 65, u'[)')), (u'gender', 'F'), (u'num_sessions', 1), (u'school', <School: school_1>), (u'level', <Level: advanced>), (u'school_category', <Category: Surf <school_1>>)])

正如您所看到的,没有任何价格也没有将插件传递给Serializer ......

如果我创建了ProductSerializer的实例,我可以看到字段......

In [1]: from test.applications.catalogue.serializers import ProductSerializer

In [2]: ps = ProductSerializer()

In [3]: print repr(ps)
ProductSerializer():
    id = IntegerField(label='ID', read_only=True)
    structure = ChoiceField(choices=(('standalone', <django.utils.functional.__proxy__ object>), ('parent', <django.utils.functional.__proxy__ object>), ('child', <django.utils.functional.__proxy__ object>)), label='Product structure', required=False)
    upc = CharField(allow_blank=True, allow_null=True, help_text='Universal Product Code (UPC) is an identifier for a product which is not specific to a particular  supplier. Eg an ISBN for a book.', label='UPC', max_length=64, required=False, validators=[<UniqueValidator(queryset=Product.objects.all())>])
    title = CharField(allow_blank=True, max_length=255, required=False)
    slug = SlugField(max_length=255)
    description = CharField(allow_blank=True, required=False, style={'base_template': 'textarea.html'})
    rating = FloatField(read_only=True)
    date_created = DateTimeField(read_only=True)
    date_updated = DateTimeField(read_only=True)
    is_discountable = BooleanField(help_text='This flag indicates if this product can be used in an offer or not', label='Is discountable?', required=False)
    name = CharField(max_length=50)
    age = IntegerRangeField()
    gender = ChoiceField(allow_blank=True, allow_null=True, choices=(('M', <django.utils.functional.__proxy__ object>), ('F', <django.utils.functional.__proxy__ object>)), required=False)
    num_sessions = IntegerField(help_text='Number of sessions that the product has.', label='Number of sessions', max_value=32767, min_value=0, required=False)
    school = PrimaryKeyRelatedField(queryset=School.objects.all())
    level = PrimaryKeyRelatedField(queryset=Level.objects.all())
    school_category = PrimaryKeyRelatedField(allow_null=True, label='Category', queryset=Category.objects.all(), required=False)
    addons = AddonSerializer(many=True, read_only=True):
        id = IntegerField(label='ID', read_only=True)
        title = CharField(allow_blank=True, max_length=255, required=False)
        name = CharField(max_length=50)
        slug = SlugField(max_length=255)
        description = CharField(allow_blank=True, required=False, style={'base_template': 'textarea.html'})
        price = SerializerMethodField()
    color = SerializerMethodField()
    price = SerializerMethodField()

任何想法为什么我没有得到价格 - 串行器中的插件?谢谢!

编辑:根据文档和上面的答案,我通过将字段更改为IntegerField来修复此问题

price = serializers.IntegerField()

然后移动在模型中添加了一个方法

@property
def get_price(self):
    Selector = get_class('partner.strategy', 'Selector')
    selector = Selector()
    strategy = selector.strategy()
    stock_info = strategy.fetch_for_product(self)
    return stock_info.price.incl_tax

这实际上有效,看起来更干净......

1 个答案:

答案 0 :(得分:1)

这是因为您使用SerializerMethodField作为价格。根据{{​​3}}:

  

这是一个只读字段。它通过调用附加到它的序列化程序类的方法来获得它的价值。

如果您希望将整数传递给后端,则必须使用IntegerField。为了实现检索数据的专用功能,您可以the documentation

price = serializers.IntegerField(source='get_price')

可能必须将此get_price()方法移至模型才能使其正常工作。