单个Django模型,多个表?

8
我知道之前曾经有人问过同样的问题,但我希望能得到一个更好的答案(不需要在运行时修改类属性)。这是问题:

单个Django模型,多个表?

我也遇到了同样的问题 - 我正在寻找类似回答者第一次回复的解决方案,但实际上能够起作用。如果没有更好的解决方案,有人可以评论一下回答者的解决方案有多可靠吗?在我看来,更改数据库名称和查询数据库之间的延迟可能会导致返回错误表的结果:

查询1:更改名称

查询2:再次更改名称

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

编辑:该模型旨在用于约15个表格 - 因此继承是不切实际的,因为每次都需要新的模型名称。

谢谢

P.S. 如果这不是询问问题详细信息的正确方式,请原谅我。


为什么您更喜欢使用不同的表而不是不同的模型? - christophe31
所以你更喜欢处理15个字符串组合,而不是15个可能更明确的行为模型?我正在给你写一个答案。 - christophe31
是的,因为页面上明确引用了字符串组合。拥有一个能够访问每个表的通用模型是一个好的解决方案,并且可以使代码易读、有组织。如果最终引用的是同一件事情,那么非常明确是没有意义的。 - user908085
事实上,表名和数据库似乎是存储在类级别而不是实例级别...因此,有点像静态属性,做您想要的事情似乎很危险。您可能需要创建一个类装饰器。因此,通过继承和装饰器,每个类只需要3行代码。 - christophe31
1
正确 - 在Django中,表名取自类名,除非用户在Meta子类下定义了一个db_table名称。更改这个将会改变数据库访问的表。这是上一个主题的解决方案,但是动态更改可能会导致问题。但即使有元类和名称,每个表仍然需要3行代码。这相当于额外的45行代码,这本身并不算过多。只是似乎应该有更好的解决方案。 - user908085
显示剩余2条评论
2个回答

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")

这里的问题在于共享选项 - 但实际上在django后台进行共享,并且基于表名。以此为例: table = getModel(request.POST["table"]) table2 = getModel(request.POST["table"]) - user908085
自从我使用复制并使用新的选项对象而不是编辑共享对象后,它们不再共享。 - christophe31
尝试我的解决方案,我保证它不再是共享的。我的setter很长,以便创建实例级别的值而不是类级别的共享值。 - christophe31
实际上它看起来是共享的,因为_meta是一个实例。如果我们修改这个实例,它将影响该实例的所有用户。在这里,我使用了一个新的内存地址,所以它不是共享的。 - christophe31
1
这个解决方案不起作用 - db_table属性实际上没有被修改(这很奇怪,我没有预料到)。至于共享 - Django在内部处理此问题 - 浅拷贝只会覆盖最后一个类 - 试一下。 - user908085
显示剩余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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接