将复杂的GROUP BY和INNER JOIN SQL语句转换为Django ORM级别

将复杂的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

我正在尝试将下面提到的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()
   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}]