Django Rest Framework:使多序列化 POST 的单个部分无效



使用 django-rest-framework,我正在使用一个带有 many=True 的序列化程序,检查已经存在的项目并使它们失效。

问题是:

当部分请求无效时,将拒绝整个请求而不创建有效对象。

示例有效载荷: [{'record_timestamp': '2016-03-04T09:46:04', 'reader_serial': u'00000000f9b320ac', 'card_serial': u'048EC71A0F3382', 'gps_latitude': None, 'gps_longitude': None, 'salt': 34, 'reader_record_id': 1063}, {'record_timestamp': '2016-03-04T09:46:06', 'reader_serial': u'00000000f9b320ac', 'card_serial': u'04614B1A0F3382', 'gps_latitude': None, 'gps_longitude': None, 'salt': 34, 'reader_record_id': 1064}]

示例响应: [{"last_record_id":[2384],"error":["This record already exists"]},{}]

理想响应: [{"last_record_id":[2384],"error":["This record already exists"]},{'reader': 10, 'card': 12, 'gps_latitude': None, 'gps_longitude': None, 'reader_record_id': 1064}}]

我希望第一条记录提供错误,但要正确创建第二条记录,响应是创建的对象。

class CardRecordInputSerializer(serializers.ModelSerializer):
class Meta:
    model = CardRecord
    fields = ('card', 'reader', 'bus', 'park', 'company', 'client',
             'record_timestamp', 'reader_record_id')
    read_only_fields = ('card', 'reader', 'bus', 'park', 'company'
                        'client')

def validate(self, data):
    """
    Check that the record is unique
    """
    #import ipdb; ipdb.set_trace()
    hash_value = data.get("hash_value", None)
    if CardRecord.objects.filter(hash_value=hash_value):
        raise ValidationError(
                detail={"error":"This record already exists", 
                "last_record_id":data.get("reader_record_id", None)})
    else:
        return data

def to_internal_value(self, data):
    internal_value = super(CardRecordInputSerializer, self)
                            .to_internal_value(data)
    card_serial = data.get("card_serial", None).upper()
    reader_serial = data.get('reader_serial', None).upper()
    record_timestamp = data.get('record_timestamp', None)
    date_altered = False
    record_date = dateutil.parser.parse(record_timestamp)
    #check if clock has reset to 1970
    if record_date < datetime.datetime(2014, 4, 24):
        record_date = datetime.datetime.now().isoformat()
        date_altered = True
    #create a hash to check that this record is unique
    salt = data.get('salt', None)
    hash_generator = hashlib.sha1()
    hash_generator.update(card_serial)
    hash_generator.update(reader_serial)
    hash_generator.update(str(record_timestamp))
    hash_generator.update(str(salt))
    hash_value = str(hash_generator.hexdigest())
    internal_value.update({
        "card_serial": card_serial,
        "reader_serial": reader_serial,
        "salt": salt,
        "hash_value": hash_value,
        "record_timestamp": record_date,
        "date_altered": date_altered
    })
    return internal_value

def create(self, validated_data):
    #import ipdb; ipdb.set_trace()
    '''
        Create a new card transaction record
    '''
    try:
        card_serial = validated_data.get('card_serial', None)
        card = Card.objects.filter(uid=card_serial).last()
        reader_serial = validated_data.get('reader_serial', None)
        reader = Reader.objects.filter(serial=reader_serial).last()
        #if we havent seen this reader before, add it to the list
        if not reader:
            reader = Reader.objects.create(serial=reader_serial)
        company = card.company
        client = reader.client
        park = reader.park
        record_timestamp = validated_data.get('record_timestamp', None)
        reader_record_id = validated_data.get('reader_record_id', None)
        #if datetime is naive, set it to utc
        if record_timestamp.tzinfo is None 
            or record_timestamp.tzinfo.utcoffset(d) is None:
                record_timestamp = pytz.utc.localize(record_timestamp)
        hash_value = validated_data.get('hash_value', None)
        date_altered = validated_data.get('date_altered', None)
        return CardRecord.objects.create(card = card, 
                                        reader = reader, 
                                        company = company, 
                                        client = client, 
                                        park = park, 
                                        record_timestamp = record_timestamp,
                                        reader_record_id = reader_record_id,
                                        hash_value = hash_value,
                                        date_altered = date_altered)
    #Usually a card that doesn't have company
    except AttributeError:
        return {
                'status': 'Bad Request',
                'message': 'One of the values was malformed or does not exist.'
                }

如何创建有效对象并为无效对象提供错误?

我最终跳过了验证。然后在我的 create 方法中,如果对象已经存在,我只返回它,如果它不存在,我创建它并返回它。

客户端不再知道服务器有该记录,但这对我的用例来说很好。

我还换成了使用 PUT 来反映该方法是幂等的事实。

我觉得验证器是进行检查的地方,但这有效。

class CardRecordInputSerializer(serializers.ModelSerializer):
class Meta:
    model = CardRecord
    fields = ('card', 'reader', 'bus', 'park', 'company', 'client',
             'record_timestamp', 'reader_record_id')
    read_only_fields = ('card', 'reader', 'bus', 'park', 'company'
                        'client')

def validate(self, data):
    """
    Check that the record is unique
    """
    #import ipdb; ipdb.set_trace()
                               #<--------Removed the validation
    return data

def to_internal_value(self, data):
    internal_value = super(CardRecordInputSerializer, self)
                            .to_internal_value(data)
    card_serial = data.get("card_serial", None).upper()
    reader_serial = data.get('reader_serial', None).upper()
    record_timestamp = data.get('record_timestamp', None)
    date_altered = False
    record_date = dateutil.parser.parse(record_timestamp)
    #check if clock has reset to 1970
    if record_date < datetime.datetime(2014, 4, 24):
        record_date = datetime.datetime.now().isoformat()
        date_altered = True
    #create a hash to check that this record is unique
    salt = data.get('salt', None)
    hash_generator = hashlib.sha1()
    hash_generator.update(card_serial)
    hash_generator.update(reader_serial)
    hash_generator.update(str(record_timestamp))
    hash_generator.update(str(salt))
    hash_value = str(hash_generator.hexdigest())
    internal_value.update({
        "card_serial": card_serial,
        "reader_serial": reader_serial,
        "salt": salt,
        "hash_value": hash_value,
        "record_timestamp": record_date,
        "date_altered": date_altered
    })
    return internal_value

def create(self, validated_data):
    #import ipdb; ipdb.set_trace()
    '''
        Create a new card transaction record
    '''
    try:
        card_serial = validated_data.get('card_serial', None)
        card = Card.objects.filter(uid=card_serial).last()
        reader_serial = validated_data.get('reader_serial', None)
        reader = Reader.objects.filter(serial=reader_serial).last()
        #if we havent seen this reader before, add it to the list
        if not reader:
            reader = Reader.objects.create(serial=reader_serial)
        company = card.company
        client = reader.client
        park = reader.park
        record_timestamp = validated_data.get('record_timestamp', None)
        reader_record_id = validated_data.get('reader_record_id', None)
        #if datetime is naive, set it to utc
        if record_timestamp.tzinfo is None 
            or record_timestamp.tzinfo.utcoffset(d) is None:
                record_timestamp = pytz.utc.localize(record_timestamp)
        hash_value = validated_data.get('hash_value', None)
        date_altered = validated_data.get('date_altered', None)
        record = CardRecord.objects.filter(hash_value=hash_value).last()
        if record:                         #<--------Check if that object already exists
            return record                   #<-------- if it does just return it
        else:                                #<-------- otherwise make it
            return CardRecord.objects.create(
                        card = card, 
                        reader = reader, 
                        company = company, 
                        client = client, 
                        park = park, 
                        record_timestamp = record_timestamp,
                        reader_record_id = reader_record_id,
                        hash_value = hash_value,
                        date_altered = date_altered)
    #Usually a card that doesn't have company
    except AttributeError:
        return {
                'status': 'Bad Request',
                'message': 'One of the values was malformed or does not exist.'
                }

最新更新