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

时间:2018-05-31 06:46:40

标签: python django django-rest-framework

在我的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

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

修改-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作为我的数据库。

修改-3

到目前为止,我们发现问题的原因是{I}尝试按name length排序时PhysicalServerListAPIView字段不明确:

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

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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

问题在于你的get_queryset代码。

你有

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

更新: @Simon Charette指出此处不需要.extra(),因为这种行为可以在不退回的情况下完成。

正如西蒙建议你做的最好的事情就是尽可能多地使用Django的ORM,如果所有其他方法都失败了,那么只能依靠.extra()。他建议做.order_by(Length('name'), name)可能是你想要实现的最佳解决方案。

稍微研究一下,这里应该使用.extra().annotate()或仅使用ORM的基本功能.order_by()。这是Reddit上的short discussion,很容易消化。

  1. 如果你只使用ORM的功能就可以得到你想要的东西,那就去做吧!
  2. 如果需要,请回到.annotate()以向查询添加额外信息
  3. 如果您需要完成的操作无法使用上述工具,请使用.extra()
  4. 如果失败,请使用.raw()
  5. 回退手动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中调试

    我在这里没有多少使用这种方法,但这是你如何开始的

    1. 键入python manage.py shell
    2. 启动shell
    3. from django.db import connection
    4. 测试ORM python命令。请参阅playing with the api
    5. 通过直接连接到您的数据库进行调试

      这个是最有趣的,因为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;
      4. 这让我上市了:

        +----------------------------+
        | 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. 测试从“调试一个查询”步骤获得的查询。

          SELECT(长度(api_switchesport.name))AS lengthapi_physicalserveridapi_physicalservernameapi_physicalserver。{ {1}},switches_idapi_physicalserverphysical_server_model_idapi_physicalserver
          来自switchesport_id
              INNER JOIN api_physicalserver ON         (api_switchesportapi_physicalserver = switchesport_idapi_switchesport
              在idapi_switchesport LIKE'%10%'订购bandwidth ASC;

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

        3. 了解有关SQL的更多信息

          在Django,Django Rest Framework和Django的ORM(对象关系映射器)之间,如果不进行SQL挖掘,我们可以做很多事情。但是,当你遇到问题时,知道一些SQL约定会有所帮助(同样,不要假设你不这样做,但有些读这个答案可能是新的)。

          这里,主要的是虽然我们只能通过名称引用字段,但是一旦我们处理多个具有相同名称的表,我们需要使用点表示法。 name以避免收到错误。

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