2014-12-21 8 views
17

SytuacjaOrder Serializatora Walidacja w Django REST ramowego

Pracując z walidacji w Django REST ramowego ModelSerializer, zauważyłem, że pola Meta.model są zawsze sprawdzane, nawet jeśli niekoniecznie sensu robić więc. Wykonaj następujący przykład serializacji modelu User:

  1. Mam punkt końcowy, który tworzy użytkownika. Jako takie istnieje pole password i confirm_password. Jeśli te dwa pola nie pasują, nie można utworzyć użytkownika. Podobnie, jeśli żądana wersja username już istnieje, użytkownik nie może zostać utworzony.
  2. słupkami użytkowników niewłaściwe wartości dla każdej z dziedzin wymienionych powyżej
  3. Implementacja validate zostało dokonane w serializatora (patrz poniżej), łapanie niepasujące password i confirm_password pola

Realizacja validate:

def validate(self, data): 
    if data['password'] != data.pop('confirm_password'): 
     raise serializers.ValidationError("Passwords do not match") 
    return data 

problem

Nawet po podniesieniu ValidationError przez validate, ModelSerializer nadal wysyła zapytania do bazy danych, aby sprawdzić, czy username jest już w użyciu. Jest to widoczne na liście błędów, która jest zwracana z punktu końcowego; występują błędy zarówno modelu, jak i błędów innych niż pola.

W związku z tym chciałbym wiedzieć, jak zapobiec sprawdzania poprawności modelu, aż po zakończeniu sprawdzania braku pola, zapisując wywołanie do mojej bazy danych.

próba rozwiązania

I zostały próbuje przejść przez źródła DRF by dowiedzieć się, gdzie to się dzieje, ale okazały się nieskuteczne w zlokalizowaniu co muszę zastąpić, aby uzyskać to do pracy .

Odpowiedz

48

Ponieważ najprawdopodobniej twoje pole username ma zestaw unique=True, Django REST Framework automatycznie dodaje walidator, który sprawdza, czy nowa nazwa użytkownika jest unikatowa. Możesz to potwierdzić, wykonując repr(serializer()), który wyświetli wszystkie automatycznie wygenerowane pola, w tym weryfikatory.

Walidacja jest prowadzony w określonym, undocumented celu deserializacji

  1. polu zwanym (serializer.to_internal_value i field.run_validators)
  2. serializer.validate_[field] nazywa się dla każdego pola
  3. Serializatora poziomu zatwierdzającym nazywanych (serializer.run_validation następnie przez serializer.run_validators)
  4. serializer.validate nazywane jest

Problemem jest to, że sprawdzanie poprawności na poziomie pola jest wywoływane przed sprawdzaniem poprawności na poziomie serializera. Chociaż nie poleciłbym tego, możesz usunąć walidator na poziomie pola, ustawiając extra_kwargs w meta serilalizera.

class Meta: 
    extra_kwargs = { 
     "username": { 
      "validators": [], 
     }, 
    } 

Trzeba będzie ponownie wdrożyć kontrola unique w swoim własnym walidacji jednak, wraz z wszelkimi dodatkowymi poprawności, które zostały wygenerowane automatycznie.

+0

O cholera, tak właśnie się wydarzyło. Czy uważasz, że właściwe byłoby po prostu pozwolić, aby walidacja trafiła do bazy danych, czy też powinienem ponownie wprowadzić unikalny walidator? – nmagerko

+0

"pozwól, aby walidacja trafiła do bazy danych" spowoduje błędy integralności, więc zdecydowanie nie. Zalecam ponowne wdrożenie unikalnego walidatora, jeśli chcesz, aby to ostatnie działało. –

+0

Właściwie, myślę, że mogę spróbować nadpisać 'to_internal_value' i sprawić, by to się skończyło. Niezależnie od tego, odpowiedziałeś na pytanie – nmagerko

1

Nie wierzę, że powyższe rozwiązania działają. W moim przypadku mój model ma pola "first_name" i "last_name", ale API otrzyma tylko "nazwę".

Ustawienie "extra_kwargs" i "validators" w klasie Meta wydaje się nie mieć żadnego skutku, first_name i last_name są zawsze wymagane, a walidatory są zawsze wywoływane. Nie mogę przeciążyć pól z nazwami first_name/last_name, ponieważ nazwy mają sens. Po wielu godzinach frustracji, znalazłem tylko w ten sposób mogę zastąpić weryfikatorów z instancją ModelSerializer było zastąpić inicjatora klasy następująco (odpuszczania nieprawidłowe wcięcia):

class ContactSerializer(serializers.ModelSerializer): 
name = serializers.CharField(required=True) 

class Meta: 
    model = Contact 
    fields = [ 'name', 'first_name', 'last_name', 'email', 'phone', 'question' ] 

def __init__(self, *args, **kwargs): 
    self.fields['first_name'] = serializers.CharField(required=False, allow_null=True, allow_blank=True) 
    self.fields['last_name'] = serializers.CharField(required=False, allow_null=True, allow_blank=True) 
    return super(ContactSerializer, self).__init__(*args, **kwargs) 

def create(self, validated_data): 
    return Contact.objects.create() 

def validate(self, data): 
    """ 
    Remove name after getting first_name, last_name 
    """ 
    missing = [] 
    for k in ['name', 'email', 'question']: 
     if k not in self.fields: 
      missing.append(k) 
    if len(missing): 
     raise serializers.ValidationError("Ooops! The following fields are required: %s" % ','.join(missing)) 
    from nameparser import HumanName 
    names = HumanName(data['name']) 
    names.capitalize() 
    data['last_name'] = names.last 
    if re.search(r'\w+', names.middle): 
     data['first_name'] = ' '.join([names.first, names.middle]) 
    else: 
     data['first_name'] = names.first 
    del(data['name']) 

    return data 

Teraz doc mówi, że pozwalając puste i null z polami znaków to nie nie, ale to serializator, a nie model, a ponieważ API jest wywoływane przez wszystkich kowbojów, muszę pokryć moje bazy.