我一直在尝试找出基于独立但相同的模型自动创建多个SQL表的最佳方法,所有这些都基于相同的基类。我基本上是用不同的组创建伪留言板或墙,我希望每个组都有自己的db_table的帖子,每个帖子包含用户id,时间戳等。
我的第一个想法是有一个基类的帖子,只是包括一个字段的组名,但我认为这将是不好的做法。我的理由是,一个包含所有组的每个Post的表会变得非常大(理论上),并且会减慢过滤速度,而且当我可以为每个组使用单独的表并跳过此字段时,组名的额外字段从长远来看会浪费内存。
我也考虑过使用具有多对一关系的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)
我发现自己在应用程序中遇到了RuntimeError:冲突模型,这可能是我应得的。然后我想我需要的是这样的东西:
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上,虽然我找到了创建抽象基类和子类来解决类似问题的指南,但我没有看到为我创建函数来解决这个问题的方法。我最终在这里手工为每个组创建一个子类。如果有什么方法可以让这个过程自动化,我很乐意听听。此外,如果我不只是去一个数据库表包含每个帖子是愚蠢的,我也想知道这一点,为什么!或者是否有更好的方法来实现这种系统。如果这个问题之前已经回答过了,我很抱歉,我真的找不到它了。
谢谢!
使用单个表并不是个坏习惯。额外的内存是最小的,在现代系统上应该不是问题。您也不应该担心性能,过早的优化(不包括实际的系统设计)被认为是不好的做法,但是如果遇到性能问题,您总是可以在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...])
把代码放在Posts
类之后的models.py
中,所有的类都会为你创建。它们可以像普通类一样被使用:Django甚至不知道你动态创建了这个类。虽然您的Meta
类不继承Posts.Meta
,您的元设置仍然应该保留。
已在Django 1.4中测试过。
试试这样做
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