防止 Django 更新 MSSQL 中的标识列

Posted

技术标签:

【中文标题】防止 Django 更新 MSSQL 中的标识列【英文标题】:Prevent Django from updating identity column in MSSQL 【发布时间】:2011-08-23 09:04:40 【问题描述】:

我正在使用 MSSQL 中的旧数据库。我们有一个表,其中有两列导致我出现问题:

class Emp(models.Model):  
    empid = models.IntegerField(_("Unique ID"), unique=True, db_column=u'EMPID')  
    ssn = models.CharField(_("Social security number"), max_length=10, primary_key=True, db_column=u'SSN') # Field name made lowercase.  

所以表有 ssn 列作为主键,django 生成的 SQL 更新代码的相关部分是这样的:

UPDATE [EMP] SET [EMPID] = 399, 
......... 
WHERE [EMP].[SSN] = 2509882579 

问题是 EMP.EMPID 是 MSSQL 中的一个身份字段,因此每当我尝试将更改保存到现有员工时,pyodbc 都会引发此错误:

ProgrammingError: ('42000', "[42000] [Microsoft][SQL Native Client][SQL Server]C
annot update identity column 'EMPID'. (8102) (SQLExecDirectW); [42000] [Microsof
t][SQL Native Client][SQL Server]Statement(s) could not be prepared. (8180)")

将 EMP.EMPID 作为标识对程序的任何内容都不是至关重要的,因此通过创建一个临时列并复制、删除、重命名来删除它似乎是合乎逻辑的事情。这为将老客户转移到 Django 中创建了一个额外的步骤,所以我的问题是,有没有什么方法可以防止 Django 在我对这张表进行更新时生成 '[EMPID] = XXX' sn-p?

编辑 我已经像这样修补了我的模型:

def save(self, *args, **kwargs):
    if self.empid:
        self._meta.local_fields = [f for f in self._meta.local_fields if f.name != 'empid']
        super().save(*args, **kwargs)

这有效,利用了 Django 在 django/db/models/base.py (525) 中填充它的 sql-sentence 的方式。如果有人有更好的方法或可以解释为什么这是不好的做法,我很乐意听到!

【问题讨论】:

我对 django 一无所知,所以我可能是这里的方式,但你有没有尝试使用Autofield。像这样empid = models.AutoField( .... AutoField 必须是模型的主键,对于遗留数据库支持,我必须使用 SSN 字段作为主键,所以很遗憾,这不是一个选项:( 我知道这是一个遗留应用程序,但使用 SSN 作为主键是个坏主意。虽然不太可能,但您的应用程序仍然有可能有一天会收到相同的 SSN(当某人去世时由社会保障局回收)并且您的应用程序将“崩溃”,至少在数据完整性方面是这样。你能改变应用程序以使用 EMPID 作为 PK 吗? @HardCode:这将是最佳做法,但由于所有其他表都使用 ssn 字段链接到 Emp 表,这可能会产生比它解决的问题更多的问题。好消息是我住在冰岛,那里的人口不到 350,000,每个应用程序实例只有 30 到 3000 名员工,因此风险非常小,尽管它在那里 :) 那么EMPID列是标识列,而不是主键?是否在任何限制条件中引用或在任何报告中使用? 【参考方案1】:

这个问题很老了,Sindri 找到了一个可行的解决方案,但我想提供一个我已经在生产中使用了几年的解决方案,不需要在_meta 中乱搞。

我必须编写一个 Web 应用程序,该应用程序与包含许多计算字段的现有业务数据库集成。这些字段通常用于计算记录的状态,几乎用于整个应用程序中的每个对象访问,Django 必须能够使用它们。

这些类型的字段可与模型管理器一起使用,模型管理器使用extra(select=...) 将所需字段添加到查询中。

ComputedFieldsManager代码sn-p:https://gist.github.com/manfre/8284698

class Emp(models.Model):
    ssn = models.CharField(_("Social security number"), max_length=10, primary_key=True, db_column=u'SSN') # Field name made lowercase.

    objects = ComputedFieldsManager(computed_fields=['empid'])


# the empid is added on to the model instance
Emp.objects.all()[0].empid

# you can also search on the computed field
Emp.objects.all().computed_field_in('empid', [1234])

【讨论】:

感谢您的意见! ComputedFieldsManagers 看起来不错,但我担心它不会解决我的问题,因为这意味着我们的代码库中的所有内容都会发生变化。撇开这个不谈,我真的希望来这里的人会寻求你的答案,而不是我的 hack :)

以上是关于防止 Django 更新 MSSQL 中的标识列的主要内容,如果未能解决你的问题,请参考以下文章

MsSql判断表是否有自增标识

关于MSSQL用户高置的问题

Django 连接到 MSSQL

从 Django 网页中的 MSSQL 数据库中获取搜索结果

Django With MSSQL using Pyodbc:未保存模型表单

如何在 mssql 中更新 JSON 中的嵌套数组