将动态生成的模型添加到Django项目中的models.py

时间:2015-07-20 18:23:19

标签: python django django-models

我正在基于抽象模型类AbstractAttr和普通模型生成Django模型(假设Foo)。

我希望我的foo/models.py看起来像这样:

from bar.models import Attrs
# ...
class Foo(models.Model):
    ....
    attrs = Attrs()

在模仿字段的Attrs类中,我有一个contribute_to_class,使用type()生成所需的模型。生成的模型c称为FooAttr。

一切正常。如果我迁移,我会看到FooAttr出现在正确的表格中。

除了一件事。

我希望能够from foo.models import FooAttr。不知何故,我生成的FooAttr类没有绑定到生成它的models.py文件中。

如果我将models.py更改为:

class Foo(models.Model):
    # ...

FooAttr = generate_foo_attr_class(...)

它有效,但这不是我想要的(例如,这会强制dev猜测生成类名)。

我想要的是什么,在第一个例子中有点像定义类并将它绑定到特定的models.py模块?

该项目(前阿尔法)在这里(在开发分支): https://github.com/zostera/django-mav

一些相关代码:

def create_model_attribute_class(model_class, class_name=None, related_name=None, meta=None):
    """
    Generate a value class (derived from AbstractModelAttribute) for a given model class
    :param model_class: The model to create a AbstractModelAttribute class for
    :param class_name: The name of the AbstractModelAttribute class to generate
    :param related_name: The related name
    :return: A model derives from AbstractModelAttribute with an object pointing to model_class
    """

    if model_class._meta.abstract:
        # This can't be done, because `object = ForeignKey(model_class)` would fail.
        raise TypeError("Can't create attrs for abstract class {0}".format(model_class.__name__))

    # Define inner Meta class
    if not meta:
        meta = {}
    meta['app_label'] = model_class._meta.app_label
    meta['db_tablespace'] = model_class._meta.db_tablespace
    meta['managed'] = model_class._meta.managed
    meta['unique_together'] = list(meta.get('unique_together', [])) + [('attribute', 'object')]
    meta.setdefault('db_table', '{0}_attr'.format(model_class._meta.db_table))

    # The name of the class to generate
    if class_name is None:
        value_class_name = '{name}Attr'.format(name=model_class.__name__)
    else:
        value_class_name = class_name

    # The related name to set
    if related_name is None:
        model_class_related_name = 'attrs'
    else:
        model_class_related_name = related_name

    # Make a type for our class
    value_class = type(
        str(value_class_name),
        (AbstractModelAttribute,),
        dict(
            # Set to same module as model_class
            __module__=model_class.__module__,
            # Add a foreign key to model_class
            object=models.ForeignKey(
                model_class,
                related_name=model_class_related_name
            ),
            # Add Meta class
            Meta=type(
                str('Meta'),
                (object,),
                meta
            ),
        ))

    return value_class


class Attrs(object):
    def contribute_to_class(self, cls, name):
        # Called from django.db.models.base.ModelBase.__new__
        mav_class = create_model_attribute_class(model_class=cls, related_name=name)
        cls.ModelAttributeClass = mav_class

2 个答案:

答案 0 :(得分:1)

我看到你在models.py中创建模型,所以我认为你应该能够将它添加到模块的全局变量中。怎么样:

new_class = create_model_attribute_class(**kwargs)
globals()[new_class.__name__] = new_class
del new_class  # no need to keep original around

答案 1 :(得分:0)

感谢所有人考虑这一点。我在GitHub上更新了项目的源代码,并添加了更多测试。见https://github.com/zostera/django-mav

由于模型的实际生成是在foo/models.py之外完成的(它发生在mav/models.py中,因此在模板上似乎无法将模型链接到foo/models.py。此外,在重新考虑此之后,它似乎是Python的自动化(显式更好,没有魔力)。

所以我的新策略是使用简单的函数,装饰器以便于添加mav,并将生成的模型链接到mac/attrs.py,因此我可以普遍from mav.attrs import FooAttr。我还将生成的类链接到Foo模型,如Foo._mav_class。

(在此评论中,Foo当然是用作我们想要将模型属性值添加到的示例模型。