2016-04-27 2 views
0

Mit django-rest-framework arbeiten Ich benutze einen Serializer mit vielen = True, nach bereits vorhandenen Elementen zu suchen und sie ungültig zu machen.Django Rest Framework: Einen einzelnen Teil eines multiserialisierten POST ungültig machen

Das Problem ist:

Wenn ein Teil einer Anforderung ungültig ist, die ganze Anforderung zurückgewiesen, ohne die gültigen Objekte zu schaffen.

Probe Nutzlast: [{'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}]

Beispielantwort: [{"last_record_id":[2384],"error":["This record already exists"]},{}]

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

ich den ersten Datensatz möchte den Fehler geben, aber der zweite Datensatz korrekt erstellt werden , wobei die Antwort das erzeugte Objekt ist.

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.' 
       } 

Wie kann ich gültige Objekte erstellen und Fehler für die ungültigen Objekte bereitstellen?

+0

Erhalten Sie Ihre Daten von einem Formular auf einer Webseite? Wenn dem so ist, denke ich, dass dies mehr Arbeit (und Ärger) sowohl auf der Vorder- als auch auf der Rückseite verursachen wird. Angenommen, Sie können dem Benutzer anzeigen, welche Instanzen ungültig sind. Da POST-Anfragen ** nicht ** idempotent sind (dh jedes Mal, wenn Sie die gleiche Anfrage machen, ist das Ergebnis anders - es erstellt jedes Mal eine neue Instanz, wenn Sie eine Anfrage stellen), müssen Sie auf der Front-End-Seite Entfernen Sie die bereits erstellten Objekte aus der Anforderungsnutzlast, sodass keine Duplikate erstellt werden. – iulian

+0

@iulian Daten werden von einem Python Requests Rest Client empfangen. Die POSTs sind idempotent, da der Serialisierer prüft, ob dieses Objekt bereits existiert. Sie können 2 Erstellungsanforderungen für dasselbe Objekt senden, und nur 1 Objekt wird erstellt. Ich möchte, dass jedes Objekt separat behandelt wird, ohne dass für jedes Objekt eine neue HTTP-Anfrage erstellt werden muss. –

Antwort

0

Ich übersprang die Validierung. Dann in meiner create-Methode, wenn das Objekt bereits existiert, gebe ich es einfach zurück, wenn es nicht existiert, ich erstelle es und gebe es zurück.

Der Client weiß nicht mehr, dass der Server diesen Datensatz hatte, aber das ist gut für meinen Anwendungsfall.

Ich wechselte auch zu PUT, um die Tatsache widerzuspiegeln, dass die Methode idempotent ist.

Ich fühle mich wie der Validator ist der Ort, um den Scheck zu tun, aber das funktioniert.

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.' 
       } 
Verwandte Themen