16

Sto lavorando allo sviluppo di un'API con Django-rest-framework e consumo da un'app Web. Ha un modello di medico con un Fk dal modello utente django.auth. Voglio inviare da un modulo per il modello medico, ma il serializzatore restituisce questo messaggio:SerializerClass field on Serializer save from primary key

{ "utente": { "non_field_errors": [ ". Dati non validi attesi un dizionario, ma ha ottenuto unicode"]} }

Sto inviando la chiave primaria dell'oggetto utente. Qual è il giusto (o solo un modo) per memorizzare una chiave esterna su DRF. Ho provato a sovrascrivere get_validation_exclusions sul serializzatore e a sovrascrivere il metodo perform_create sul gruppo di viste.

L'api e l'app Web sono scollegate. L'API è sviluppata con django e l'app web con angularjs.

Il mio modello

class Physician(models.Model): 
    medical_office_number = models.CharField(max_length = 15) 
    fiscal_id_number = models.CharField(max_length = 20) 
    user = models.OneToOneField(User) 

    def __unicode__(self): 
     return self.user.first_name +' '+ self.user.last_name 

Serializer:

class PhysicianSerializer(serializers.ModelSerializer): 
    user = AccountSerializer() 
    class Meta: 
     model = Physician 
     fields = ('id', 'user', 'medical_office_number', 'fiscal_id_number') 
     read_only_fields = ('id') 
     depth = 1 
    def get_validation_exclusions(self, *args, **kwargs): 
     exclusions = super(PhysicianSerializer, self).get_validation_exclusions() 
     return exclusions + ['user'] 

* Modifica Questo è il mio conto serializzatore, che si basa su questa implementazione e con il suggerimento @ Kevin Brown

class PrimaryKeyNestedMixin(serializers.RelatedField, serializers.ModelSerializer): 

    def to_internal_value(self, data): 
     return serializers.PrimaryKeyRelatedField.to_internal_value(self, data) 
    def to_representation(self, data): 
     return serializers.ModelSerializer.to_representation(self, data) 

class AccountSerializer(PrimaryKeyNestedMixin): 
    password = serializers.CharField(write_only=True, required=False) 
    confirm_password = serializers.CharField(write_only=True, required=False) 

    class Meta: 
     model = Account 
     fields = ('id', 'email', 'username', 'created_at', 'updated_at', 
        'first_name', 'last_name', 'password', 
        'confirm_password', 'is_admin',) 
     read_only_fields = ('created_at', 'updated_at',) 

Viewset

class AccountViewSet(viewsets.ModelViewSet): 
    lookup_field = 'username' 
    queryset = Account.objects.all() 
    serializer_class = AccountSerializer 

Quando si tenta di serializzare questo oggetto, si genera un errore.

Così posso postare qualsiasi utente dall'elemento <select>. Ma non posso verificare la soluzione. Qualcosa che mi manca?

errore Stacktrace

TypeError at /api/v1/accounts/ 

__init__() takes exactly 1 argument (5 given) 

Exception Location:  /home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/rest_framework/relations.py in many_init, line 68 
Python Executable: /home/jlromeroc/workspace/asclepios/venv/bin/python 
Python Version:  2.7.3 

File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response 111. response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view 57. return view_func(*args, **kwargs) 
File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/rest_framework/viewsets.py" in view 85. return self.dispatch(request, *args, **kwargs) 
File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch 407. response = self.handle_exception(exc) File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch 404. response = handler(request, *args, **kwargs) 
File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/rest_framework/mixins.py" in list 45. serializer = self.get_serializer(instance, many=True) 
File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/rest_framework/generics.py" in get_serializer 90. instance, data=data, many=many, partial=partial, context=context File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/rest_framework/relations.py" in __new__ 48. return cls.many_init(*args, **kwargs) 
File "/home/jlromeroc/workspace/asclepios/venv/local/lib/python2.7/site-packages/rest_framework/relations.py" in many_init 68. list_kwargs = {'child_relation': cls(*args, **kwargs)} 

Exception Type: TypeError at /api/v1/accounts/ 
Exception Value: __init__() takes exactly 1 argument (5 given) 

Edit ** ho scelto di ignorare la funzione di creare sul viewset e includere l'oggetto nella richiesta, in modo che può essere convalidato, ma poi, il serializzatore cerca di inserire un nuovo oggetto per il modello Account. Come posso prevenire questo comportamento? Ho provato a impostare il serializzatore sulla classe PhysicianSerializer come read_only, ma django tenta di memorizzare il modello con un user_id null. Come posso salvare un modello senza provare a inserire anche un oggetto correlato?

risposta

3

Il problema qui è che con i serializzatori nidificati, il framework Django REST si aspetta che l'input e l'output siano una rappresentazione annidata. DRF convaliderà automaticamente l'input per assicurarsi che corrisponda al serializzatore nidificato, consentendo di creare l'oggetto principale e qualsiasi relazione in una singola richiesta.

Si sta cercando di avere un output nidificato con un input PrimaryKeyRelatedField. Questo è molto comune per coloro che non hanno bisogno di creare relazioni nella stessa richiesta, ma utilizzeranno sempre oggetti esistenti nelle loro relazioni. Il modo in cui devi farlo è in pratica prendere in una chiave primaria (proprio come un PrimaryKeyRelatedField) in to_internal_value, ma in uscita un serializzatore in to_representation.Qualcosa di simile (non testato) dovrebbe funzionare

class PrimaryKeyNestedMixin(serializers.PrimaryKeyRelatedField, serializers.ModelSerializer): 

    def to_internal_value(self, data): 
     return serializers.PrimaryKeyRelatedField.to_internal_value(self, data) 

    def to_representation(self, data): 
     return serializers.ModelSerializer.to_representation(self, data) 

si avrebbe bisogno di utilizzare questo come un mixin sul serializzatore nidificato, AccountSerializer nel tuo caso, e dovrebbe fare quello che stai cercando.

+0

e questo è valido per tutti i serializzatori che sono un fk su un altro serializzatore? Sembra un approccio strano per una situazione abbastanza normale. se hai un