67

DRYを壊さずにカスタムQuerySetとカスタムの両方を実装する方法を見つけようとしています。Managerこれは私がこれまでに持っているものです:

class MyInquiryManager(models.Manager):
    def for_user(self, user):
        return self.get_query_set().filter(
                    Q(assigned_to_user=user) |
                    Q(assigned_to_group__in=user.groups.all())
                )

class Inquiry(models.Model):   
    ts = models.DateTimeField(auto_now_add=True)
    status = models.ForeignKey(InquiryStatus)
    assigned_to_user = models.ForeignKey(User, blank=True, null=True)
    assigned_to_group = models.ForeignKey(Group, blank=True, null=True)
    objects = MyInquiryManager()

私がこのようなことをするまで、これはうまくいきます:

inquiries = Inquiry.objects.filter(status=some_status)
my_inquiry_count = inquiries.for_user(request.user).count()

QuerySetはと同じメソッドを持っていないため、これはすぐにすべてを壊しますManager。カスタムQuerySetクラスを作成してで実装しようとしましたMyInquiryManagerが、すべてのメソッド定義を複製することになります。

このスニペットも機能することがわかりましたがfor_user、再定義に大きく依存しているため、追加の引数を渡す必要がありget_query_setます。

QuerySetManagerサブクラスの両方ですべてのメソッドを再定義せずにこれを行う方法はありますか?

4

8 に答える 8

52

ジャンゴが変わった! 2009 年に書かれたこの回答のコードを使用する前に、残りの回答と Django のドキュメントをチェックして、より適切な解決策があるかどうかを確認してください。


これを実装した方法は、actualget_active_for_accountを custom のメソッドとして追加することQuerySetです。次に、マネージャーをオフにして機能させるには、単にトラップして__getattr__、それに応じて返すことができます

このパターンを再利用できるようにするために、Managerビットを別のモデル マネージャーに抽出しました。

custom_queryset/models.py

from django.db import models
from django.db.models.query import QuerySet

class CustomQuerySetManager(models.Manager):
    """A re-usable Manager to access a custom QuerySet"""
    def __getattr__(self, attr, *args):
        try:
            return getattr(self.__class__, attr, *args)
        except AttributeError:
            # don't delegate internal methods to the queryset
            if attr.startswith('__') and attr.endswith('__'):
                raise
            return getattr(self.get_query_set(), attr, *args)

    def get_query_set(self):
        return self.model.QuerySet(self.model, using=self._db)

それを取得したら、モデルで行う必要があるのはQuerySet、 をカスタム内部クラスとして定義し、マネージャーをカスタム マネージャーに設定することだけです。

your_app/models.py

from custom_queryset.models import CustomQuerySetManager
from django.db.models.query import QuerySet

class Inquiry(models.Model):
    objects = CustomQuerySetManager()

    class QuerySet(QuerySet):
        def active_for_account(self, account, *args, **kwargs):
            return self.filter(account=account, deleted=False, *args, **kwargs)

このパターンでは、次のいずれかが機能します。

>>> Inquiry.objects.active_for_account(user)
>>> Inquiry.objects.all().active_for_account(user)
>>> Inquiry.objects.filter(first_name='John').active_for_account(user)

カスタム user( ) で使用している場合は UPD から AbstractUser変更する必要があります。

class CustomQuerySetManager(models.Manager):

from django.contrib.auth.models import UserManager

class CustomQuerySetManager(UserManager):
    ***
于 2010-01-29T17:47:30.757 に答える
13

ミックスインを使用して、マネージャーとクエリセットにメソッドを提供できます。

__getattr__()これにより、アプローチの使用も回避されます。

from django.db.models.query import QuerySet

class PostMixin(object):
    def by_author(self, user):
        return self.filter(user=user)

    def published(self):
        return self.filter(published__lte=datetime.now())

class PostQuerySet(QuerySet, PostMixin):
    pass

class PostManager(models.Manager, PostMixin):
    def get_query_set(self):
        return PostQuerySet(self.model, using=self._db)
于 2013-08-30T14:24:07.757 に答える
2

T.ストーンのアプローチのわずかに改善されたバージョン:

def objects_extra(mixin_class):
    class MixinManager(models.Manager, mixin_class):
        class MixinQuerySet(QuerySet, mixin_class):
            pass

        def get_query_set(self):
            return self.MixinQuerySet(self.model, using=self._db)

    return MixinManager()

クラス デコレータは、次のように簡単に使用できます。

class SomeModel(models.Model):
    ...
    @objects_extra
    class objects:
        def filter_by_something_complex(self, whatever parameters):
            return self.extra(...)
        ...

更新: 非標準の Manager および QuerySet 基本クラスのサポート、たとえば @objects_extra(django.contrib.gis.db.models.GeoManager, django.contrib.gis.db.models.query.GeoQuerySet):

def objects_extra(Manager=django.db.models.Manager, QuerySet=django.db.models.query.QuerySet):
    def oe_inner(Mixin, Manager=django.db.models.Manager, QuerySet=django.db.models.query.QuerySet):
        class MixinManager(Manager, Mixin):
            class MixinQuerySet(QuerySet, Mixin):
                pass

            def get_query_set(self):
                return self.MixinQuerySet(self.model, using=self._db)

        return MixinManager()

    if issubclass(Manager, django.db.models.Manager):
        return lambda Mixin: oe_inner(Mixin, Manager, QuerySet)
    else:
        return oe_inner(Mixin=Manager)
于 2013-02-19T21:03:33.703 に答える
0

ソースコードに基づいてdjango 3.1.3、簡単な解決策を見つけました

from django.db.models.manager import BaseManager

class MyQuerySet(models.query.QuerySet):
      def my_custom_query(self):
          return self.filter(...)

class MyManager(BaseManager.from_queryset(MyQuerySet)):
     ...

class MyModel(models.Model):
     objects = MyManager()
于 2021-08-19T12:10:02.797 に答える
-1

以下は私にとってはうまくいきます。

def get_active_for_account(self,account,*args,**kwargs):
    """Returns a queryset that is 
    Not deleted
    For the specified account
    """
    return self.filter(account = account,deleted=False,*args,**kwargs)

これはデフォルトのマネージャーにあります。だから私は次のようなことをしていました:

Model.objects.get_active_for_account(account).filter()

しかし、それが副次的な管理者にとって機能しない理由はありません。

于 2010-01-29T17:06:26.073 に答える