在我的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
,我就不会有这个问题。
答案 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,很容易消化。
.annotate()
以向查询添加额外信息.extra()
.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如何实际命名表。
再次,根据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查询。也许对你而言,也是对于那些可能会发现这一点的人,让我在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
我在这里没有多少使用这种方法,但这是你如何开始的
python manage.py shell
from django.db import connection
这个是最有趣的,因为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(长度(api_switchesport.name))AS length
,api_physicalserver
。id
,api_physicalserver
。name
,api_physicalserver
。{ {1}},switches_id
。api_physicalserver
,physical_server_model_id
。api_physicalserver
来自switchesport_id
INNER JOIN api_physicalserver
ON
(api_switchesport
。api_physicalserver
= switchesport_id
。api_switchesport
)
在id
。api_switchesport
LIKE'%10%'订购bandwidth
ASC;
对我来说,此查询已成功执行,但如果它为您抛出错误,请尝试修改内容。特别是字段名length
。最初对我来说就像api_switchesport.name
一样,因为几乎所有的表都有一个名称字段,所以你的数据库不确定引用哪个表。
在Django,Django Rest Framework和Django的ORM(对象关系映射器)之间,如果不进行SQL挖掘,我们可以做很多事情。但是,当你遇到问题时,知道一些SQL约定会有所帮助(同样,不要假设你不这样做,但有些读这个答案可能是新的)。
这里,主要的是虽然我们只能通过名称引用字段,但是一旦我们处理多个具有相同名称的表,我们需要使用点表示法。 name
以避免收到错误。
尝试W3School的interactive queriable database。他们也有一个tutorial。