如何从插件猴子补丁南处理模型?

2024-09-30 14:32:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在制作一个带有插件的django站点。一个简单的插件(甚至是一个主插件)。在

插件要避免外挂的依赖性,要避免外挂应用程序之间的依赖性。在

因为主应用程序已经有一个south管理,所以也有所有插件,所以我不能在这些模块的设置中更改迁移目录。在

那么,如何从另一个south应用程序中monkey补丁south应用程序模型呢?在

附言:我是法国人,如果你发现任何错误,请随时纠正我的问题,如果我不清楚,可以问任何问题。在

编辑:我添加了一个关于django迁移的答案。


Tags: 模块django模型目录插件应用程序编辑站点
3条回答

当我迁移到django1.7和django迁移时,我发布了一个新的答案,解决方案并不明显,我不得不创建自己的迁移类来向远程表添加外键。在

from django.db.migrations import AddField

class AddRemoteField(AddField):
    def __init__(self, remote_app, *args, **kwargs):
        super(AddRemoteField, self).__init__(*args, **kwargs)
        self.remote_app = remote_app

    def state_forwards(self, app_label, *args, **kwargs):
        super(AddRemoteField, self).state_forwards(self.remote_app, *args, **kwargs)

    def database_forwards(self, app_label, *args, **kwargs):
        super(AddRemoteField, self).database_forwards(
            self.remote_app, *args, **kwargs)

    def database_backwards(self, app_label, *args, **kwargs):
        super(AddRemoteField, self).database_backwards(
            self.remote_app, *args, **kwargs)

然后我制作了一个迁移文件:

^{pr2}$

看在基督的份上,不要用蒙克斯补丁。使用继承,这就是它的目的。在

只需让需要处理更多字段的插件扩展现有模型,然后使用多种技术中的一种来获得模型的最专业的类实例。为了实现这一点,我使用下面的类作为所有将以这种方式使用的类的mixin。在

class Specializable(object):

    @classmethod
    def all_classes(selfclass):
        """ Returns the class this is called on, plus its known subclasses """
        subs = selfclass.__subclasses__()
        subs.insert(0, selfclass)
        return subs

    def get_sub_instance(self):
        """ Gets concrete instance of object of deepest subtype which has its ancestor link pointing to this object (depth first search behaviour). """
        selftype = type(self)
        for klass in selftype.__subclasses__():
            this_ptr_name = klass._meta.get_ancestor_link(selftype).name
            try:
                sub = klass.objects.get(**{this_ptr_name: self})
                subsub = sub.get_sub_instance()
                if subsub: return subsub
                else: return sub
            except ObjectDoesNotExist:
                pass

    @classmethod
    def new_with_translator(selfclazz, name):
        def new(cls, *args, **kwargs):
            selfclazz.create_subclass_translator(cls, install = name)
            return models.Model.__new__(cls, *args, **kwargs)

        return new

    @classmethod
    def create_subclass_translator(selfclazz, Baseclass, install=None):
        """ Creates a classmethod object for installation on Baseclass,
        which acts as a factory for instances of subclasses of Baseclass,
        when called on that subclass. The factory takes as input an instance
        of a subclass (old_instance), and a dictionary of properties with which
        to initialise the new instance. It also installs the base class instance
        of old_instance as the base instance for the new instance. All three will
        share the same pk.

        if install is set, this will also install the function on Baseclass under
        that name if it does not have a property of that name. """

        def create_from_other_instance(selfclass, old_instance, properties):
            """ Creates an instance of this class using properties and old_instance.
            In particular, it will try to re-use the superclass instance of old_instance.
            properties should contain all of the fields for this class (but need not include the superclass values)
            This *must* be called on a subclass of the baseclass - it will give odd results if called on the baseclass itself.
            This will NOT delete old_instance and it will NOT SAVE the object created - the caller is responsible for
            those things."""

            if selfclass is Baseclass: raise TypeError("This method cannot be used on the base class")

            ancestor_link = selfclass._meta.get_ancestor_link(Baseclass).name
            properties.update({ancestor_link: getattr(old_instance,ancestor_link)})
            for f in get_model_fields(Baseclass):
                val = getattr(old_instance, f)
                if val and not properties.get(f):
                    properties[f] = val

            return selfclass(**properties)

        new_method = classmethod(create_from_other_instance)

        if install and not hasattr(Baseclass, install):
            setattr(Baseclass, install, new_method)

        return new_method

下面是它的使用示例,来自我的一些代码:

^{pr2}$

如您所见,Specializable类需要知道它们的方法可能被子类覆盖,并进行适当的编码。在

如果你的插件知道子类之间的关系,那么它可以使用一些技巧,比如在它知道的子类中搜索同一个模型id,在超类实例出现时得到相应的子类模型实例。在

目前,我最好的解决方案是在插件中创建自己的迁移文件(这意味着在迁移文件的models字典中添加表)。在

我将在稍后的下一次迁移中看到是否所有模型都将自动遵循。在

在我的新迁移文件中:

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.add_column(u'remoteapp_model', 'fieldname',
                      self.gf('django.db.models.fields.related.ForeignKey',
                      (to=orm["my_plugin.MyModel"], default=None, null=True, blank=True),
                      keep_default=False)


    def backwards(self, orm):
        db.delete_column(u'remoteapp_model', 'fieldname')

    # for models, you may want to copy from a previous migration file
    # and add from the models of the main application the related tables
    models = {} 

在我的模型中:

^{pr2}$

相关问题 更多 >