2016-11-07 14 views
0

Я использую mongoengine с картой отдыха django. Моя модель:Добавить динамическое свойство с классом в метаклассе Python

import mongoengine as mongo 
class Plan(mongo.Document): 
    slug = mongo.StringField(max_length=255, primary_key=True) 
    subplans = mongo.ListField(mongo.EmbeddedDocumentField('self')) 

Я нужен сериалайзер, что быть выглядит следующим образом:

class PlanSerializer(serializers.DocumentSerializer): 
    subplans = PlanSerializer(many=True, required=False) 

    class Meta: 
     model = Plan 

Но что неправильно для Python. Поэтому я использую метакласс для добавления subplans поля динамически:

class AddSubplanAttrMetaclass(type): 
    def __new__(cls, name, bases, dct): 
     # this code is incorrect because PlanSerializer not in globals 
     class_obj = globals()[name] 
     dct['subplans'] = class_obj(many=True, required=False) 
     return super(AddSubplanAttrMetaclass, cls).__new__(cls, name, bases, dct) 

class PlanSerializer(serializers.DocumentSerializer, metaclass=AddSubplanAttrMetaclass): 

    class Meta: 
     model = Plan 

Как я может установить PlanSerializer класс свойство внутри __new__ метода метакласса?

ответ

0

Проблемы у вас есть есть то, что при попытке использовать либо строку subplans = PlanSerializer(many=True, required=False) и при попытке с метаклассом, линия class_obj = globals()[name] когда сама ваша PlanSerializerclass еще не была определена. (Проверьте мой ответ на How is super() in Python 3 implemented?)

Правильного способом сделать это в метаклассе бы назвать суперкласс нового первым - что возвращает вам реальный объект класса, а затем вызвать этот объект - то вдоль:

class AddSubplanAttrMetaclass(type): 
    def __new__(metacls, name, bases, dct): 
     # this code is incorrect because PlanSerializer not in globals 
     class_obj = super(AddSubplanAttrMetaclass, cls).__new__(metacls, name, bases, dct) 
     class_obj.subplans = class_obj(many=True, required=False) 
     return class_obj 

Но это не обязательно и все еще может иметь проблемы - поскольку не все инициализация класса завершена, пока вы все еще находитесь в методах __new__ (или даже __init__) метакласса. Например, если метод __init__ из PlanSerializer будет использовать super, этот вызов завершится с ошибкой - super может использоваться только после полной инициализации класса.

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

class PlanSerializer(serializers.DocumentSerializer): 
    class Meta: 
     model = Plan 

PlanSerializer.subplans = PlanSerializer(many=True, required=False) 

я, наверное, сказал, потому что это не будет работать, если Монго нужен атрибут должен быть установлен при инициализации самого класса - если это так, то вы можете попытаться прибегнуть к объекту дескриптора. Дескриптор - это просто объект, который реализует метод __get__, как показано ниже. Обычно это делается с декоратором @property, но это не будет работать для атрибутов уровня класса, которые вам нужны для этого случая.

class PlanSerializer(serializers.DocumentSerializer): 
    class Subplans(object): 
     serializer = None 
     def __get__(self, instance, owner): 
      if not self.serializer: 
       self.serializer = PlanSerializer(many=True, required=False) 
      return self.serializer 
    subplans = Subplans() 

    class Meta: 
     model = Plan 

Таким образом использование вызова в Subplans класса задерживается, когда он фактически используется, вместо времени разбора тела класса, и он должен работать.