5

我正在制作一个带有插件的 django 站点。每个插件都是一个简单的 django 应用程序,取决于一个主要的(甚至是其他插件)。

虽然应用程序/插件之间的依赖关系对我来说很清楚,但通过猴子补丁添加列(作为插件特定模型的外键)以避免主应用程序依赖于插件应该是可以接受的。

由于主应用程序已经有一个南管理,所以所有插件都有,我无法在这些模块的设置中更改迁移目录。

那么,我如何从其他南方应用程序修补南方应用程序模型?

ps:我是法国人,如果您发现任何错误,请随时纠正我的问题,或者如果我不清楚,请提出任何问题。

编辑:我添加了一个关于我现在如何处理 django 迁移的答案。

4

3 回答 3

4

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

如果所有模型都将自动跟随,我将在下次迁移时看到。

在我的新迁移文件中:

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 = {} 

在我的模型文件中:

from remoteapp.models import RemoteModel
from django.db import models

class MyModel(models.Model):
    pass

models.ForeignKey(MyModel, null=True, blank=True,
                  default=None).contribute_to_class(RemoteModel, 'my_model')
于 2013-05-27T12:43:08.657 回答
1

我在迁移到 django 1.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)

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

from __future__ import unicode_literals

from django.db import models, migrations
from my_app.tools import AddRemoteField
from my_app.models import Client


class Migration(migrations.Migration):

    dependencies = [
        ('anikit', '0002_manual_user_migration'),
    ]

    operations = [
        AddRemoteField(
            remote_app='auth',
            model_name='user',
            name='client',
            field=models.ForeignKey(Client, verbose_name='client',
                                    null=True, blank=True),
            preserve_default=True,
        ),
    ]
于 2014-11-26T23:20:42.110 回答
0

为了基督的爱,不要为此使用猴子补丁。使用继承,这就是它的用途。

只需让需要处理更多字段的插件扩展现有模型,然后使用多种技术之一来获取模型的最专业的类实例。为了实现这一点,我将下面的类用作将以这种方式使用的所有类的 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

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

#KYCable inherits from Model, so must precede Model
class Director(KYCable, models.Model, Specializable, DateFormatter, AdminURL, Supercedable):
    def get_individual(self):
        return self.get_sub_instance().get_individual()

正如你所看到的,Specializable类需要知道它们的方法可能被子类覆盖,并被适当地编码。

如果您的插件知道子类关系,那么它可以使用诸如在它知道的子类中搜索相同模型 id 的技巧,以在出现超类实例时获取相应的子类模型实例。

于 2013-06-01T18:30:41.867 回答