2011-01-02 10 views
24

Djangoでは、モデルクラスを持っているとします。Djangoでは、クエリーセットにメソッドを追加できますか?

from django.db import models 

class Transaction(models.Model): 
    ... 

次に、モデルにメソッドを追加したい場合は、合理的に複雑なフィルタを使用して、カスタムモデルマネージャを追加することができます。

class TransactionManager(models.Manager): 

    def reasonably_complex_filter(self): 
     return self.get_query_set().filter(...) 


class Transaction(models.Model): 
    objects = TransactionManager() 

そして私は行うことができます。

>>> Transaction.objects.reasonably_complex_filter() 

を私はモデルから設定されたクエリの最後に連鎖させることができ、カスタムメソッドを追加することができます方法はありますか?

すなわち、私はこれを行うことができますようにカスタムメソッドを追加します。

>>> Transaction.objects.filter(...).reasonably_complex_filter() 

答えて

5

はあなたが最終的に終わるQuerySetにメソッドを追加する必要があります。したがって、この機能を使用する場合は、定義したメソッドを持つQuerySetサブクラスを作成して使用する必要があります。

私はそれを行う方法を説明し、あなたがしたい場合があります理由このチュートリアルが見つかりました:あなたはあなたが必要とする方法を追加し、カスタムクエリセットを返すようにget_query_set()方法を変更することができ

http://adam.gomaa.us/blog/2009/feb/16/subclassing-django-querysets/index.html

+0

Right、gotcha。そのブログ記事で提案されている方法が確実に機能するかどうか知っていますか?コメントは、 '__getattr__'アプローチにはいくつかの問題があることを示唆しています。 –

+0

私はこのコードを自分で実行することはしませんでしたが、Djangoプロジェクトで使用されている '__getattr__'はこれまで同様の方法で信頼性高く使用されています。 –

+1

記事はかなり古くなっていますが、いくつかの方法でメソッドを 'QuerySet'オブジェクトにアタッチする必要があります。 –

3

を。私は例TransactionモデルにTransactionQuerySetをサブクラス、または関連Manager中を見てきました

class TransactionManager(models.Manager): 
    def get_query_set(self): 
     return TransactionQuerySet(self.model) 

class TransactionQuerySet(models.query.QuerySet): 
    def reasonably_complex_filter(self): 
     return self.filter(...) 

が、それは完全にあなた次第です:あなたのケースでは、あなたが使用します。

編集:私はTransactionManagerので、Transaction.objects.reasonably_complex_filter()からobjects最初の参照が私の実装では不可能であるという事実を見落としているように見えます。これは、次の3つの方法で修正できます。

  • reasonably_complex_filterをManagerとQuerySetの両方に実装します。
  • 唯一のフィルタである場合はTransaction.objects.all().reasonably_complex_filter()を使用してください。
  • QuerySetManagerの両方でコードを重複させずにこのメソッドを実装するソリューションについては、Marcus Whybrowの答えを参照してください。

どのオプションが最も望ましいかによって異なりますが、コードの重複を避けることを強くお勧めしますが(グローバルな方法でこれを克服することもできます)。ただし、この種の練習が1回だけ必要な場合や、複雑なフィルタを別のフィルタと組み合わせて使用​​する場合は、最後のオプションはオーバーヘッドの面でコストがかかる可能性があります。

0

私は実際に別の方法で行くことになりました。それは私のカスタムメソッドの最後にfilter呼び出しをチェーンする必要があることが判明したので、私は任意のキーワード引数を取って、合理的に複雑なクエリの最後にfilter()呼び出しに渡す方法を修正した:

class TransactionManager(models.Manager): 

    def reasonably_complex_filter(self, **kwargs): 
     return self.get_query_set().filter(...).filter(**kwargs) 

私の目的のためにうまく動作すると思われ、QuerySetのサブクラス化より少しシンプルです。

14

これは、Zach Smithとベンの礼儀で、Django 1.3で動作することが知られている完全なソリューションです。

class Entry(models.Model): 
    objects = EntryManager() # don't forget this 

    is_public = models.BooleanField() 
    owner = models.ForeignKey(User) 


class EntryManager(models.Manager): 
    '''Use this class to define methods just on Entry.objects.''' 
    def get_query_set(self): 
     return EntryQuerySet(self.model) 

    def __getattr__(self, name, *args): 
     if name.startswith("_"): 
      raise AttributeError 
     return getattr(self.get_query_set(), name, *args) 

    def get_stats(self): 
     '''A sample custom Manager method.''' 
     return { 'public_count': self.get_query_set().public().count() } 


class EntryQuerySet(models.query.QuerySet): 
    '''Use this class to define methods on queryset itself.''' 
    def public(self): 
     return self.filter(is_public=True) 

    def by(self, owner): 
     return self.filter(owner=owner) 


stats = Entry.objects.get_stats()  
my_entries = Entry.objects.by(request.user).public() 

注:get_query_set()方法is now deprecated in Django 1.6。この場合はget_queryset()を使用してください。ジャンゴ1.7のよう

29

、能力to use a query set as a managerを添加した:

class PersonQuerySet(models.QuerySet): 
    def authors(self): 
     return self.filter(role='A') 

    def editors(self): 
     return self.filter(role='E') 

class Person(models.Model): 
    first_name = models.CharField(max_length=50) 
    last_name = models.CharField(max_length=50) 
    role = models.CharField(max_length=1, choices=(('A', _('Author')), 
                ('E', _('Editor')))) 
    people = PersonQuerySet.as_manager() 

を次のように得られた:

Person.people.authors(last_name='Dahl') 

また、custom lookupsを追加する機能も追加しました。

関連する問題