单个Django模型,多张表?

8 投票
2 回答
6427 浏览
提问于 2025-04-17 00:13

我知道这个问题之前有人问过,但我希望能得到一个“更好”的答案(不涉及在运行时修改类属性)。这个问题是:

单个Django模型,多个表?

我也遇到了同样的问题 - 我在寻找一个类似于回答者第一个回复的解决方案,但希望它能真正有效。如果没有更好的解决办法,能不能有人评论一下回答者的解决方案有多可靠?我觉得在更改数据库名称和查询数据库之间的延迟可能会导致返回错误表中的结果:

查询1:更改名称

查询2:再次更改名称

查询1:获取结果(但使用的是查询2中的错误名称)

编辑:这个模型是打算用于大约15个表的 - 所以继承不太实际,因为每次都需要一个新的模型名称。

谢谢

附言:如果这不是询问问题详细信息的正确方式,我表示歉意。

2 个回答

0

这是一个非常有用的代码片段。

from django.db import models
shard_tables = {}
class ShardMixin():
    @classmethod
    def shard(cls, id=None):
        def get_ext(id):   # the multi tables rule
            return str(id % 100)  
        ext = get_ext(id)
        _db_table = "%s%s" % (cls._meta.db_table , ext)  # your table name
        if _db_table not in shard_tables:
            class Meta:
                db_table = _db_table
            attrs = {
                '__module__': cls.__module__,
                'Meta': Meta,
            }
            shard_tables[_db_table] = type("%s%s" % (cls.__name__, ext), (cls, ), attrs)
        return shard_tables[_db_table]

class User(models.Model, ShardMixin):
    username = models.CharField(max_length=255, verbose_name=" username")
    class Meta:
        abstract = True
        db_table = "user_"

users = User.shard(id=3).objects.values()
print(users)
2

如果你想要一个动态表格和数据库可以互换的模型,而且希望它的表现比你提到的问题中的更好,你可以使用一些简单的方法或属性:

import copy

class MyModel(models.Model):
    # anything
    @property
    def table_name(self):
        return self._meta.db_table

    @table_name.setter
    def table_name(self, value):
        new_meta = copy.copy(self._meta)
        new_meta.db_table = value
        self._meta = new_meta

    @classmethod
    def set_qs_for_table(cls, qs, table):
        my_class = copy.copy(cls)
        my_options = copy.copy(my_class._meta)
        my_class._meta = my_options
        qs.model = my_class

你可以试试这样的做法...

这里提到的复制部分是为了避免不同模型之间共享选项带来的风险。我花了一些时间才找到这个解决方案的这一部分,但其他的看起来既简单又好用。

当然,一旦在Python代码中,你可以使用

qs = MyClass.objects.all()
MyClass.set_qs_for_table(qs, "this_table")
my_instance = qs[0]
my_instance.table_name = "that_table"
my_instance.save(using="this_db")

撰写回答