Solución:
Dado que lo más probable es que tu username
el campo tiene unique=True
establecido, Django REST Framework agrega automáticamente un validador que verifica para asegurarse de que el nuevo nombre de usuario sea único. De hecho, puede confirmar esto haciendo repr(serializer())
, que le mostrará todos los campos generados automáticamente, que incluye los validadores.
La validación se ejecuta en un orden específico no documentado
- Deserialización de campo llamada (
serializer.to_internal_value
yfield.run_validators
) -
serializer.validate_[field]
se llama para cada campo - Los validadores de nivel de serializador se denominan (
serializer.run_validation
seguido porserializer.run_validators
) -
serializer.validate
se llama
Entonces, el problema que está viendo es que la validación a nivel de campo se llama antes de la validación a nivel de serializador. Si bien no lo recomendaría, puede eliminar el validador de nivel de campo configurando extra_kwargs
en el meta de su serilalizer.
class Meta:
extra_kwargs = {
"username": {
"validators": [],
},
}
Deberá volver a implementar el unique
Sin embargo, verifique su propia validación, junto con los validadores adicionales que se hayan generado automáticamente.
También estaba tratando de entender cómo fluye el control durante la validación del serializador y después de revisar cuidadosamente el código fuente de djangorestframework-3.10.3, se me ocurrió el siguiente diagrama de flujo de solicitud. He descrito el flujo y lo que sucede en el flujo a mi mejor entendimiento sin entrar en demasiados detalles, ya que se puede buscar desde la fuente.
Ignore las firmas de métodos incompletas. Solo centrándose en qué métodos se llaman y en qué clases.
Suponiendo que tiene un anulado is_valid
método en su clase de serializador (MySerializer(serializers.Serializer)
) cuando usted llama my_serializer.is_valid()
ocurre lo siguiente.
-
MySerializer.is_valid()
es ejecutado. - Suponiendo que está llamando a la superclase (
BaseSerializer
)is_valid
método (como:super(MySerializer, self).is_valid(raise_exception)
en tusMySerializer.is_valid()
método, que se llamará. - Ahora desde
MySerializer
se está extendiendoserializers.Serializer
, losrun_validation()
método deserializer.Serializers
se llama. Esto está validando solo los datos dictados por el primero. Por lo tanto, aún no hemos comenzado las validaciones a nivel de campo. - Entonces el
validate_empty_values
defields.Field
se llama. Esto vuelve a suceder en todo eldata
y ni un solo campo. - Entonces el
Serializer.to_internal_method
se llama. - Ahora recorremos cada campo definido en el serializador. Y para cada campo, primero llamamos al
field.run_validation()
método. Si el campo ha anulado elField.run_validation()
método, entonces se llamará primero. En caso deCharField
se anula y llama alrun_validation
método deField
clase base. Paso 6-2 en la figura. - En ese campo volvemos a llamar al
Field.validate_empty_values()
- los
to_internal_value
del tipo de campo se llama a continuación. - Ahora hay una llamada al
Field.run_validators()
método. Supongo que aquí es donde los validadores adicionales que agregamos en el campo especificando elvalidators = []
la opción de campo se ejecuta una por una - Una vez hecho todo esto, volvemos a la
Serializer.to_internal_value()
método. Ahora recuerde que estamos haciendo lo anterior para cada campo dentro del ciclo for. Ahora, los validadores de campo personalizados que escribió en su serializador (métodos comovalidate_field_name
) se ejecutan. Si ocurrió una excepción en cualquiera de los pasos anteriores, sus validadores personalizados no se ejecutarán. read_only_defaults()
- actualizar validar datos con valores predeterminados, creo
- ejecutar validadores a nivel de objeto. creo que el
validate()
El método en su objeto se ejecuta aquí.
No creo que las soluciones anteriores funcionen más. En mi caso, mi modelo tiene campos ‘first_name’ y ‘last_name’, pero la API solo recibirá ‘name’.
La configuración de ‘extra_kwargs’ y ‘validadores’ en la clase Meta parece no tener ningún efecto, first_name y last_name siempre se consideran obligatorios, y los validadores siempre se llaman. No puedo sobrecargar los campos de caracteres first_name / last_name con
anotherrepfor_first_name = serializers.CharField(source=first_name, required=False)
ya que los nombres tienen sentido. Después de muchas horas de frustración, encontré que la única forma en que podía anular los validadores con una instancia de ModelSerializer era anular el inicializador de clase de la siguiente manera (perdone la sangría incorrecta):
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
Ahora el documento dice que permitir espacios en blanco y nulos con campos de caracteres es un no, pero esto es un serializador, no un modelo, y como la API es llamada por todo tipo de vaqueros, necesito cubrir mis bases.