2010-12-03 9 views
9

いくつかのデータベースオブジェクトを連結した文字列でフィルタリングしたいと思います。Django ORM:余分な属性でフィルタ

通常のSQLクエリは次のようになります。私も、私のモデルでこれを構成し

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() 

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

をモデルでは、私は、管理者が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は宣言されたフィールドとして認識されません。なぜなら、それはクエリ内の余分な引数だからです。

あなたはextra()を使用してWHEREfullNameを追加することができます。

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

残念ながら、これは機能しません。それでも、fullName属性が見つからないという不満があります。しかし、オブジェクトからfullName属性を取得することは、直接動作します。この 'extra'メソッドは、以前に設定された余分な属性を何らかの方法で上書きしますか? –

+1

これは実際には動作しません: 'models.Person.objects.extra(select = {'fullName':" CONCAT(firstName、 '、name) "}、where = [' fullName LIKE%s ']、params = ['Alexei%']) '(これは 'where句'の中に" Unknown column 'fullName'をスローします) –

+7

三重コメントをおかけして申し訳ありません。この現象はDjangoがfu​​llNameをエイリアスはMySQLでは動作しませんが、HAVING節で動作しますが、これはDjangoでサポートされていないようですが、代わりに次の(あまり美しくない)妥協点を使用しています。 Persons.objects.extra(ここで= ["CONCAT(firstName、 ''、name)LIKE%s]、params = ['Alexei%'])'。 –

1

を私は、カスタム集計関数を実装することで、これを解決しました。 この場合、一致をフィルタリング/検索できるように、個々のフィールドを住所に連結する必要がありました。 次の集約関数を使用すると、SQL CONCAT_WSを実行するフィールドと1つ以上のフィールドを指定できます。

編集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フィールドの下に 'partner'キーがあるレコードのみが返されます:

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'"]) 
関連する問題