Сериализатор django динамически устанавливает атрибуты - PullRequest
0 голосов
/ 31 августа 2018

Можно ли динамически устанавливать атрибуты для Serializer динамически?

CONTEXT_CLASSES = dict()
contexts = ['EmailContext', 'SmsMessageContext',...]

for ctx in contexts:
    Ctx_class = getattr(sys.modules[__name__], f'{ctx}Serializer')
    ctx_class_instance = Ctx_class(many=False, required=False, read_only=True)
    attr_name = ctx.split('Context')[0].lower() + '_ctx'
    CONTEXT_CLASSES[attr_name] = ctx_class_instance


class ContextContainerSerializer(serializers.ModelSerializer):

    for attr_name, instance in CONTEXT_CLASSES.items():
        # set attributes somehow

    class Meta:
         ...
         fields = tuple([value[0] for value in CONTEXT_CLASSES.values()])

Помещение в __init__ не будет работать, так как class Meta выполняется перед ним и устанавливает переменную fields, которая уже содержит имена атрибутов и вызовет django.core.exceptions.ImproperlyConfigured.

1 Ответ

0 голосов
/ 31 августа 2018

Вы можете установить атрибуты при инициализации

class ContextContainerSerializer(serializers.ModelSerializer):

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

        # should be able to access "self.Meta"
        for attr_name, instance in CONTEXT_CLASSES.items():
            setattr(self.Meta, attr_name, instance)

Или для более динамического управления / паттернов используйте metaclasses

from rest_framework.serializers import SerializerMetaclass


class ExtraContextMetaclass(SerializerMetaclass):
     def __new__(cls, name, bases, dct):
         """

         :param name:  name of class to be created
         :param bases: sub classes
         :param dct: other attributes
         :return:
         """

         new_class = super().__new__(cls, name, bases, dct)
         new_class.Meta = cls.dynamic_meta(new_class.Meta)
         return new_class

     @classmethod
     def dynamic_meta(cls, old_meta):
         # create a class called "Meta"
         new_meta = type('Meta', (), dict(fields=None))

         # copy over required attributes from original Meta
         new_meta.model = old_meta.model
         new_meta.fields = list(old_meta.fields[:])

         # set new attributes dynamically 
         for attr_name, instance in CONTEXT_CLASSES.items():
             setattr(new_meta, attr_name, instance)

         return new_meta


class ContextContainerSerializer(serializers.ModelSerializer, 
                                 metaclass=ExtraContextMetaclass):

    class Meta:
        model = Model
        fields = ['some_field']
...