2014-12-21 5 views
11

Sto utilizzando il Django REST Framework (DRF) per creare un endpoint con cui posso registrare nuovi utenti. Tuttavia, quando raggiungo l'endpoint di creazione con un POST, il nuovo utente viene salvato tramite un serializzatore, ma la password viene salvata in chiaro nel database. Il codice per la mia serializer è la seguente:Perché la password del mio modello utente Django non è stata eseguita?

from django.contrib.auth import get_user_model 
from rest_framework import serializers 

class UserSerializer(serializers.ModelSerializer): 

    class Meta: 
     model = get_user_model() 
     fields = ['password', 'username', 'first_name', 'last_name', 'email'] 
     read_only_fields = ['is_staff', 'is_superuser'] 
     write_only_fields = ['password'] 

Si prega di notare che io sto usando il modello utente predefinito dal pacchetto auth Django, e che io sono molto nuovo a lavorare con DRF! Inoltre, ho trovato this question che fornisce una soluzione, ma questo sembra richiedere due interazioni di database - non credo che questo sia efficiente, ma potrebbe essere un'ipotesi errata da parte mia.

+0

Ecco un'altra soluzione che sovrascrive 'perform_create' e' perform_update': http://stackoverflow.com/questions/27468552/changing-serializer-fields-on-the-fly/#answer-27471503 –

risposta

23

La questione è DRF semplicemente impostare i valori dei campi sul modello. Pertanto, la password è impostata sul campo della password e salvata nel database. Ma per impostare correttamente una password, è necessario chiamare il metodo set_password(), che eseguirà l'hashing.

Ci sono diversi modi per farlo, ma il modo migliore su rest framework v3 è di ignorare i metodi update() e create() sul Serializer.

class UserSerializer(serializers.ModelSerializer): 
    # <Your other UserSerializer stuff here> 

    def create(self, validated_data): 
     password = validated_data.pop('password', None) 
     instance = self.Meta.model(**validated_data) 
     if password is not None: 
      instance.set_password(password) 
     instance.save() 
     return instance 

    def update(self, instance, validated_data): 
     for attr, value in validated_data.items(): 
      if attr == 'password': 
       instance.set_password(value) 
      else: 
       setattr(instance, attr, value) 
     instance.save() 
     return instance 

Due cose qui:

  1. noi utenti self.Meta.model, quindi se il modello viene modificato sul serializer, funziona ancora (fintanto che ha un metodo ovviamente set_password ).
  2. iteriamo su validated_data articoli e non i campi, per giustificare facoltativamente exclude campi.

Inoltre, questa versione di create non salva le relazioni M2M. Non necessario nel tuo esempio, ma potrebbe essere aggiunto se necessario. Avresti bisogno di scoppiare quelli dal ditt, salvare il modello e impostarli in seguito.

+0

Ottima risposta, ma sono un po 'nuovo per questo, quindi dovrò chiedere cosa intendi non salvando il modello per modellare le relazioni.Se sto usando un 'ModelSerializer', questa funzionalità non verrà automaticamente? – nmagerko

+0

In particolare, una risposta qui (http: // stackoverflow .com/a/13564519/996249, nota l'autore) dice che posso semplicemente modificare 'serializer.object' – nmagerko

+1

Per le relazioni model2model, cosa intendo dire se stai salvando altri modelli connessi all'utente, usando un serializzatore annidato, il l'esempio che ho fornito salverà solo l'utente, non gli altri oggetti Come per il tuo link, si tratta di REST Framework v2.La versione 3 ha cambiato l'API per quanto riguarda il salvataggio del modello. – spectras

1

semplicemente ignorare il create and update methods del serializzatore:

def create(self, validated_data): 
     user = get_user_model(**validated_data) 
     user.set_password(validated_data['password']) 
     user.save() 
     return user 

    def update(self, instance, validated_data): 
     for f in UserSerializer.Meta.fields + UserSerializer.Meta.write_only_fields: 
      set_attr(instance, f, validated_data[f]) 
     instance.set_password(validated_data['password']) 
     instance.save() 
     return instance 
+0

Esiste un significativo differenza tra il tuo approccio e l'approccio della risposta qui sotto? Sembrano in qualche modo simili a me – nmagerko

+0

@nmagerko no l'approccio è esattamente lo stesso, non controllo se la password potrebbe essere None, stavo preferendo brevità per delineare il metodo e lasciarvi l'effettiva implementazione, se questo vi interessa allora per favore cambialo! – DRC

+0

Purtroppo, devo accettare l'altra risposta; l'utilizzo di 'get_user_model' non funzionerà sempre qui, poiché alcune parole chiave fornite nei dati validati sono estranee e genereranno un errore :( – nmagerko