【问题标题】:Django MSSQL: Override Foreign Key Constraint Name GenerationDjango MSSQL:覆盖外键约束名称生成
【发布时间】:2021-02-10 20:30:22
【问题描述】:

我正在尝试使用 django-mssql-backend 在 Django 3.0 中创建数据库模型,就像 SQL Server 2019 的 db backend 一样。数据库对其中包含的表使用多个模式,其中一些表是非托管的(已经存在),还有一些是通过迁移从头开始创建的。为了使多个模式正常工作,我使用了在此处的另一个答案中找到的技巧,该技巧建议将表名格式化如下:

class MyModel(models.Model):
    ...

    class Meta:
        db_table = 'schema].[table'

这是为了使 SQL 编译为使在外部自动形成的包裹方括号完成模式/表定义。这样做的问题是,ForeignKey 对象的约束名称是使用此表名生成的,这会导致出现无效的约束名称,从而导致在创建表后迁移失败,并且是时候创建约束了。\

它们生成如下:
[schema1].[table1_colname_id_bc165567_fk2_schema2].[table_colname]

有没有办法覆盖这种行为?如果可以通过分叉后端并添加手动编译代码来覆盖它,我将如何去做呢?否则,如何在使用多个模式并充分利用 Django 附带的 ORM/迁移时在我的模型中添加外键?

【问题讨论】:

    标签: python sql-server django python-3.9 django-mssql-backend


    【解决方案1】:

    我最终弄明白了。在 fork django-mssql-backend 之后,我覆盖了sql_server.pyodbc.schema 模块中DatabaseSchemaEditor 类中的_create_index_name_fk_constraint_name 方法,将表名更改为split('[')[-1],这省略了字符串的模式hack 部分和额外的开括号,但不应干扰没有应用该 hack 的表格。

    这里是被覆盖的方法
    (大部分代码与 django.db.backends.base.schema 中的原始实现相同):

    def _create_index_name(self, table_name, column_names, suffix=""):
            """
            Generate a unique name for an index/unique constraint.
            The name is divided into 3 parts: the table name, the column names,
            and a unique digest and suffix.
            """
    
            # CHANGE HERE (table_name to table_name.split('[')[-1]
            _, table_name = split_identifier(table_name.split('[')[-1])
            hash_suffix_part = '%s%s' % (names_digest(table_name, *column_names, length=8), suffix)
            max_length = self.connection.ops.max_name_length() or 200
            # If everything fits into max_length, use that name.
            index_name = '%s_%s_%s' % (table_name, '_'.join(column_names), hash_suffix_part)
            if len(index_name) <= max_length:
                return index_name
            # Shorten a long suffix.
            if len(hash_suffix_part) > max_length / 3:
                hash_suffix_part = hash_suffix_part[:max_length // 3]
            other_length = (max_length - len(hash_suffix_part)) // 2 - 1
            index_name = '%s_%s_%s' % (
                table_name[:other_length],
                '_'.join(column_names)[:other_length],
                hash_suffix_part,
            )
            # Prepend D if needed to prevent the name from starting with an
            # underscore or a number (not permitted on Oracle).
            if index_name[0] == "_" or index_name[0].isdigit():
                index_name = "D%s" % index_name[:-1]
            return index_name
    
        def _fk_constraint_name(self, model, field, suffix):
            def create_fk_name(*args, **kwargs):
                return self.quote_name(self._create_index_name(*args, **kwargs))
    
            return ForeignKeyName(
                model._meta.db_table.split('[')[-1],
                [field.column],
                # CHANGE HERE (db_table to db_table.split('[')[-1]
                split_identifier(field.target_field.model._meta.db_table.split('[')[-1])[1],
                [field.target_field.column],
                suffix,
                create_fk_name,
            )
    

    如果 Django 的未来版本继续不支持多个模式,希望这对未来遇到类似问题的其他人有用。

    编辑:我已经在 django-mssql-backend 存储库上创建了一个拉取请求,以合并这些更改,因此如果该问题的上下文获得批准,将来对人们来说可能不是问题。

    【讨论】:

    • 这个拉取请求被接受了吗?我也有同样的问题。我宁愿不分叉和破解项目
    • 很遗憾,即使所有检查都通过了,它仍然没有被批准和合并。现在存在一个新的 django-mssql-backend 分支,名为 mssql-django,由官方 Microsoft GitHub 帐户维护,尽管我尚未检查他们是否添加了此功能。
    猜你喜欢
    • 2016-08-14
    • 2017-04-20
    • 2019-06-10
    • 1970-01-01
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-29
    相关资源
    最近更新 更多