2016-01-27 5 views
2

Я определил сериализаторы, как показано ниже. Я использую mixin для изменения полей отображения на лету.Django Rest Framework: Вложенные поля динамической модели Serializer

class SerializerTwo(serializers.ModelSerializer): 

    class Meta: 
     model = Two 
     fields = ('name', 'contact_number') 

class SerializerOne(DynamicFieldsModelSerializer, serializers.ModelSerializer): 
    another_field = SerializerTwo() 

    class Meta: 
     lookup_field = 'uuid' 
     model = One 
     fields = ('status', 'another_field',) 

Теперь то, что я хочу сделать, динамически передать (на лету), что все поля будут использоваться с SerializerTwo, как я делаю для SerializerOne.

То, как я делаю это для SerializerOne является:

# where fields=('status') 
SerializerOne(queryset, fields=fields) 

Есть ли способ, с помощью которого можно добавлять поля из SerializerTwo к выше инициализации Serializer.

# where fields=('status', 'name') name from SerializerTwo 
# the double underscore notation does not work here for fields, so another_field__name cannot be used as well 
SerializerOne(queryset, fields=fields) 

ответ

0

Я использую следующий способ реализации так называемого Nested Serializer Dynamic Model Fields.

class SerializerTwo(serializers.ModelSerializer): 
    fields_filter_key = 'two_fields' 
    class Meta: 
     model = Two 
     fields = ('name', 'contact_number') 

class SerializerOne(DynamicFieldsModelSerializer, serializers.ModelSerializer): 
    fields_filter_key = 'one_fields' 
    another_field = serializers.SerializerMethodField() 

    class Meta: 
     lookup_field = 'uuid' 
     model = One 
     fields = ('status', 'another_field',) 

    def get_another_field(self, obj): 
     another_filed_serializer = SerializerTwo(obj.another_field, 
               context=self.context) 
     return another_filed_serializer.data 

и мы делаем некоторые модификации DynamicFieldsModelSerializer

class DynamicFieldsModelSerializer(serializers.ModelSerializer): 
    def __init__(self, *args, **kwargs): 
     super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) 

     if 'request' not in self.context or not self.fields_filter_key: 
      return 
     fields = self.context['request'].query_params.get(self.fields_filter_key) 
     if fields: 
      fields = fields.split(',') 
      allowed = set(fields) 
      existing = set(self.fields.keys()) 
      for field_name in existing - allowed: 
       self.fields.pop(field_name) 

поэтому последняя проблема в том, как организовать URL, написать GET URL вроде этого:

domain/something?one_fields=name,contact_number&two_fields=another_field

0

После того, та же проблема, я нашел решение, я надеюсь, что это поможет некоторым людям. Я изменил DynamicFieldsModelSerializer, как определено here

def __init__(self, *args, **kwargs): 
# Don't pass the 'fields' arg up to the superclass 
fields = kwargs.pop('fields', None) 
nested = kwargs.pop('nested', None) 
# Instantiate the superclass normally 
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) 

if fields is not None: 
    # Drop any fields that are not specified in the `fields` argument. 
    allowed = set(fields) 
    existing = set(self.fields.keys()) 
    for field_name in existing - allowed: 
     self.fields.pop(field_name) 

if nested is not None: 
    for serializer in nested: 
     try: 
      nested_serializer = self.fields[serializer] 
     except: 
      logger.warning("Wrong nested serializer name") 
      continue 

     allowed = set(nested[serializer]) 
     existing = set(nested_serializer.fields.keys()) 
     for field_name in existing - allowed: 
      nested_serializer.fields.pop(field_name) 

После этого, Вы можете использовать его как это:

SerializerOne(queryset, nested={"another_field": ["name"]}) 

Вы можете изменить мое решение использовать двойное подчеркивание вместо другого kewyord с Dict, но я хотел отделить обычные поля от вложенного сериализатора.

Она также может быть улучшена, чтобы быть рекурсивным, здесь я только иметь дело с глубины одного вложенного сериализатором

EDIT Я изменил мой код, чтобы использовать двойной синтаксис подчеркивания в конце концов:

def __init__(self, *args, **kwargs): 

    def parse_nested_fields(fields): 
     field_object = {"fields": []} 
     for f in fields: 
      obj = field_object 
      nested_fields = f.split("__") 
      for v in nested_fields: 
       if v not in obj["fields"]: 
        obj["fields"].append(v) 
       if nested_fields.index(v) < len(nested_fields) - 1: 
        obj[v] = obj.get(v, {"fields": []}) 
        obj = obj[v] 
     return field_object 

    def select_nested_fields(serializer, fields): 
     for k in fields: 
      if k == "fields": 
       fields_to_include(serializer, fields[k]) 
      else: 
       select_nested_fields(serializer.fields[k], fields[k]) 

    def fields_to_include(serializer, fields): 
     # Drop any fields that are not specified in the `fields` argument. 
     allowed = set(fields) 
     existing = set(serializer.fields.keys()) 
     for field_name in existing - allowed: 
      serializer.fields.pop(field_name) 

    # Don't pass the 'fields' arg up to the superclass 
    fields = kwargs.pop('fields', None) 
    # Instantiate the superclass normally 
    super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) 

    if fields is not None: 
     fields = parse_nested_fields(fields) 
     # Drop any fields that are not specified in the `fields` argument. 
     select_nested_fields(self, fields) 

Затем вы можете использовать его следующим образом:

SerializerOne(instance, fields=["another_field__name"])