【问题标题】:Django creating multiple tables/model classes from same base class with factory functionDjango使用工厂函数从同一个基类创建多个表/模型类
【发布时间】:2014-06-21 00:35:13
【问题描述】:

我一直在尝试找出基于不同但相同的模型自动创建多个 SQL 表的最佳方法,这些模型都基于相同的基类。我基本上是在创建具有不同组的伪留言板或墙,我希望每个组都有自己的帖子 db_table,每个帖子都包含用户 ID、时间戳等。

我的第一个想法是拥有一个 Posts 基类并只包含一个组名称字段,但我认为这是不好的做法。我的理由是,包含所有组的每个帖子的一张表会变得非常大(无论如何理论上)并减慢过滤速度,而且从长远来看,组名称的额外字段会浪费内存,因为我可以分开每组表并跳过此字段。

我也考虑过使用具有多对一关系的 ForeignKey,但据我所知,这具有相同的缺点。我这样想有错吗?或者这些尺寸问题真的不是问题吗?

所以我的下一个想法是让 Posts 成为一个抽象类,然后根据每个 Group 创建子类。这就是我最终所做的。但是,我发现自己不得不一遍又一遍地复制和粘贴代码,并且每次都更改类名。这对我来说感觉非常不合 Python。是这样的:

class Posts(models.Model):
    timestamp = models.DateTimeField(auto_now_add=True, unique=False)
    user_id = ...
    #etc.
    #
    class Meta:
        abstract = True


class GroupA(Posts):
    class Meta(Posts.Meta):
        db_table = 'groupa_board'

class GroupB(Posts):
    class Meta(Posts.Meta):
        db_table = 'groupb_board'

class GroupC...etc.

我真正想要的是一个工厂函数来为我做这件事。我试过这样的事情:

def makeBoard(group):
    class Board(Posts):
        class Meta(Posts.Meta):
            db_table = group
    return board        #note I tried with and without this line

然后我使用组列表运行了一个简单的 for 循环。

for group in groups:
    makeBoard(group)

我发现自己遇到了运行时错误:应用程序中的模型冲突,这可能是我应得的。所以我想我需要的是这样的:

def makeBoard(group):
    class group(Posts):      #***group here being a variable, not the class name
        class Meta(Posts.Meta):
            db_table = '%s' % group #maybe issues here too, but the table
    return group                    #name is not that important if the class
                                    #name works

但我不知道如何进行这项工作!有没有办法将变量从列表传递到类名?

无论如何,如果你还在我身边,我很感激。我整天都在使用stackoverflow,虽然我找到了创建抽象基类和子类以解决类似问题的指南,但我没有看到一种方法来创建一个函数来为我做这件事。我最终选择了这里,只是手动为每个组创建了一个子类。如果有办法自动化这个过程,我很想听听。

另外,如果我不只是使用一个包含每个帖子的数据库表而变得愚蠢,我也想知道这一点,以及为什么!或者,如果有更好的方法来完全实现这种系统。抱歉,如果之前已经回答过,我真的找不到。

谢谢!

【问题讨论】:

    标签: python mysql sql django


    【解决方案1】:

    使用单个表不是坏习惯。在现代系统上,额外的内存是最小的,这不应该是一个问题。你也不应该担心性能,过早的优化(不包括实际的系统设计)被认为是不好的做法,但如果你遇到性能问题,你总是可以在 group 列上指定一个索引:

    group = models.CharField(max_length=100, db_index=True)
    

    这并不是说它是最好的选择,或者你的方法不好。此外,完全可以使用 type() 内置函数动态创建模型。动态创建模型和创建其他类的唯一区别是您必须专门传递__module__ 属性。您可以通过以下方式为Posts 创建子类:

    def fabric(names, baseclass=Posts):
        for name in names:
            class Meta:
                db_table = '%s_table' % name.lower()
            attrs = {'__module__': baseclass.__module__, 'Meta': Meta} 
            # specify any other class attributes here. E.g. you can specify extra fields:
            attrs.update({'my_field': models.CharField(max_length=100)})
            newclass = type(str(name), (baseclass,), attrs)
            globals()[name] = newclass 
    
    fabric(['GroupA', 'GroupB', 'GroupC', etc...])
    

    将该代码放在models.pyPosts 类之后,所有类都会为您创建。它们可以以任何方式使用普通类:Django 甚至不知道你动态创建了这个类。尽管您的 Meta 类不继承自 Posts.Meta,但您的元设置仍应保留。

    用 Django 1.4 测试。

    【讨论】:

    • 太棒了!在 1.8 中完美运行。并感谢您的解释。我从没想过使用索引,尽管现在我想这也能解决我的问题。无论如何,我已经用上面的代码清理了我的代码。它确实做到了我的想法。很高兴了解 module 属性,我必须记住这一点。再次感谢!
    【解决方案2】:

    这样试试

    import app.models as group_models
    from django.db.models.base import ModelBase
    
    def fabric(group):
        for item in dir(group_models):
            c = getattr(group_models, item)
            if type(c) is ModelBase:
                if c._meta.db_table == '%s_table' % group:
                    return c
        return None
    

    【讨论】:

    • 嗯.. 似乎对我没有影响。你能解释一下这是如何工作的吗?不是假设我已经制作了模型吗?无论如何,我会玩弄它和 ModelBases 一些。谢谢!
    • 我没有正确理解这个问题。我想没有任何简单的方法可以满足您的需要。
    • 但是,如果不需要每个模型都有外部表,您可以尝试使用 ModelManager。我的意思是你可以有一些字段来识别组,并在结构中重写 ModelManager。
    • 别担心!实际上 knbk 在这里提供了一个很好的答案,一定要看看。
    猜你喜欢
    • 2020-06-01
    • 2022-01-27
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-07
    • 2020-02-22
    • 1970-01-01
    相关资源
    最近更新 更多