无法使用上下文和SerializerMethodField

时间:2017-06-29 00:12:05

标签: python django python-3.x django-rest-framework

所以后面的故事是我有一个包含3个字段的模型。一个用默认填充,另一个用API请求传入,最后一个在视图中计算。我无法弄清楚如何将request.data和计算值同时送入序列化程序。这是我的模特:

import re
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.db import models
from django.utils import timezone

URL_MAX_SHORT_ID_LEN = 12
# Maximum length for IE and several other search engines and protocols is
# 2047/2048.
URL_MAX_URL_LEN = 2047

class URL(models.Model):
    short_id = models.CharField(max_length=URL_MAX_SHORT_ID_LEN, primary_key=True)
    url = models.URLField(max_length=URL_MAX_URL_LEN)
    added_date = models.DateTimeField('date added',
                                      default=timezone.now)

    def clean(self):
        # TODO: make max length and possible chars a setting or global variable
        if re.match('^[a-zA-Z0-9]{1,' + str(URL_MAX_SHORT_ID_LEN) + '}$', self.short_id) is None:
            raise ValidationError({'short_id':
                'only a-zA-Z0-9 valid chars and max length of ' +
                                   str(URL_MAX_SHORT_ID_LEN)
            })

    def save(self, *args, **kwargs):
        self.full_clean()
        super(URL, self).save(*args, **kwargs)

    def __str__(self):
        return str(self.__class__) + ': ' + str(self.__dict__)

这是我的序列化器:

from rest_framework import serializers
from url_shortener.models import URL

class URLSerializer(serializers.ModelSerializer):
    short_id = serializers.SerializerMethodField()

    class Meta:
        model = URL
        fields = ('short_id', 'url', 'added_date')
        read_only_fields = ('added_date',)

    def get_short_id(self, obj):
        return self.context.get('short_id')

在shell中进行测试会抛出一条错误,指出short_id为空:

>>> from url_shortener.serializers import *
>>> from django.utils.six import BytesIO
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser
>>>
>>> j = b'{"url":"https://gobin.io"}'
>>> stream = BytesIO(j)
>>> data = JSONParser().parse(stream)
>>> s = URLSerializer(data=data, context={'short_id': 'asd123'})
>>> s.is_valid()
True
>>> s.validated_data
OrderedDict([('url', 'https://gobin.io')])
>>> s.save()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/jolly/git/lurl/myenv/lib/python3.5/site-packages/rest_framework/serializers.py", line 215, in save
    self.instance = self.create(validated_data)
  File "/Users/jolly/git/lurl/myenv/lib/python3.5/site-packages/rest_framework/serializers.py", line 916, in create
    instance = ModelClass.objects.create(**validated_data)
  File "/Users/jolly/git/lurl/myenv/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/jolly/git/lurl/myenv/lib/python3.5/site-packages/django/db/models/query.py", line 394, in create
    obj.save(force_insert=True, using=self.db)
  File "/Users/jolly/git/lurl/lurl/url_shortener/models.py", line 27, in save
    self.full_clean()
  File "/Users/jolly/git/lurl/myenv/lib/python3.5/site-packages/django/db/models/base.py", line 1248, in full_clean
    raise ValidationError(errors)
django.core.exceptions.ValidationError: {'short_id': ['This field cannot be blank.', 'only a-zA-Z0-9 valid chars and max length of 12']}

似乎get_short_id永远不会被调用。我加注了它,它从未被抛出。我不确定我做错了什么,或者这是做我正在做的事情的正确方法。

2 个答案:

答案 0 :(得分:0)

SerializerMethodField是read only。您可以使用普通的CharField并将其值传递给序列化程序保存方法,而不是:

class URLSerializer(serializers.ModelSerializer):
    short_id = serializers.CharField()

在视图中:

s = URLSerializer(data=data)
s.is_valid()
s.save(short_id='asd123')

答案 1 :(得分:0)

我终于找到了正确的搜索词,找到了similar stackoverflow。我不确定它是否总是正确的,但在我的情况下,short_id实际上是read_only。所以我用extra_kwargs创建了我的序列化器:

from rest_framework import serializers
from url_shortener.models import URL, URL_MAX_SHORT_ID_LEN

class URLSerializer(serializers.ModelSerializer):
    class Meta:
        model = URL
        fields = ('short_id', 'url', 'added_date')
        read_only_fields = ('added_date',)
        extra_kwargs = {
            'short_id' : {'read_only' : True}
        }

然后验证并保存如下:

s = URLSerializer(data=data)
s.is_valid()
s.save(short_id='asd123')