Django:过滤RawQuerySet

时间:2012-02-03 21:22:56

标签: django django-models

我有some weird query,所以我必须执行原始SQL。问题是这个查询变得越来越大,并且有很多可选的过滤器(排序,列标准等)。

所以,鉴于此查询:

SELECT DISTINCT Camera.* FROM Camera c
     INNER JOIN cameras_features fc1 ON c.id = fc1.camera_id AND fc1.feature_id = 1
     INNER JOIN cameras_features fc2 ON c.id = fc2.camera_id AND fc2.feature_id = 2

这大致是Python代码:

def get_cameras(features):
  query = "SELECT DISTINCT Camera.* FROM Camera c"
  i = 1
  for f in features:
    alias_name = "fc%s" % i
    query += "INNER JOIN cameras_features %s ON c.id = %s.camera_id AND %s.feature_id = " % (alias_name,alias_name,alias_name)
    query += " %s "
    i += 1
  return Camera.objects.raw(query, tuple(features))

这工作得很好,但我需要添加更多过滤器和订购,例如假设我需要按颜色和按价格排序,它开始增长:

#extra_filters is a list of tuples like:
# [('price', '=', '12'), ('color' = 'blue'), ('brand', 'like', 'lum%']
def get_cameras_big(features,extra_filters=None,order=None):
  query = "SELECT DISTINCT Camera.* FROM Camera c"
  i = 1
  for f in features:
    alias_name = "fc%s" % i
    query += "INNER JOIN cameras_features %s ON c.id = %s.camera_id AND %s.feature_id = " % (alias_name,alias_name,alias_name)
    query += " %s "
    i += 1
  if extra_filters:
    query += " WHERE "
    for ef in extra_filters:
      query += "%s %s %s" % ef #not very safe, refactoring needed
  if order:
    query += "order by %s" % order

  return Camera.objects.raw(query, tuple(features))

所以,我不喜欢它如何开始增长,我知道Model.objects.raw()返回一个RawQuerySet,所以我想做这样的事情:

queryset = get_cameras( ... )
queryset.filter(...)
queryset.order_by(...)

但这不起作用。当然我可以只执行原始查询,之后获取带有数据的实际QuerySet,但我将执行两个查询。像:

raw_query_set = get_cameras( ... )
camera.objects.filter(id__in(raw_query_set.ids)) #don't know if it works, but you get the idea

我认为使用QuerySet init 或缓存可以做到这一点,但是无法做到。

5 个答案:

答案 0 :(得分:19)

.raw()是一个终点。 Django无法对查询集做任何事情,因为这需要能够以某种方式将SQL解析回它用于创建SQL的DBAPI。如果您使用.raw(),则完全由您构建所需的确切SQL。

如果您能以某种方式将查询缩减为可由.extra()处理的内容。您可以使用Django的API构建您喜欢的任何查询,然后使用.extra()添加额外的SQL,但这将是您唯一的方法。

答案 1 :(得分:1)

还有另一种选择:将RawQuerySet转换为列表,然后就可以像这样进行排序......

results_list.sort(key=lambda item:item.some_numeric_field, reverse=True)

和你这样的过滤......

filtered_results = [i for i in results_list if i.some_field == 'something'])

...所有以编程方式。我一直这样做以减少数据库请求。效果很好!

答案 2 :(得分:0)

要构造查询集,每个方法都会返回一个新的查询集,因此每次添加时都需要存储该新的查询集。所以改变你的伪代码::

queryset = get_cameras( ... )
queryset = queryset.filter(...)
queryset = queryset.order_by(...)

这将构建更复杂的查询集。

答案 3 :(得分:0)

我实现了支持 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case MY_REQUEST_PERMISSION_COARSE_LOCATION: { if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ // permissiosn was granted yaaaay! Do the coarse location related task you need to do //Log.i(TAG, mLastLocation.getLongitude() + ""); //Log.i(TAG, mLastLocation.getLatitude() + ""); //mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); } else { // permissions denied, boo! Disable the functionality that depends on this permissions } return; // Still inside the case checker, perform other permissions that this app might require working on } // end of case test for the acces coarse location } // end of stich statmeent } // end of onRequestPermissionsResult() filter()order_by()values()的Django原始查询集。它不适用于任何RAW查询,但对于具有某些values_list()SELECT的典型INNER JOIN,它应该有效。

LEFT JOIN是作为Django模型FilteredRawQuerySetQuerySet的组合实现的,其中SQL查询的基础(左侧部分)是通过RawQuerySet生成的,而RawQuerySetWHERE指令是ORDER BY

的红外线

https://github.com/Dmitri-Sintsov/django-jinja-knockout/blob/master/django_jinja_knockout/query.py

适用于Django 1.8 .. 1.11。

它还为模型实例的QuerySet对象结果列表实现ListQuerySet实现,因此可以像处理普通查询集一样处理这些实现。

以下是使用示例:

https://github.com/Dmitri-Sintsov/djk-sample/search?l=Python&q=filteredrawqueryset&type=&utf8=%E2%9C%93

答案 4 :(得分:0)

您可以做的另一件事是,如果您无法将其转换为常规QuerySet,则可以在数据库后端创建一个View。它基本上在您访问时在视图中执行查询。在Django中,您将创建一个非托管模型以附加到View。使用该模型,您可以将过滤器应用为常规模型。使用外键,可以将on_delete arg设置为models.DO_NOTHING。

有关非托管模型的更多信息: https://docs.djangoproject.com/en/2.0/ref/models/options/#managed