首页 > 解决方案 > django.db.utils.OperationalError:(1052,“字段列表中的列'名称'不明确”)

问题描述

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

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

使用:

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

我的物理服务器模型:

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)
    ...

编辑-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

编辑-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

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

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

如果我直接退货qs,我不会有这个问题。

标签: pythondjangodjango-rest-framework

解决方案


问题在于您的 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 上的简短讨论,很容易理解。

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

当然,一切都可以使用手动 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 如何在“通过直接连接到您的数据库进行调试”部分中实际命名您的表的建议。

同样,按照西蒙的建议,我认为您甚至不需要在您的视图中使用 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。

记录所有查询

请参阅有关记录所有查询的这个问题(以及查看发生了什么查询的有趣工具)

显示一个查询

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

有关更多详细信息,请参见此处:django orm,如何查看(或记录)已执行的查询?

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 中调试

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

  1. 通过键入启动 shellpython manage.py shell
  2. from django.db import connection
  3. 测试 ORM python 命令。看玩 api

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

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

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

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

  1. 列出您的数据库:SHOW DATABASES;
  2. 连接到您的数据库:USE your_database_name_here;
  3. 显示您的表格: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 为我们做出了这个决定,我们可能不知道表的实际名称!

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

    选择(长度(api_switchesport.name))作为lengthapi_physicalserverid, api_physicalserver. name, api_physicalserver. switches_id, api_physicalserver. physical_server_model_id, api_physicalserver. switchesport_id
    api_physicalserver
    INNER JOIN api_switchesportON
    ( api_physicalserver. switchesport_id= api_switchesport. id)
    WHERE 开始api_switchesportbandwidthLIKE '%10%' ORDER BY lengthASC;

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

了解有关 SQL 的更多信息

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

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

试试 W3School 的交互式可查询数据库。他们也有一个与之配套的教程。


推荐阅读