将复杂的GROUP BY和INNER JOIN SQL语句转换为Django ORM级别
我正在尝试将下面提到的SQL查询转换为Django ORM层查询,但未能获得SQL语句提供的完美输出。 型号将复杂的GROUP BY和INNER JOIN SQL语句转换为Django ORM级别,django,django-models,orm,django-orm,Django,Django Models,Orm,Django Orm,我正在尝试将下面提到的SQL查询转换为Django ORM层查询,但未能获得SQL语句提供的完美输出。 型号 class YearlyTable(models.Model): class Meta: db_table = 'yearlytable' managed = True user_id = models.IntegerField(db_index=True) rotations = models.IntegerField() cal
class YearlyTable(models.Model):
class Meta:
db_table = 'yearlytable'
managed = True
user_id = models.IntegerField(db_index=True)
rotations = models.IntegerField()
calories = models.FloatField()
distance = models.FloatField()
duration = models.IntegerField(default=0)
year = models.IntegerField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
然后,我尝试使用原始Django查询示例执行上述查询:
User.objects.raw('select users.state,sum(yearlytable.rotations) as sum_rotations,sum(yearlytable.calories) as sum_calories,sum(yearlytable.distance) as sum_distance from yearlytable inner join users on (yearlytable.user_id = users.id) where yearlytable.user_id in(select id from users where country like \'United States%\' and NOT ("email" LIKE \'%yopmail.com%\')) group by users.state;')
但这也不起作用。现在我不想在这里使用游标,因为我担心SQL注入问题。所以光标离开了桌子
for u in User.objects.raw('select users.state,sum(yearlytable.rotations) as sum_rotations,sum(yearlytable.calories) as sum_calories,sum(yearlytable.distance) as sum_distance from yearlytable inner join users on (yearlytable.user_id = users.id) where yearlytable.user_id in(select id from users where country like \'United States%\' and NOT ("email" LIKE \'%yopmail.com%\')) group by users.state;'):
print u
下面是堆栈跟踪:
Traceback:
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
111. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
57. return view_func(*args, **kwargs)
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/django/views/generic/base.py" in view
69. return self.dispatch(request, *args, **kwargs)
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
407. response = self.handle_exception(exc)
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
404. response = handler(request, *args, **kwargs)
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/rest_framework/decorators.py" in handler
51. return func(*args, **kwargs)
File "/home/akki/rest_api/widget/views.py" in heat_map
18. for u in User.objects.raw('select users.state,sum(yearlytable.rotations) as sum_rotations,sum(yearlytable.calories) as sum_calories,sum(yearlytable.distance) as sum_distance from yearlytable inner join users on (yearlytable.user_id = users.id) where yearlytable.user_id in(select id from users where country like \'United States%\' and NOT ("email" LIKE \'%yopmail.com%\')) group by users.state;'):
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/django/db/models/query.py" in __iter__
1535. query = iter(self.query)
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/django/db/models/sql/query.py" in __iter__
76. self._execute_query()
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/django/db/models/sql/query.py" in _execute_query
90. self.cursor.execute(self.sql, self.params)
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/django/db/backends/utils.py" in execute
81. return super(CursorDebugWrapper, self).execute(sql, params)
File "/home/akki/rest_api/venv/local/lib/python2.7/site-packages/django/db/backends/utils.py" in execute
65. return self.cursor.execute(sql, params)
我试过的Django ORM是:
YearlyTable.objects.annotate(r=Sum('rotations'))
最好将此sql查询转换为django orm级别。似乎可以使用以下orm查询完成此操作: 1) 我们对
User
进行筛选,以查找与最内部的SELECT
语句匹配的内容。这将返回User.id
的列表
2) 首先在YearlyTable上使用values()
将在User.state上执行groupby
3) distinct()用于确保我们只对每个可能的用户进行一次说明
4) 用于对所需的值执行求和
5) 最后,我们再次调用values(),生成包含您在顶级SELECT
query中请求的信息的词典
从django.db.models导入总和
YearlyTable.objects.filter(
user\u id\u in=user.objects.filter(
国家/地区以“美国”开头
).排除(
电子邮件包含class='yopmail.com'
).values\u list('id',flat=True)
).values('user\u state').distinct().annotate(
总和旋转=总和(“旋转”),
sum_卡路里=sum(‘卡路里’),
距离之和=距离之和(距离)
).值('user\u state'、'sum\u rotations'、'sum\u carries'、'sum\u distance')
这似乎可以通过使用以下ORM查询来完成:
1) 我们对User
进行筛选,以查找与最内部的SELECT
语句匹配的内容。这将返回User.id
的列表
2) 首先在YearlyTable上使用values()
将在User.state上执行groupby
3) distinct()用于确保我们只对每个可能的用户进行一次说明
4) 用于对所需的值执行求和
5) 最后,我们再次调用values(),生成包含您在顶级SELECT
query中请求的信息的词典
从django.db.models导入总和
YearlyTable.objects.filter(
user\u id\u in=user.objects.filter(
国家/地区以“美国”开头
).排除(
电子邮件包含class='yopmail.com'
).values\u list('id',flat=True)
).values('user\u state').distinct().annotate(
总和旋转=总和(“旋转”),
sum_卡路里=sum(‘卡路里’),
距离之和=距离之和(距离)
).值('user\u state'、'sum\u rotations'、'sum\u carries'、'sum\u distance')
假设:
使用django ORM而不诉诸原始SQL
按照惯用的方式设计django模型,这意味着相关的表应该使用models ForeignKey、OneonOne或Manytomy属性
假设YearlyTable与用户有一对一的关系
在models.py中:
我用以下示例数据填充了表格:
u = User(email='a@w.com', first_name='ab', city='New York', state='New York', postal_code='12345', country='United States')
y = YearlyTable(user=u, rotations=10, calories=10.8, distance=12.5, duration=20, year=2011)
u = User(email='b@w.com', first_name='ac', city='Buffalo', state='New York', postal_code='67891', country='United States')
y = YearlyTable(user=u, rotations=8, calories=11.8, distance=11.5, duration=30, year=2012)
u = User(email='c@w.com', first_name='ad', city='Rochester', state='New York', postal_code='13579', country='United States')
y = YearlyTable(user=u, rotations=20, calories=15.8, distance=13.5, duration=40, year=2013)
u = User(email='d@w.com', first_name='ae', city='Pittsburgh', state='Pennsylvania', postal_code='98765', country='United States')
y = YearlyTable(user=u, rotations=30, calories=10.2, distance=12.5, duration=40, year=2012)
u = User(email='e@w.com', first_name='af', city='Los Angeles', state='California', postal_code='97531', country='United States')
y = YearlyTable(user=u, rotations=10, calories=14.8, distance=13.5, duration=10, year=2010)
检查物理表并直接对其进行查询
在pythonshell中运行
假设:
使用django ORM而不诉诸原始SQL
按照惯用的方式设计django模型,这意味着相关的表应该使用models ForeignKey、OneonOne或Manytomy属性
假设YearlyTable与用户有一对一的关系
在models.py中:
我用以下示例数据填充了表格:
u = User(email='a@w.com', first_name='ab', city='New York', state='New York', postal_code='12345', country='United States')
y = YearlyTable(user=u, rotations=10, calories=10.8, distance=12.5, duration=20, year=2011)
u = User(email='b@w.com', first_name='ac', city='Buffalo', state='New York', postal_code='67891', country='United States')
y = YearlyTable(user=u, rotations=8, calories=11.8, distance=11.5, duration=30, year=2012)
u = User(email='c@w.com', first_name='ad', city='Rochester', state='New York', postal_code='13579', country='United States')
y = YearlyTable(user=u, rotations=20, calories=15.8, distance=13.5, duration=40, year=2013)
u = User(email='d@w.com', first_name='ae', city='Pittsburgh', state='Pennsylvania', postal_code='98765', country='United States')
y = YearlyTable(user=u, rotations=30, calories=10.2, distance=12.5, duration=40, year=2012)
u = User(email='e@w.com', first_name='af', city='Los Angeles', state='California', postal_code='97531', country='United States')
y = YearlyTable(user=u, rotations=10, calories=14.8, distance=13.5, duration=10, year=2010)
检查物理表并直接对其进行查询
在pythonshell中运行
“但这也不起作用”怎么回事?我收到“索引错误”、“元组索引超出范围”消息发布完整消息并发布您的模型感谢更新问题,但这不是stacktrace-->实际stacktrace@e4c5“但这也不起作用”怎么回事?我收到“索引错误”、“元组索引超出范围”message发布完整消息并发布您的模型感谢您更新问题,但这不是stacktrace-->实际stacktrace@E4C5我收到以下错误:文件“/home/akki/rest_api/venv/local/lib/python2.7/site packages/django/db/models/sql/query.py”,第1389行,在raise_field_error中的选项是:%s“%(name),”。加入(可用)))django.core.exceptions.FieldError:无法将关键字“user”解析为字段。选择包括:卡路里、创建的、距离、持续时间、id、修改的、旋转、用户id、年份与BobbyC类似,我建议您将user\u id
字段迁移到ForeignKey
到user
模型,因为它还可以简化语句:YearlyTable.objects.filter(user\u in=user.objects.filter(country_uustartswith='United')。排除(email_uu包含='yopmail.com'))。值('user_ustate')。不同()。注释(sum_rotations=sum('rotations')、sum_carries=sum('carries')、sum_distance=sum('distance')。值('user_ustate'、'sum_urotations'、'sum_urotations'、'sum_ucarries'、'sum_udistance'))
我收到以下错误:文件“/home/akki/rest_api/venv/local/lib/python2.7/site packages/django/db/models/sql/query.py”,第1389行,在raise_field_error“选项中是:%s“%(名称),”。join(可用)))django.core.exceptions.FieldError:无法将关键字“user”解析到字段中。选择包括:卡路里、创建的、距离、持续时间、id、修改的、旋转、用户id、年份与BobbyC类似,我建议您将user\u id
字段迁移到ForeignKey
到user
模型,因为它还可以简化语句:YearlyTable.objects.filter(us
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class User(AbstractBaseUser):
email = models.EmailField(max_length=255, unique=True)
first_name = models.CharField(max_length=255, blank=True, null=True)
city = models.CharField(max_length=200, blank=True, null=True)
state = models.CharField(max_length=200, blank=True, null=True)
postal_code = models.IntegerField(blank=True, null=True)
country = models.CharField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.email
class YearlyTable(models.Model):
user = models.OneToOneField('User', unique=True)
rotations = models.IntegerField()
calories = models.FloatField()
distance = models.FloatField()
duration = models.IntegerField(default=0)
year = models.IntegerField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
def __unicode__(self):
return str(self.user)
u = User(email='a@w.com', first_name='ab', city='New York', state='New York', postal_code='12345', country='United States')
y = YearlyTable(user=u, rotations=10, calories=10.8, distance=12.5, duration=20, year=2011)
u = User(email='b@w.com', first_name='ac', city='Buffalo', state='New York', postal_code='67891', country='United States')
y = YearlyTable(user=u, rotations=8, calories=11.8, distance=11.5, duration=30, year=2012)
u = User(email='c@w.com', first_name='ad', city='Rochester', state='New York', postal_code='13579', country='United States')
y = YearlyTable(user=u, rotations=20, calories=15.8, distance=13.5, duration=40, year=2013)
u = User(email='d@w.com', first_name='ae', city='Pittsburgh', state='Pennsylvania', postal_code='98765', country='United States')
y = YearlyTable(user=u, rotations=30, calories=10.2, distance=12.5, duration=40, year=2012)
u = User(email='e@w.com', first_name='af', city='Los Angeles', state='California', postal_code='97531', country='United States')
y = YearlyTable(user=u, rotations=10, calories=14.8, distance=13.5, duration=10, year=2010)
psql -d
# select * from testapp_user;
id | password | last_login | email | first_name | city | state | postal_code | country
----+----------+------------+---------+------------+-------------+--------------+-------------+---------------
1 | | | a@w.com | ab | New York | New York | 12345 | United States
2 | | | b@w.com | ac | Buffalo | New York | 67891 | United States
3 | | | c@w.com | ad | Rochester | New York | 13579 | United States
4 | | | d@w.com | ae | Pittsburgh | Pennsylvania | 98765 | United States
5 | | | e@w.com | af | Los Angeles | California | 97531 | United States
(5 rows)
# select * from testapp_yearlytable;
id | rotations | calories | distance | duration | year | created | modified | user_id
----+-----------+----------+----------+----------+------+-------------------------------+-------------------------------+---------
1 | 10 | 10.8 | 12.5 | 20 | 2011 | 2016-05-17 16:23:46.39941+00 | 2016-05-17 16:23:46.399445+00 | 1
3 | 8 | 11.8 | 11.5 | 30 | 2012 | 2016-05-17 16:24:26.264569+00 | 2016-05-17 16:24:26.264606+00 | 2
4 | 20 | 15.8 | 13.5 | 40 | 2013 | 2016-05-17 16:24:51.200739+00 | 2016-05-17 16:24:51.200785+00 | 3
5 | 30 | 10.2 | 12.5 | 40 | 2012 | 2016-05-17 16:25:08.187799+00 | 2016-05-17 16:25:08.187852+00 | 4
6 | 10 | 14.8 | 13.5 | 10 | 2010 | 2016-05-17 16:25:24.846284+00 | 2016-05-17 16:25:24.846324+00 | 5
(5 rows)
# SELECT
testapp_user.state,
sum(testapp_yearlytable.rotations) as sum_rotations,
sum(testapp_yearlytable.calories) as sum_calories,
sum(testapp_yearlytable.distance) as sum_distance
FROM testapp_yearlytable
INNER JOIN testapp_user on (testapp_yearlytable.user_id = testapp_user.id)
WHERE testapp_yearlytable.user_id in
(SELECT id FROM testapp_user
WHERE country LIKE 'United States%' and
NOT ("email" LIKE '%a@w.com%'))
GROUP BY testapp_user.state;
state | sum_rotations | sum_calories | sum_distance
--------------+---------------+--------------+--------------
New York | 28 | 27.6 | 25
Pennsylvania | 30 | 10.2 | 12.5
California | 10 | 14.8 | 13.5
> python manage.py shell
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from testapp.models import User, YearlyTable
>>> from django.db.models import Q, Sum
>>> User.objects.filter(~Q(email__icontains='a@w.com'), country__startswith='United States') \
... .values('state') \
... .annotate(sum_rotations = Sum('yearlytable__rotations'), \
... sum_calories = Sum('yearlytable__calories'), \
... sum_distance = Sum('yearlytable__distance'))
[{'sum_rotations': 28, 'state': u'New York', 'sum_calories': 27.6, 'sum_distance': 25.0}, {'sum_rotations': 30, 'state': u'Pennsylvania', 'sum_calories': 10.2, 'sum_distance': 12.5}, {'sum_rotations': 10, 'state': u'California', 'sum_calories': 14.8, 'sum_distance': 13.5}]