【问题标题】:Add dynamic property with class in Python metaclass在 Python 元类中添加带有类的动态属性
【发布时间】:2016-11-07 12:12:49
【问题描述】:

我将 mongoengine 与 django rest 框架一起使用。我的模型:

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__ 方法内的属性?

【问题讨论】:

    标签: python django-rest-framework mongoengine metaclass


    【解决方案1】:

    您遇到的问题是,当您尝试使用该行时 subplans = PlanSerializer(many=True, required=False) 并且在尝试使用元类时,当您的 PlanSerializerclass 本身尚未定义时,class_obj = globals()[name] 行。 (在How is super() in Python 3 implemented?查看我的答案)

    在元类中执行此操作的正确方法是首先调用超类的 new - 返回实际的类对象,然后调用该对象 - 类似的东西:

    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__)方法中时,并非所有类初始化都已完成。例如,如果PlanSerializer__init__ 方法本身将使用super,则该调用将失败 - super 只能在类完全初始化后使用。

    但是,您根本不需要元类 - 您可能只需将 subplans 属性设置为描述符 - 并懒惰地检索该属性。

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

    我说可能是因为如果 Mongo 在初始化类本身时需要设置属性,这将不起作用 - 如果是这种情况,您可以尝试使用描述符对象。描述符只是一个实现__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类的调用的使用被延迟到实际使用的时候,而不是解析类体的时候,它应该可以工作。

    【讨论】:

      猜你喜欢
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 2017-01-13
      • 1970-01-01
      • 2019-04-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多