2014-12-14 18 views
5

Per esempio ho il seguente serializzatore:Modifica campi serializzatore al volo

class UserSerializer(serializers.ModelSerializer): 

    class Meta: 
     model = User 
     fields = (
      'userid', 
      'password' 
     ) 

Ma io non voglio la password di uscita sul GET (ci sono altri campi nel mio esempio reale ovviamente). Come posso farlo senza scrivere altro serializzatore? Cambia l'elenco dei campi al volo. C'è un modo per farlo?

risposta

20

Sembra che stiate cercando un campo di sola scrittura. Quindi il campo sarà richiesto alla creazione, ma non verrà visualizzato all'utente (il contrario di un campo di sola lettura). Fortunatamente, Django REST Framework ora supporta i campi di sola scrittura con the write_only attribute.

In Django REST Framework 3.0, è sufficiente aggiungere l'argomento aggiuntivo to the extra_kwargs meta option.

class UserSerializer(serializers.ModelSerializer): 

    class Meta: 
     model = User 
     fields = (
      'userid', 
      'password' 
     ) 
     extra_kwargs = { 
      'password': { 
       'write_only': True, 
      }, 
     } 

Perché il password dovrebbe essere hashing (che si sta utilizzando utente di Django, giusto?), Si sta andando ad avere bisogno di hash anche la password come è venuta in. Questo dovrebbe essere fatto sulla vostra vista, molto probabilmente sovrascrivendo i metodi perform_create e perform_update.

from django.contrib.auth.hashers import make_password 

class UserViewSet(viewsets.ViewSet): 

    def perform_create(self, serializer): 
     password = make_password(self.request.data['password']) 

     serializer.save(password=password) 

    def perform_update(self, serializer): 
     password = make_password(self.request.data['password']) 

     serializer.save(password=password) 

In 2.x Django REST quadro, è necessario ridefinire completamente il campo password sul serializzatore.

class UserSerializer(serializers.ModelSerializer): 
    password = serializers.CharField(write_only=True) 

    class Meta: 
     model = User 
     fields = (
      'userid', 
      'password' 
     ) 

Al fine di hash della password prima del tempo in 2.x Django REST Framework, è necessario eseguire l'override pre_save.

from django.contrib.auth.hashers import make_password 

class UserViewSet(viewsets.ViewSet): 

    def pre_save(self, obj, created=False): 
     obj.password = make_password(obj.password) 

     super(UserViewSet, self).pre_save(obj, created=created) 

Questo risolverà il problema comune con le altre risposte, che è che la stessa serializzatore che viene utilizzato per creare/aggiornare l'utente sarà utilizzato anche per restituire l'oggetto utente aggiornata come risposta.Ciò significa che la password verrà comunque restituita nella risposta, anche se si desidera solo che sia di sola scrittura. Il problema aggiuntivo con questo è che la password può o non può essere hash nella risposta, che è qualcosa che davvero non vuoi fare.

+0

Questo è esattamente quello che stavo cercando, anche se non utilizzo il modello utente Django - le password vengono già sottoposte a hash, ma è ancora più semplice in questo modo. Grazie. – wswld

+0

Grazie! Aiutami molto –

0

Per quanto ne so dai documenti, il modo più veloce sarebbe semplicemente disporre di 2 serializzatori, in modo condizionato dalla vista.

Inoltre, la documentazione mostrano questa altra alternativa, ma è un po 'troppo meta: http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields

che comporta la creazione di metodi di inizializzazione intelligenti, fornisce un esempio. userei solo 2 serializzatori, se potessi sapere che quei cambiamenti sono gli unici che farò. altrimenti, controlla l'esempio

+0

Mentre funziona, lo stesso serializzatore verrà utilizzato per visualizzare l'oggetto dopo che è stato creato, quindi la password (auspicata con hash) sarebbe visibile. –

1

questo dovrebbe essere quello che ti serve. Ho usato una vista funzione ma puoi usare class View o ViewSet (override get_serializer_class) se preferisci.

noti che serializer_factory anche accettare exclude= ma, per motivi di sicurezza, io preferisco usare fields=

serializer_factory creare una classe Serializer al volo utilizzando un serializzatore esistente come base (uguale a Django modelform_factory)

== ============

class UserSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = User 
     fields = (
      'userid', 
      'password' 
     ) 


@api_view(['GET', 'POST']) 
def user_list(request): 
    User = get_user_model() 

    if request.method == 'GET': 
     fields=['userid'] 
    elif request.method == 'POST': 
     fields = None 
    serializer = serializer_factory(User, UserSerializer, fields=fields) 
    return Response(serializer.data) 

def serializer_factory(model, base=HyperlinkedModelSerializer, 
         fields=None, exclude=None): 
    attrs = {'model': model} 
    if fields is not None: 
     attrs['fields'] = fields 
    if exclude is not None: 
     attrs['exclude'] = exclude 

    parent = (object,) 
    if hasattr(base, 'Meta'): 
     parent = (base.Meta, object) 
    Meta = type(str('Meta'), parent, attrs) 
    if model: 
     class_name = model.__name__ + 'Serializer' 
    else: 
     class_name = 'Serializer' 
    return type(base)(class_name, (base,), {'Meta': Meta, }) 
2

Solo un'altra cosa per la soluzione di @Kevin Brown.

Poiché partial update eseguirà anche perform_update, sarebbe meglio aggiungere codice aggiuntivo come segue.