11

Django でカスタムフィールド ルックアップを作成するにはどうすればよいですか?

クエリセットをフィルタリングする場合、django は使用できるルックアップのセットを提供します: __contains__iexact__inなど。マネージャーに新しいルックアップを提供できるようにしたいので、たとえば、誰かが次のように言うことができます。

twentysomethings = Person.objects.filter(age__within5=25)

これを行うには、またはクラスをPersonサブクラス化する必要がありますか? それはどのように実装されますか?QuerySetManager

4

4 に答える 4

12

これを行うためのより柔軟な方法は、カスタム QuerySet とカスタム マネージャーを作成することです。オザンのコードからの作業:

class PersonQuerySet(models.query.QuerySet):
    def in_age_range(self, min, max):
        return self.filter(age__gte=min, age__lt=max)

class PersonManager(models.Manager):
    def get_query_set(self):
         return PersonQuerySet(self.model)

    def __getattr__(self, name):
        return getattr(self.get_query_set(), name)

class Person(models.Model):
    age = #...

    objects = PersonManager()

これにより、カスタム クエリを連鎖させることができます。したがって、これらのクエリは両方とも有効です。

Person.objects.in_age_range(20,30)

Person.objects.exclude(somefield = some_value).in_age_range(20, 30)
于 2010-02-05T22:04:29.997 に答える
7

フィールドルックアップを作成するのではなく、ベストプラクティスは、次のように少し見えるマネージャーメソッドを作成することです。

class PersonManger(models.Manager):
    def in_age_range(self, min, max):
        return self.filter(age__gte=min, age__lt=max)

class Person(models.Model):
    age = #...

    objects = PersonManager()

その場合、使用法は次のようになります。

twentysomethings = Person.objects.in_age_range(20, 30)
于 2010-02-04T22:08:33.963 に答える
6

まず、あなたが望むことを公に促進することを意図した Django の仕組みが整っていないことを言わせてください。

(編集 - 実際には Django 1.7 以降があります: https://docs.djangoproject.com/en/1.7/howto/custom-lookups/ )

つまり、本当にこれを達成したい場合は、メソッドをサブクラス化QuerySetしてオーバーライドします_filter_or_exclude()。次に、カスタムのみを返すカスタムマネージャーを作成しますQuerySet(またはDjangoのモンキーパッチQuerySet、yuck)。これをneo4djangoで行うのは、Neo4j 固有のQueryオブジェクトを構築する際に Django ORM クエリセット コードをできるだけ多く再利用するためです。

ザックの答えから適応した、このような(大まかに)何かを試してください。読者の演習として、フィールド ルックアップ解析の実際のエラー処理を残しました :)

class PersonQuerySet(models.query.QuerySet):
    def _filter_or_exclude(self, negate, *args, **kwargs):
        cust_lookups = filter(lambda s: s[0].endswith('__within5'), kwargs.items())
        for lookup in cust_lookups:
            kwargs.pop(lookup[0])
            lookup_prefix = lookup[0].rsplit('__',1)[0]
            kwargs.update({lookup_prefix + '__gte':lookup[1]-5,
                           lookup_prefix + '__lt':lookup[1]+5})
        return super(PersonQuerySet, self)._filter_or_exclude(negate, *args, **kwargs)

class PersonManager(models.Manager):
    def get_query_set(self):
         return PersonQuerySet(self.model)

class Person(models.Model):
    age = #...

    objects = PersonManager()

最後のコメント - 明らかに、カスタム フィールドのルックアップを連鎖させたい場合、これはかなり複雑になります。また、私は通常、これをもう少し機能的に記述し、パフォーマンスのために itertools を使用しますが、省略した方が明確であると考えました。楽しむ!

于 2011-11-26T22:38:50.473 に答える