django.db.utils.OperationalError:(1052,“字段列表中的列'名称'不明确”)

Posted

技术标签:

【中文标题】django.db.utils.OperationalError:(1052,“字段列表中的列\'名称\'不明确”)【英文标题】:django.db.utils.OperationalError: (1052, "Column 'name' in field list is ambiguous")django.db.utils.OperationalError:(1052,“字段列表中的列'名称'不明确”) 【发布时间】:2018-11-10 02:38:59 【问题描述】:

在我的 Django 项目中,当我查询数据时出现以下错误:

django.db.utils.OperationalError: (1052, "字段列表中的列 'name' 不明确")

使用:

http://localhost:8000/api/physicalserver/list/?switchesport__bandwidth=10

但如果我使用:

http://localhost:8000/api/physicalserver/list/?switches__id=xxx

它会正常工作的。

我的 ListAPIView 代码:

class PhysicalServerListAPIView(ListAPIView):
    serializer_class = PhysicalServerListSerializer
    permission_classes = [AllowAny]
    pagination_class = CommonPagination
    def get_queryset(self):
        query_params = self.request.query_params
        filters = '__contains'.format(key): value
               for key, value in query_params.items()
               
        qs = PhysicalServer.objects.filter(**filters)
        return qs.extra(select='length':'Length(name)').order_by('length', 'name')

我的序列化代码:

class PhysicalServerListSerializer(ModelSerializer):
    bandwidth = serializers.SerializerMethodField()

    class Meta:
        model = PhysicalServer
        fields = "__all__"
        depth = 1

    def get_bandwidth(self, obj):
        return obj.switchesport.bandwidth

我的 PhysicalServer 模型:

class PhysicalServer(models.Model):         
    name = models.CharField(max_length=32)

    switches = models.ForeignKey(to=Switches, on_delete=models.DO_NOTHING)
    physical_server_model = models.ForeignKey(to=PhysicalServerModel, null=True, on_delete=models.DO_NOTHING)
    switchesport = models.OneToOneField(to=SwitchesPort, related_name="physical_server", on_delete=models.DO_NOTHING)
    ...

EDIT-1

我的开关型号:

class Switches(models.Model):

    name = models.CharField(max_length=32)
    desc = models.CharField(max_length=256)
    routerdevice = models.ForeignKey(to=RouterDevice, related_name="switches")

    gatewaydevice = models.ForeignKey(to=GatewayDevice,  related_name="switches")

    ctime = models.DateTimeField(auto_now_add=True)
    uptime = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-name']

    def __str__(self):
        return self.name
    def __unicode__(self):
        return self.name

还有我的 SwitchesPort 型号代码:

class SwitchesPort(models.Model):

    name = models.CharField(max_length=32, unique=True)  
    desc = models.CharField(max_length=256, null=True, blank=True)
    switches = models.ForeignKey(to=Switches, on_delete=models.CASCADE,related_name="switchesports")
    vlanedipv4networkgroup = models.ForeignKey(
        to=VlanedIPv4NetworkGroup,  
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="switchesports")

    bandwidth = models.IntegerField(default=10)

    ctime = models.DateTimeField(auto_now_add=True)
    uptime = models.DateTimeField(auto_now=True)

    class Meta:

        ordering = ['name']

    def __str__(self):
        return self.name
    def __unicode__(self):
        return self.name

Edit-2

我的PhysicalServerModel,(应该是PhysicalServerType):

class PhysicalServerModel(models.Model):

    name = models.CharField(max_length=32)
    desc = models.CharField(max_length=256)

    cpu = models.CharField(null=True, blank=True, max_length=64)  
    ram = models.CharField(null=True, blank=True, max_length=64)  
    disk = models.CharField(null=True, blank=True, max_length=64)
    bandwidth = models.CharField(null=True, blank=True, max_length=64, default=10)
    price = models.DecimalField(null=True, blank=True, max_digits=8, decimal_places=2, max_length=16)

    ctime = models.DateTimeField(auto_now_add=True)
    uptime = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-id']

    def __str__(self):
        return self.name
    def __unicode__(self):
        return self.name

我的 djangorestframework 版本是3.7.1,django 版本是1.11.1。我使用mysql 作为我的数据库。


EDIT-3

到目前为止,我们发现问题是因为当我尝试在PhysicalServerListAPIView 中按length 排序时,name 字段不明确:

 return qs.extra(select='length':'Length(name)').order_by('length', 'name')

如果我直接返回qs,我就不会有这个问题了。

【问题讨论】:

在 SQL 连接期间似乎有些模棱两可。请添加您的Switches 型号? @JerinPeterGeorge 查看我的编辑。 从两个模型中删除 Meta ordering 是否可以解决问题? @richard_ 为什么你认为是订购造成的?反正我评论了Meta,没有迁移,测试了一下,不行。 您发布的代码难以复制。请注意,包含您的导入语句是 MCVE 的一部分(特别是完整且可验证)。请参阅meta.***.com/questions/312045/… 进行讨论。 【参考方案1】:

问题在于您的 get_queryset 代码。

你有

return qs.extra(select='length':'Length(name)').order_by('length', 'name')

更新:@Simon Charette 指出这里不需要.extra(),因为这种行为可以在不依赖它的情况下完成。

正如 Simon 所建议的那样,您最好的选择是尽可能多地使用 Django 的 ORM,并且只有在其他方法都失败时才使用 .extra()。他建议做.order_by(Length('name'), name) 可能是您想要实现的最佳解决方案。

再多研究一下,现在应该使用.extra().annotate() 或仅使用 ORM 的基本功能,如.order_by()。这是 Reddit 上的short discussion,很容易理解。

    如果您可以仅使用 ORM 的函数完成您想要完成的工作,那就去做吧! 如果需要,请返回 .annotate() 为查询添加额外信息 如果您需要做的事情不能使用上述工具,请使用.extra() 如果失败,请使用.raw() 使用手动 SQL 查询

当然,一切都可以使用手动 SQL 查询来完成,但抽象层的全部意义在于尽可能多地使用它。

如果你想通电并使用额外的电源,你必须这样做:

return qs.extra(select='length':'Length(APP_NAME_MODEL_NAME.name)').order_by('length', 'api_MODELNAME.name')

当然,将 APP_NAME 和 MODEL_Name 替换为您的值。对我来说,它是 api_switchesport。请参阅下面关于检查 Django 如何在“通过直接连接到您的数据库进行调试”部分中实际命名您的表的建议。

同样,按照 Simon 的建议,我认为您甚至不需要在您的视图中使用 get_queryset 函数,只需在您的 urls.py 中执行以下操作:

from django.db.models.functions import Length

urlpatterns = [
    url(r'^physicalserver/list/$', 
    PhysicalServerListAPIView.as_view (queryset=
        PhysicalServer.objects.all().order_by(
            Length('name'), 'name'
        )
    ), name='physicalserver-list'),
]


调试 SQL

这里的主要问题是/曾经是 SQL 查询不起作用。也许对你来说,也对那些可能发现这一点的人来说,让我回顾一下在 Django 中调试 SQL。

记录所有查询

查看有关记录所有查询的这个问题(以及查看发生的查询的interesting tool)

显示一个查询

要仅显示一个问题查询,您可以执行以下操作(我包含了您的示例,但将 qs.extra 替换为您可能需要调试的另一个查询):

更多详情请看这里:django orm, how to view (or log) the executed query?

from django.db import connection
result = qs.extra(select='length':'Length(_.name)'.format(appname, field)).order_by('length', '_.name'.format(appname, field))
print(connection.queries)
return result

在 Shell 中调试

我在这里并没有太多使用这种方法,但这是你开始的方式

    键入python manage.py shell 启动一个shell from django.db import connection 测试 ORM python 命令。见playing with the api

通过直接连接到您的数据库进行调试

这个是这里最有趣的,因为 Django 为我们做了很多决定,我们可能不知道数据库中的列名等基础知识。

要连接到 MySQL 数据库(SQLite 是默认数据库,但 qg_java_17137 使用 MySQL)我输入了sudo mysql,但其他各种问题回答了如何连接到不同类型的数据库。对于 SQLite,这将是 sqlite3 example.db 或类似的命令。

这给了我一个提示 mysql> 输入命令。

    列出您的数据库:SHOW DATABASES; 连接到您的数据库:USE your_database_name_here; 显示您的表格:SHOW TABLES;

这让我得到了这个列表:

+----------------------------+
| Tables_in_sandbox          |
+----------------------------+
| api_gatewaydevice          |
| api_physicalserver         |
| api_physicalservermodel    |
| api_routerdevice           |
| api_switches               |
| api_switchesport           |
| api_vlanedipv4networkgroup |
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+

这告诉我们一件有趣的事情。 Django 已将我的应用程序名称(“api”)添加到数据库中的所有表中。这是有道理的,因为不同的应用程序通常有同名的表,但是因为 Django 为我们做出了这个决定,我们可能不知道我们表的实际名称!

    测试您从“调试一个查询”步骤获得的查询。

    SELECT (Length(api_switchesport.name)) AS length, api_physicalserver.id, api_physicalserver.name, api_physicalserver.switches_id, api_physicalserver.physical_server_model_id, @ 987654358@.switchesport_id 发件人api_physicalserver 内连接api_switchesport ON (api_physicalserver.switchesport_id = api_switchesport.id) WHERE api_switchesport.bandwidth LIKE '%10%' ORDER BY length ASC;

    对我来说,此查询成功执行,但如果它为您抛出错误,请尝试修改内容。特别是字段名称api_switchesport.name。最初对我来说就像 name 一样出现,并且由于几乎所有表都有名称字段,因此您的数据库不确定引用的是哪个表。

了解有关 SQL 的更多信息

在 Django、Django Rest Framework 和 Django 的 ORM(对象关系映射器)之间,我们可以做很多事情,而无需深入研究 SQL。但是,当您遇到问题时,了解一些 SQL 约定会有所帮助(同样,不要假设您不知道,但是阅读此答案的一些人可能对此很陌生)。

这里,主要的一点是,虽然我们只能通过名称来引用字段,但是一旦我们处理多个同名的表,我们就需要使用点表示法。 table_name.field_name 以避免像您收到的错误。

试试 W3School 的interactive queriable database。他们也有一个tutorial。

【讨论】:

谢谢你的回答,我试过了。我收到(1054, "Unknown column 'api_switchesport.name' in 'field list'") 错误。但是您的回答让我知道问题是由排序引起的。如果我不使用'length' 进行排序,我不会收到错误消息。 我假设您的应用名称是 api,因为这是您使用的基本路线。如果您的应用名称不同,它将以不同的方式存储在您的数据库中。请查看我的编辑以及我们可以用来进一步诊断的代码。 我进行了重大修改,添加了有关在 Django 中调试 SQL 以解决此问题和未来查询/数据库问题的建议。【参考方案2】:

这是extra() 的众所周知的限制;它不会正确别名引用。我建议您不要使用 it should only be used as a last resort。

你应该使用annotate(Length)

annotate(
    length=Length('name'),
).order_by('length', 'name')

https://docs.djangoproject.com/en/2.0/ref/models/database-functions/#length

或者只是将Length 传递给order_by,如果它只是注释 用于订购目的

.order_by(Length('name'), name)

https://docs.djangoproject.com/en/2.0/ref/models/querysets/#order-by

【讨论】:

我从您的回答中学到了很多东西!我不想删除我的答案,因为当 OP 原始发布问题时,get_queryset(self): 代码没有被包括在内,所以我觉得我的答案仍然增加了调试过程的价值(我与 OP 一起做了很多工作找出问题出在哪里),但我编辑了我的答案,并认为你反映了你的答案提供的更好的建议(当然,赞成你的答案)。

以上是关于django.db.utils.OperationalError:(1052,“字段列表中的列'名称'不明确”)的主要内容,如果未能解决你的问题,请参考以下文章