动态模型 Django

3
假设我在一个数据库中有10张表,这些表的名称和数据都不同,但是它们的结构相同。然后我有一张表,用于收集所有表的名称和创建日期。
例如:
PrimaryTable
table_name_1
table_name_2
....
table_name_10

所有表格的结构示例:

class PrimaryTable(models.Model):
    name = models.CharField(db_column='Name', unique=True, max_length=100)
    date = models.CharField(db_column='Date', max_length=100)

    class Meta:
        managed = True
        db_table = 'Primary Table'

    def __str__(self):
        return self.name


class table_name_1(models.Model):
    title = models.TextField(db_column='Title', blank=True, null=True)
    url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True,
                           null=True)
    description = models.TextField(db_column='Description', blank=True, null=True)
    created_at = models.DateTimeField(db_column='Created_at')


class table_name_2(models.Model):
    title = models.TextField(db_column='Title', blank=True, null=True)
    url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True,
                           null=True)
    description = models.TextField(db_column='Description', blank=True, null=True)
    created_at = models.DateTimeField(db_column='Created_at')

我只想创建一个包含所有具有相同结构的表的类,并在从PrimaryTable选择表时调用它。

我不想每次创建表时都使用"python manage.py inspectdb > models.py"。我想在创建新表时立即访问它。


我认为在架构上合理的方式不可能实现。相反,我会尝试通过使用触发器解决mysql中动态创建表的问题,并在表创建时填充您的“元表”。然后,我会基于自定义参数化查询而不是依赖于Django模型来获取表中的数据,因为Django模型不适用于这种动态情况。显然,在数据库级别上动态创建表是一个糟糕的设计,但我假设它是预先存在的。 - Jarek.D
更多关于此类型触发器的信息请参见:https://dev59.com/0VPTa4cB1Zd3GeqPmMOb - Jarek.D
3个回答

2
你可以将模型字段定义为字典形式:
fields = dict(
    title = models.TextField(db_column='Title', blank=True, null=True),
    url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True, null=True),
    description = models.TextField(db_column='Description', blank=True, null=True),
    created_at = models.DateTimeField(db_column='Created_at'),
)

然后,您可以动态地创建并运行与之相关的迁移:
from django.db import connection, migrations, models
from django.db.migrations.executor import MigrationExecutor

def create_table(table_name, model_fields, app_label):
    class Migration(migrations.Migration):
        initial = True
        dependencies = []
        operations = [
            migrations.CreateModel(
                name=table_name,
                fields=[
                    ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ] + [(k, field) for k,field in model_fields.items()],
                options={
                    'db_table': table_name,
                },
            ),
        ]
    executor = MigrationExecutor(connection)
    migration = Migration(table_name, app_label)
    with connection.schema_editor(atomic=True) as schema_editor:
        migration.apply(executor._create_project_state(), schema_editor)

然后您可以使用动态模型创建来访问表中的数据:

from django.db import models

def create_model(name, fields=None, app_label=None, module='', options=None, admin_opts=None):
    """
    Create specified model
    """
    class Meta:
        db_table = name
    if app_label:
        # app_label must be set using the Meta inner class
        setattr(Meta, 'app_label', app_label)
    # Update Meta with any options that were provided
    if options is not None:
        for key, value in options.iteritems():
            setattr(Meta, key, value)
    # Set up a dictionary to simulate declarations within a class
    attrs = {'__module__': module, 'Meta': Meta}
    # Add in any fields that were provided
    if fields:
        attrs.update(fields)
    # Create the class, which automatically triggers ModelBase processing
    model = type(name, (models.Model,), attrs)
    return model

我希望你能大致明白。 我从一个使用Django 2的项目中获取了代码(类似的代码用于根据上传的CSV文件创建表格,每个上传的CSV的列和名称都存储在类似于你的PrimaryTable的表中)。


谢谢,维克托。我发现这非常有用,而且它运行得很好。我还发现,使用makemigrations对使用create_model定义的任何模型进行迁移时,迁移正常工作。手动使用create_table动态构建迁移类是否有很好的理由? - Timothy Bushell

1
你可以将所有具有相同结构的数据放在一个表中,以简化事情。您可以使用管理筛选器和自定义管理器方法一次聚焦于一种类型的信息,这将提供与单独表相同的功能。
class Information(models.Model):
    name = models.CharField(db_column='Name', unique=True, max_length=100)
    date = models.CharField(db_column='Date', max_length=100)
    information_type = models.ForeignKey(InformationType, related_name='information_records')

    def __str__(self):
        return self.name


class InformationType(models.Model):
    title = models.TextField(db_column='Title', blank=True, null=True)
    url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True,
                           null=True)
    description = models.TextField(db_column='Description', blank=True, null=True)
    created_at = models.DateTimeField(db_column='Created_at')

    def __str__(self):
        return self.title

谢谢。这确实对我有帮助,但我仍然需要弄清楚另一个部分。 - Omega

0

我知道该怎么做,但不知道如何编写。

我将在models中创建一个类:

例如models.py:

class Alltables(models.Model):
    title = models.TextField(db_column='Title', blank=True, null=True)  # Field name made lowercase.
    url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True,
                           null=True)  # Field name made lowercase.
    description = models.TextField(db_column='Description', blank=True, null=True)  # Field name made lowercase.
    created_at = models.DateTimeField(db_column='Created_at')  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'Table_name' # make it args

    def __str__(self):
        return self.url

在Meta类中,我想要实现Args在db_table中的应用。

这意味着当我在HTML中点击一个表格时,我希望该表格名称被插入到db_table中,并给我其中的数据。

因为PrimaryTable类将给我该数据库中所有表格的名称。

我希望我已经解释清楚了。但我仍然需要知道如何在Meta类中实现args,并在views.py和HTML模板中使用它。

tables/views.py

def table_base(request):
    table_name = Crawledtables._meta.db_table
    list_tables = Crawledtables.objects.order_by('id')
    table_list = {'list_tables': list_tables}
    return render(request, 'tables/table_base.html', {'table_name': table_name,
                                                      'list_tables': list_tables})


class AboutDetail(DetailView):
    model = Crawledtables
    pk_url_kwarg = 'table_id'
    template_name = 'tables/table_list.html'

    def get_object(self):
        if 'table_id' not in self.kwargs:
            return Crawledtables.objects.get(id=1)
        else:
            return Crawledtables.objects.get(id=self.kwargs['table_id'])

tables/views.py 的一部分

class Details(ListView):
    model = table_name_1
    template_name = 'tables/table_list.html'
    context_object_name = 'details'
    paginate_by = 15
    queryset = table_name_1.objects.all()

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