2010-12-03 101 views
9

我想通過連接字符串過濾一些數據庫對象。Django ORM:按額外屬性過濾

正常的SQL查詢是:

SELECT concat(firstName, ' ', name) FROM person WHERE CONCAT(firstName, ' ', name) LIKE "a%"; 

在這個模型中,我創建了一個經理叫PersonObjects:

class PersonObjects(Manager): 
    attrs = { 
     'fullName': "CONCAT(firstName, ' ', name)" 
    } 

    def get_query_set(self): 
     return super(PersonObjects, self).get_query_set().extra(
      select=self.attrs) 

我也是在我的模型配置此:

objects = managers.PersonObjects() 

現在訪問fullName適用於單個對象:

>>> p = models.Person.objects.get(pk=4) 
>>> p.fullName 
u'Fred Borminski' 

但它不能在過濾器工作:

>>> p = models.Person.objects.filter(fullName__startswith='Alexei') 
Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/usr/lib/python2.7/site-packages/django/db/models/manager.py", line 141, in filter 
    return self.get_query_set().filter(*args, **kwargs) 
    File "/usr/lib/python2.7/site-packages/django/db/models/query.py", line 550, in filter 
    return self._filter_or_exclude(False, *args, **kwargs) 
    File "/usr/lib/python2.7/site-packages/django/db/models/query.py", line 568, in _filter_or_exclude 
    clone.query.add_q(Q(*args, **kwargs)) 
    File "/usr/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1128, in add_q 
    can_reuse=used_aliases) 
    File "/usr/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1026, in add_filter 
    negate=negate, process_extras=process_extras) 
    File "/usr/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1191, in setup_joins 
    "Choices are: %s" % (name, ", ".join(names))) 
FieldError: Cannot resolve keyword 'fullName' into field. Choices are: firstName, gender, name, (...) 

這是一個錯誤或功能?我怎樣才能解決這個問題?

謝謝。

回答

16

這不是一個錯誤。 filter()只檢查模型定義,因此它不會將fullName識別爲聲明字段(因爲它不是 - 它是查詢中的額外參數)。

您可以添加使用fullNameWHEREextra()

Person.objects.extra(where=["fullName LIKE %s"], params=["Alexei%"]) 
+0

不幸的是,這不起作用。它仍抱怨沒有找到fullName屬性。儘管直接檢索對象的fullName屬性。這種「額外」方法是否以某種方式覆蓋了管理器之前設置的額外屬性? – 2010-12-09 09:24:52

1

我通過實現自定義聚合函數解決了這個。 在這種情況下,我需要將單個字段拼接成街道地址,以便能夠過濾/搜索匹配。 以下集合函數允許指定一個字段和一個或多個其他字符以執行SQL CONCAT_WS。

編輯2015年8月3日:

https://stackoverflow.com/a/19529861/3230522收集到的詳細信息的更好的實現。如果查詢集在子查詢中使用,則以前的實現將失敗。表名現在是正確的,但我注意到這隻適用於連接來自同一個表的列。

from django.db.models import Aggregate 
from django.db.models.sql.aggregates import Aggregate as SQLAggregate 

class SqlAggregate(SQLAggregate): 
    sql_function = 'CONCAT_WS' 
    sql_template = u'%(function)s(" ", %(field)s, %(columns_to_concatenate)s)' 

    def as_sql(self, qn, connection): 
     self.extra['columns_to_concatenate'] = ', '.join(
     ['.'.join([qn(self.col[0]), qn(c.strip())]) for c in self.extra['with_columns'].split(',')]) 
     return super(SqlAggregate, self).as_sql(qn, connection) 

class Concatenate(Aggregate): 
    sql = SqlAggregate 

    def __init__(self, expression, **extra): 
     super(Concatenate, self).__init__(
      expression, 
      **extra) 

    def add_to_query(self, query, alias, col, source, is_summary): 

     aggregate = self.sql(col, 
         source=source, 
         is_summary=is_summary, 
         **self.extra) 

     query.aggregates[alias] = aggregate 
0

建議的解決方案在下面的代碼中使用postgresql和JSONB字段很好。只返回在'key'jsonb字段下具有'合作伙伴'鍵的記錄:

query_partner = "select key->>'partner' from accounting_subaccount " \ 
       "where accounting_subaccount.id = subaccount_id and key ? 'partner'" 
qs = queryset.extra(select={'partner': query_partner}, where=["key ? 'partner'"])