【问题标题】:Invalid column name 'id' in DjangoDjango中的列名“id”无效
【发布时间】:2023-03-18 21:10:02
【问题描述】:

所以我以不同的方式使用 Django 模型,我在 MS SQL 服务器中有现有的表,我想为我的应用程序访问它们。我一直在关注复数方面的一些教程,而且 https://docs.djangoproject.com/en/1.11/howto/legacy-databases/。我按照相应的步骤准备好我的模型,当我使用“迁移”命令时,我收到了这个错误

“AssertionError:一个模型不能有多个 AutoField。”

所以我查阅了 SO 并得到了 Django 的另一个文档

默认情况下,Django 为每个模型提供以下字段:

id = models.AutoField(primary_key=True)

这是一个自动递增的主键。

如果您想指定自定义主键,只需指定 primary_key=True 在您的一个字段上。如果 Django 看到你 显式设置 Field.primary_key,它不会添加自动 id 列。

每个模型只需要一个字段来使 primary_key=True(要么 显式声明或自动添加)。

所以通过他的解决方案,我在没有主键的表中添加了“id”列,并跳过了那些有主键的表。 我的“迁移”命令运行顺利。

但是当我尝试测试是否可以从中获取值时,它显示 Invalid column 'ID'

现在,我一直在想,如果 Django 隐式创建它们,或者当我在模型中显式创建它们时(然后使用 makemigrations 和 migrate 命令),它不应该为它创建列吗?

另一种情况: 已经建立了一些具有主键的表,我已经尝试对它们进行查询并且它有效。

这只是让我想如果我错过了所有这些之间的一步,

这是我的库文件

certifi==2016.2.28
Django==1.11.3
django-mssql==1.8
django-pyodbc-azure==1.11.0.0
mysqlclient==1.3.12
PyMySQL==0.7.9
pyodbc==4.0.17
pytz==2017.2
wincertstore==0.2

很抱歉格式错误,但如果我错过了连接旧系统的任何步骤,请提供帮助。请询问是否需要任何额外信息。谢谢!

编辑:正如所问,这是我的查询工作的模型

class DjangoSession(models.Model):
    session_key = models.CharField(primary_key=True, max_length=40)
    session_data = models.TextField()
    expire_date = models.DateTimeField()

    class Meta:
        managed = False
        db_table = 'django_session'

这是一个(许多)不起作用的

class table1(models.Model):
    id=models.AutoField(primary_key=True)
    col2 = models.CharField(db_column='col2', max_length=255, blank=True, null=True)  # Field name made lowercase. Field renamed to remove unsuitable characters.
    col3 = models.IntegerField(db_column='col3', blank=True, null=True)  # Field name made lowercase.
    col4 = models.CharField(max_length=250, blank=True, null=True)
    col5 = models.DateTimeField(db_column='col5')  # Field name made lowercase.

    class Meta:
        managed = True
        db_table = 'table1'

session_key 变量已经在表中,因此我不需要为该表创建主键。 然而,id=models.AutoField(primary_key=True) 已被我添加,因为我阅读了各种文档,我也尝试了创建 id 的“隐式添加”方法,但它给出了相同的结果。

引入此错误的 sql shell 查询:

from app.models import table1
table1.objects.filter(column_name='col3')

migration.createModel函数如下

migrations.CreateModel(
            name='VikalpFtrFeatureCr',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('col2', models.CharField(blank=True, db_column='col2', max_length=255, null=True)),
                ('col3', models.IntegerField(blank=True, db_column='col3', null=True)),
                ('col4', models.CharField(blank=True, max_length=250, null=True)),
                ('col5', models.DateTimeField(db_column='UPDATE_DTTM')),
            ],
            options={
                'managed': False,
                'db_table': 'VIKALP_FTR_Feature_CR',
            },
        )

【问题讨论】:

  • 您能否发布您在添加 AutoField 之前拥有的模型之一,以及将其更改为 之后的模型“我已将 'id' 列添加到不'没有主键".
  • 在您的问题中添加models.py
  • 我将与模型一起编辑问题,谢谢。
  • 看到managed 元类属性的区别了吗?
  • 1.难道不应该这样django可以将id列添加到表中吗? 2.Tried managed=False 在有问题的表上,仍然没有进展

标签: python sql-server django django-models


【解决方案1】:

目前还不清楚为什么会出现初始断言错误。我认为您错误地诊断并采取了错误的行动。现在我们无法判断到底发生了什么,也很难进一步帮助您。

我建议您备份应用程序的migrations 文件夹。让我们将您的应用程序称为“msapp”,并在适当的地方替换它。

首先,回滚迁移:

python manage.py migrate msapp zero

现在删除所有迁移文件:

rm msapp/migrations/0*.py

然后清除models.py:

rm msapp/models.py && touch msapp/models.py

使用想要的表列表运行 inspectdb:

python manage.py inspectdb table1 table2 ... >> msapp/models.py

运行 makemigrations:

python manage.py makemigrations msapp

运行迁移:

python manage.py migrate

现在显示你遇到了什么问题,如果你再次遇到断言错误,完整的回溯

更新

所以问题是当没有主键时,Django 会在模型中添加一个自动创建的字段,即使模型被标记为非托管consensus 似乎可以简单地选择一个字段并将其标记为主键,即使它不是一个。这将阻止自动插入的 ID 字段,这是在 django.db.models.options 的元类中完成的:

def _prepare():
    # ...
    if self.pk is None:
        if self.parents:
            # Promote the first parent link in lieu of adding yet another
            # field.
            field = next(six.itervalues(self.parents))
            # Look for a local field with the same name as the
            # first parent link. If a local field has already been
            # created, use it instead of promoting the parent
            already_created = [fld for fld in self.local_fields if fld.name == field.name]
            if already_created:
                field = already_created[0]
            field.primary_key = True
            self.setup_pk(field)
            if not field.remote_field.parent_link:
                warnings.warn(
                    'Add parent_link=True to %s as an implicit link is '
                    'deprecated.' % field, RemovedInDjango20Warning
                )
        else:
            auto = AutoField(verbose_name='ID', primary_key=True, auto_created=True)
            model.add_to_class('id', auto)

另一种选择是在处理这些模型时始终使用defer,这样就永远不会从数据库中查询“id”字段。您可以通过以下方式使用当前设置进行测试:

table1.objects.filter(column_name='col3').defer('id')

您可以在管理器中进一步抽象:

class NoPkModelManager(models.Manager):
    def get_queryset(self):
        return super(NoPkModelManager, self).get_queryset().defer('id')

class Table1(models.Model):
    objects = NoPkModelManager()
    # .. other fields and methods

【讨论】:

  • 您好,感谢您提供的清晰步骤,我已经按照您的要求进行了尝试,这是我的回溯Traceback (most recent call last): File "C:\Anaconda3\envs\django\lib\site-packages\django\db\backends\utils.py", line 65, in execute return self.cursor.execute(sql, params) File "C:\Anaconda3\envs\django\lib\site-packages\sql_server\pyodbc\base.py", line 545, in execute return self.cursor.execute(sql, params) pyodbc.ProgrammingError: ('42S22', "[42S22] [Microsoft][ODBC SQL Server Driver][SQL Server]Invalid column name 'id'. (207) (SQLExecDirectW)")
  • 如果需要,我可以提供更多信息,因为我是 Django 新手,我已经阅读了不同的文档(对于相同的 django 版本)并写下了我遵循的步骤。
  • 这是由migrate命令触发的?是不是也被“check”命令触发了?我没有看到任何对迁移代码的引用,这是我希望看到的。事实上,在 db/backends/utils.py 中没有正常的方式可以开始回溯。如果您的空间用完了,请更新您的问题。
  • 不,此注释错误来自以下命令的 shell,table1.objects.filter(column_name='col3')。我不确定迁移代码是什么意思,但我会编辑问题以合并 migrations.createModel 函数。
  • 更新答案。
【解决方案2】:

基本上,要使遗留数据库/表/视图正常工作,您至少需要一个在表/视图中唯一的字段。

您需要做的就是输入正确的字段类型(例如 CharField、IntegerField - 在我的示例中我使用了 CharField)并在 Meta 选项中输入表名并将 managed 设置为 False。

class YourLegacyModel(models.Model):

    id = models.CharField(db_column='unique column', primary_key=True)

    class Meta:
        db_table = 'your_table'
        managed = False

【讨论】:

  • 我已经相应地编辑了问题。我创建的主键列在整个表中是唯一的。我把 managed = True 因为我想对它做 CRUD 操作。
  • 不是这样的。让 managed 为 false,你也可以进行 crud 操作。
  • By default, inspectdb creates unmanaged models. That is, managed = False in the model’s Meta class tells Django not to manage each table’s creation, modification, and deletion: 这是我指的文档。
  • 但是您不想创建新表。我以为你在访问旧数据库?!
  • 是的,我正在访问旧数据库,我现在明白 Django 引用的是表的 CRUD 而不是数据的,我会测试一下并返回这里,谢谢!
猜你喜欢
  • 1970-01-01
  • 2019-09-05
  • 2018-11-24
  • 2017-09-02
  • 1970-01-01
  • 1970-01-01
  • 2021-01-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多