2011-01-21 20 views
10

property()を使用して動的に生成されたpythonプロパティを使用してクエリセットでDjangoのfilter()を使用する方法があるのだろうかと思っていました。私はすべてのユーザーのfirst_namelast_nameを持っており、連結名first_name last_nameに基づいてフィルタリングしたいと思います。 (この背後にある理由は、私が自動補完を行うとき、私はクエリは名、姓、または連結の一部と一致するかどうかを確認するために検索することです。私は、例えば、John Smithに一致するようにJohn Sをしたい。動的プロパティに基づくdjangoクエリ

私はのプロパティを作成しましたname:私は連結名を取得するためにuser.nameを呼び出すことができます

def _get_name(self): 
    return self.first_name + " " + self.last_name 
    name = property(_get_name) 

この方法

しかし、私はUser.objects.filter(name__istartswith=query)を行うにしようと、私はエラーCannot resolve keyword 'name' into field.

012を取得します。

これを行う方法に関するご意見はありますか?フルネームを格納するためにデータベースに別のフィールドを作成する必要がありますか?

答えて

9

filter()は、データベースレベルで動作します(実際にはSQLを書きます)。したがって、Pythonコード(dynamic property in your question)に基づくクエリでは使用できません。

これは、この部門では、多くの他の回答から一緒に入れ答えです。)

+0

興味深いことに、プロパティでフィルターをかけることはできませんが、既存のフィールドをグループ化し、グループによってフィルタリングすることができます。[回答を下に](http://stackoverflow.com/a/19040011/3999748) – Persijn

0

それはデータベースが提出されたような本ない性質にフィルタリングするジャンゴでは不可能だと思うが、あなたはクールなオートコンプリートの検索は、このようなものであることを確認するために何ができるか:

if ' ' in query: 
    query = query.split() 
    search_results = list(chain(User.objects.filter(first_name__icontains=query[0],last_name__icontains=query[1]), 
            User.objects.filter(first_name__icontains=query[1],last_name__icontains=query[0]))) 
else: 
    search_results = User.objects.filter(Q(first_name__icontains=query)| Q(last_name__icontains=query)) 

は、このコードは、ユーザーを与えますあなたのシステムの姓または名の入力を開始する柔軟性があり、ユーザーはこれを許可したことに感謝します。

+0

思想を、いくつかの問題があります。一部の人は、姓や名字にスペースを入れることがあり、スペースで区切ると壊れることがあります。さもなければ、私は既にそれを名字か姓のどちらかで検索させます。私は作成時に名前フィールドに値を設定するための信号を設定しましたので、今すぐ動作します。ありがとう。 – munchybunch

12

受け入れ答えは完全に真実ではありません。

多くの場合、キーワードマネージャのpop動的プロパティにモデルマネージャのget()を上書きし、照会する実際の属性をkwargsキーワード引数辞書に追加することができます。 superを必ず返すようにして、通常のget()呼び出しが期待どおりの結果を返すようにしてください。

私は自分の解決策を貼り付けていますが、__startswithと他の条件付きクエリに対しては、splitにロジックを追加して適切に処理することができます。ここで

は、動的プロパティによって照会できるように、私の回避策だった:models.pyで

class BorrowerManager(models.Manager): 
    def get(self, *args, **kwargs): 
     full_name = kwargs.pop('full_name', None) 
     # Override #1) Query by dynamic property 'full_name' 
     if full_name: 
      names = full_name_to_dict(full_name) 
      kwargs = dict(kwargs.items() + names.items()) 
     return super(BorrowerManager, self).get(*args, **kwargs) 

:utils.pyで

class Borrower(models.Model): 
    objects = BorrowerManager() 

    first_name = models.CharField(null=False, max_length=30) 
    middle_name = models.CharField(null=True, max_length=30) 
    last_name = models.CharField(null=False, max_length=30) 
    created = models.DateField(auto_now_add=True) 

(のために文脈のため):

def full_name_to_dict(full_name): 
    ret = dict() 
    values = full_name.split(' ') 
    if len(values) == 1: 
     raise NotImplementedError("Not enough names to unpack from full_name") 
    elif len(values) == 2: 
     ret['first_name'] = values[0] 
     ret['middle_name'] = None 
     ret['last_name'] = values[1] 
     return ret 
    elif len(values) >= 3: 
     ret['first_name'] = values[0] 
     ret['middle_name'] = values[1:len(values)-1] 
     ret['last_name'] = values[len(values)-1] 
     return ret 
    raise NotImplementedError("Error unpacking full_name to first, middle, last names") 
+2

良い!あなたが 'isinstance(getattr(self.model、attr)、property)'のようなものを使って、引数がプロパティであるかどうかを調べることができます。私は同様の方法で何かしています.. –

+1

'kwargs = dict(kwargs.items()+ names.items())'は 'kwargs.update(names)'として書くことができます – thebjorn

+1

これはDjangoでもう動作していないようです1.9 –

3

私も同様の問題があり、解決策を探していました。以下のために服用すると、検索エンジンは、最良の選択肢(Elasticsearchと例えばdjango-haystack)になることを許可され、それは(あなたがistartswithicontainsを置き換えることができます)私はDjangoのORMを使用して、あなたのニーズに合わせていくつかのコードを実装してみましょう:

from django.db.models import Value 
from django.db.models.functions import Concat 

queryset = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name') 
return queryset.filter(full_name__icontains=value) 

私の場合、ユーザーが 'first_namelast_name'かviceversaを挿入するかどうかはわかりませんでしたので、次のコードを使用しました。ジャンゴ< 1.8で

from django.db.models import Q, Value 
from django.db.models.functions import Concat 

queryset = User.objects.annotate(first_last=Concat('first_name', Value(' '), 'last_name'), last_first=Concat('last_name', Value(' '), 'first_name')) 
return queryset.filter(Q(first_last__icontains=value) | Q(last_first__icontains=value)) 

、あなたはおそらく、SQL CONCAT機能をextraに次のようなもの頼る必要があるだろう:そのことについて

queryset.extra(where=['UPPER(CONCAT("auth_user"."last_name", \' \', "auth_user"."first_name")) LIKE UPPER(%s) OR UPPER(CONCAT("auth_user"."first_name", \' \', "auth_user"."last_name")) LIKE UPPER(%s)'], params=['%'+value+'%', '%'+value+'%']) 
関連する問題