2015-10-14 15 views
8

Ho alcuni modelli come questi:django quadro riposo classe astratta serializer

class TypeBase(models.Model): 
    name = models.CharField(max_length=20) 
    class Meta: 
     abstract=True 

class PersonType(TypeBase): 
    pass 

class CompanyType(TypeBase): 
    pass 

Avendo questo, voglio creare un solo serializzatore che contiene tutti questi tipi di campi (serializzazione, deserializzazione, aggiornare e salvare).

Per essere più specifici, desidero solo un serializzatore (TypeBaseSerializer) che stampi il Dropdown sull'interfaccia utente, serializzi la risposta JSON, deserializzi in post e lo salvi per tutti i miei tipi basati.

Qualcosa di simile a questo:

class TypeBaseSerializer(serializers.Serializer): 
    class Meta: 
     model = TypeBase 
     fields = ('id', 'name') 

E 'possibile?

+0

Questa discussione utile anche sul serializzatori sottoclasse: https://github.com/tomchristie/django-rest-framework/issues/1926 – PhoebeB

risposta

7

can't use a ModelSerializer con un modello di base astratto. Da restframework.serializers:

if model_meta.is_abstract_model(self.Meta.model): 
     raise ValueError(
      'Cannot use ModelSerializer with Abstract Models.' 
     ) 

ho scritto una funzione serializer_factory per un problema simile:

from collections import OrderedDict 
from restframework.serializers import ModelSerializer 
def serializer_factory(mdl, fields=None, **kwargss): 
""" Generalized serializer factory to increase DRYness of code. 

:param mdl: The model class that should be instanciated 
:param fields: the fields that should be exclusively present on the serializer 
:param kwargss: optional additional field specifications 
:return: An awesome serializer 
""" 

    def _get_declared_fields(attrs): 
     fields = [(field_name, attrs.pop(field_name)) 
        for field_name, obj in list(attrs.items()) 
        if isinstance(obj, Field)] 
     fields.sort(key=lambda x: x[1]._creation_counter) 
     return OrderedDict(fields) 

    # Create an object that will look like a base serializer 
    class Base(object): 
     pass 

    Base._declared_fields = _get_declared_fields(kwargss) 

    class MySerializer(Base, ModelSerializer): 
     class Meta: 
      model = mdl 

     if fields: 
      setattr(Meta, "fields", fields) 

    return MySerializer 

È quindi possibile utilizzare la fabbrica per la produzione di serializzatori in base alle esigenze:

def typebase_serializer_factory(mdl): 
    myserializer = serializer_factory(
     mdl,fields=["id","name"], 
     #owner=HiddenField(default=CurrentUserDefault()),#Optional additional configuration for subclasses 
    ) 
    return myserializer 

Ora instanciate diversi serializzatori di sottoclassi:

persontypeserializer = typebase_serializer_factory(PersonType) 
companytypeserializer = typebase_serializer_factory(CompanyType) 
+0

Questa soluzione sembra funzionare per il mio problema, ma c'è un modo per essere meno generico? Penso che nel mio caso conoscerò sempre i campi dei miei tipi e ho solo bisogno di installarlo dinamicamente. –

+0

È possibile creare due serializzatori per le sottoclassi. Ero in una situazione con 7 sottoclassi e diverse modalità di serializzazione, quindi essere generico era cruciale. –

2

Penso che il seguente approccio sia più pulito. È possibile impostare il campo "abstract" su true per il serializzatore di base e aggiungere la logica comune per tutti i serializzatori secondari.

class TypeBaseSerializer(serializers.Serializer): 
    class Meta: 
     model = TypeBase 
     fields = ('id', 'name') 
     abstract = True 

    def func(...): 
    # ... some logic 

E quindi creare serializzatori secondari e utilizzarli per la manipolazione dei dati.

class PersonTypeSerializer(TypeBaseSerializer): 
    class Meta: 
     model = PersonType 
     fields = ('id', 'name') 


class CompanyTypeSerializer(TypeBaseSerializer): 
    class Meta: 
     model = CompanyType 
     fields = ('id', 'name') 

Ora è possibile utilizzare entrambi questi serializzatori normalmente per ogni modello.

Ma se si desidera realmente disporre di un serializzatore per entrambi i modelli, creare anche un modello contenitore e un serializzatore. Che è molto più pulito :)

0

Basta iterazione un po 'più di @ adki risposta:

  1. è possibile saltare modello per TypeBaseSerializer;
  2. I serializzatori derivati ​​
  3. possono fare riferimento a TypeBaseSerializer.meta.fields, quindi è necessario cambiarli in un'unica posizione.
class TypeBaseSerializer(serializers.Serializer): 
    class Meta: 
     fields = ('id', 'name', 'created') 
     abstract = True 

    def func(...): 
    # ... some logic 

class PersonTypeSerializer(TypeBaseSerializer): 
    class Meta: 
     model = PersonType 
     fields = TypeBaseSerializer.meta.fields + ('age', 'date_of_birth') 

class CompanyTypeSerializer(TypeBaseSerializer): 
    class Meta: 
     model = CompanyType 
     fields = TypeBaseSerializer.meta.fields