Django-rest-framework: OrderingFilterのネストされた関連フィールドによる順序付けのサポート

作成日 2013年07月25日  ·  28コメント  ·  ソース: encode/django-rest-framework

使用例は、ネストされた関連オブジェクトによる並べ替えです。

ネストされた表現:

{
    'username': 'george',
    'email': '[email protected]'
    'stats': {
        'facebook_friends': 560,
        'twitter_followers': 4043,
        ...
    },
},
{
    'username': 'michael',
    'email': '[email protected]'
    'stats': {
        'facebook_friends': 256,
        'twitter_followers': 120,
        ...
    },
},
...

1つのオプションは、関連モデルのdjangoorm二重アンダースコア表記__をサポートすることです。

元。 ?ordering=stats__facebook_friendsはfacebook_friendsでソートします。

現在、順序付けは、クエリセットで指定された特定のモデルのフィールドに対してのみ機能します。

https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/filters.py#L125

最も参考になるコメント

ヒントをありがとう:+1:。 修正へのリンク。 これは動作します

from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields.related import ForeignObjectRel
from rest_framework.filters import OrderingFilter


class RelatedOrderingFilter(OrderingFilter):
    """
    Extends OrderingFilter to support ordering by fields in related models
    using the Django ORM __ notation
    """
    def is_valid_field(self, model, field):
        """
        Return true if the field exists within the model (or in the related
        model specified using the Django ORM __ notation)
        """
        components = field.split('__', 1)
        try:
            field, parent_model, direct, m2m = \
                model._meta.get_field_by_name(components[0])

            # reverse relation
            if isinstance(field, ForeignObjectRel):
                return self.is_valid_field(field.model, components[1])

            # foreign key
            if field.rel and len(components) == 2:
                return self.is_valid_field(field.rel.to, components[1])
            return True
        except FieldDoesNotExist:
            return False

    def remove_invalid_fields(self, queryset, ordering, view):
        return [term for term in ordering
                if self.is_valid_field(queryset.model, term.lstrip('-'))]

全てのコメント28件

これについてはプルリクエストを検討しますが、自分で行う時間はありません。

これを実装しようとしている人は、フィルタリングで誤ったフィールド名が使用された場合に適切な動作を保証する必要があります。 少なくとも、結果は問題なく返されるはずです。 (理想的には、誤ったフィールド名は無視する必要がありますが、他のフィールドはそのままにしておく必要があります。)

検討中、これを締めくくります。 誰かがプルリクエストを発行し、それを処理するテストを行う場合、私は再考するかもしれませんが、基本的なフィルタリングクラスのそれぞれのより完全な実装は、誰かがサードパーティのパッケージで本当にうまく取り組むことができるものであり、それを個別に維持することができます、メインドキュメントからリンクされています。

テストやドキュメントを書く時間がなかったのでプルリクエストはありませんが、これが他の人がこの問題を見つけるのに役立つ場合に備えて、私は以下を使用しています:

from django.db.models.fields import FieldDoesNotExist 
from rest_framework.filters import OrderingFilter

class RelatedOrderingFilter(OrderingFilter):
    """ 
    Extends OrderingFilter to support ordering by fields in related models
    using the Django ORM __ notation
    """
    def is_valid_field(self, model, field):
        """
        Return true if the field exists within the model (or in the related 
        model specified using the Django ORM __ notation)
        """
        components = field.split('__', 1)
        try:
            field, parent_model, direct, m2m = model._meta.get_field_by_name(components[0])
            if field.rel and len(components) == 2:
                return self.is_valid_field(field.rel.to, components[1])
            return True
        except FieldDoesNotExist:
            return False

    def remove_invalid_fields(self, queryset, ordering):
        return [term for term in ordering if self.is_valid_field(queryset.model, term.lstrip('-'))]

@rhunwicksお使いのバージョンでは、逆の関係の並べ替えは許可されていません。ここにある直接のfkのみがパッチが適用されたバージョンです。

class RelatedOrderingFilter(OrderingFilter):
    """
    Extends OrderingFilter to support ordering by fields in related models
    using the Django ORM __ notation
    """
    def is_valid_field(self, model, field):
        """
        Return true if the field exists within the model (or in the related
        model specified using the Django ORM __ notation)
        """
        components = field.split('__', 1)
        try:
            field, parent_model, direct, m2m = \
                model._meta.get_field_by_name(components[0])

            # reverse relation
            if isinstance(field, RelatedObject):
                return self.is_valid_field(field.model, components[1])

            # foreign key
            if field.rel and len(components) == 2:
                return self.is_valid_field(field.rel.to, components[1])
            return True
        except FieldDoesNotExist:
            return False

    def remove_invalid_fields(self, queryset, ordering, view):
        return [term for term in ordering
                if self.is_valid_field(queryset.model, term.lstrip('-'))]

pySilverのスニペットは私にとってはうまく機能しましたが、万が一これをDRFに取り込むことはできますか?

RelatedObjectはどこからインポートしますか? 私には利用できないようです

Python 3.4.3
Django==1.8.5
djangorestframework==3.2.3

@eldamir django.db.models.related.RelatedObject

ImportError: No module named 'django.db.models.related'

ああ、1.8から削除されたようです。https: //code.djangoproject.com/ticket/21414を参照して

ヒントをありがとう:+1:。 修正へのリンク。 これは動作します

from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields.related import ForeignObjectRel
from rest_framework.filters import OrderingFilter


class RelatedOrderingFilter(OrderingFilter):
    """
    Extends OrderingFilter to support ordering by fields in related models
    using the Django ORM __ notation
    """
    def is_valid_field(self, model, field):
        """
        Return true if the field exists within the model (or in the related
        model specified using the Django ORM __ notation)
        """
        components = field.split('__', 1)
        try:
            field, parent_model, direct, m2m = \
                model._meta.get_field_by_name(components[0])

            # reverse relation
            if isinstance(field, ForeignObjectRel):
                return self.is_valid_field(field.model, components[1])

            # foreign key
            if field.rel and len(components) == 2:
                return self.is_valid_field(field.rel.to, components[1])
            return True
        except FieldDoesNotExist:
            return False

    def remove_invalid_fields(self, queryset, ordering, view):
        return [term for term in ordering
                if self.is_valid_field(queryset.model, term.lstrip('-'))]

djangoレストフレームワークの最後のバージョンに問題はありませんでした:)

ordering_fieldsを正しく指定することを忘れないでください

注意点-Django> 1.10では、いくつかの方法が変更されました

具体的には、以下を変更する必要があります。

field, parent_model, direct, m2m = model._meta.get_field_by_name(components[0])

field = model._meta.get_field(components[0])

remove_invalid_fieldsの署名は次のように変更されました。

def remove_invalid_fields(self, queryset, ordering, view, request):

これにより、Django> 1.10の最終的な動作バージョンが得られます。

class RelatedOrderingFilter(OrderingFilter):
    """
    Extends OrderingFilter to support ordering by fields in related models
    using the Django ORM __ notation
    """
    def is_valid_field(self, model, field):
        """
        Return true if the field exists within the model (or in the related
        model specified using the Django ORM __ notation)
        """
        components = field.split('__', 1)
        try:

            field = model._meta.get_field(components[0])

            # reverse relation
            if isinstance(field, ForeignObjectRel):
                return self.is_valid_field(field.model, components[1])

            # foreign key
            if field.rel and len(components) == 2:
                return self.is_valid_field(field.rel.to, components[1])
            return True
        except FieldDoesNotExist:
            return False

    def remove_invalid_fields(self, queryset, fields, view, request):
        return [term for term in fields if self.is_valid_field(queryset.model, term.lstrip('-'))]

上記のコードではOneToOneFieldに問題があり、その修正が追加されています。

from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields.reverse_related import ForeignObjectRel, OneToOneRel

from rest_framework.filters import OrderingFilter


class RelatedOrderingFilter(OrderingFilter):
    """
    Extends OrderingFilter to support ordering by fields in related models.
    """    

    def is_valid_field(self, model, field):
        """
        Return true if the field exists within the model (or in the related
        model specified using the Django ORM __ notation)
        """
        components = field.split('__', 1)
        try:

            field = model._meta.get_field(components[0])

            if isinstance(field, OneToOneRel):
                return self.is_valid_field(field.related_model, components[1])

            # reverse relation
            if isinstance(field, ForeignObjectRel):
                return self.is_valid_field(field.model, components[1])

            # foreign key
            if field.rel and len(components) == 2:
                return self.is_valid_field(field.rel.to, components[1])
            return True
        except FieldDoesNotExist:
            return False

    def remove_invalid_fields(self, queryset, fields, view):
        return [term for term in fields
                if self.is_valid_field(queryset.model, term.lstrip('-'))]        

field.relとfield.rel.toは、Django> = 1.10で非推奨の警告を発します。 それらはそれぞれ次のようになります。

  • field.remote_field
  • field.model

@tomchristieは、このパッチがこの号で4年間長引いている(そして常に更新されている)ことを考慮して(

ちょうど私の0.2セント

このパッチは私には機能しますが、リリースバージョンにマージできますか?

前に述べたように、サードパーティにするか(推奨)、テストとドキュメントを含む適切なPRが必要です。

@filiperinaldiDjangoでは> = 1.10 field.rel.tofield.related_modelなると思います。 これが、ユニットテストに合格したパッチの最新バージョンです。

nb Django 1.11を使用しているので、ymmw

class RelatedOrderingFilter(filters.OrderingFilter):
    """

    See: https://github.com/tomchristie/django-rest-framework/issues/1005

    Extends OrderingFilter to support ordering by fields in related models
    using the Django ORM __ notation
    """
    def is_valid_field(self, model, field):
        """
        Return true if the field exists within the model (or in the related
        model specified using the Django ORM __ notation)
        """
        components = field.split('__', 1)
        try:
            field = model._meta.get_field(components[0])

            if isinstance(field, OneToOneRel):
                return self.is_valid_field(field.related_model, components[1])

            # reverse relation
            if isinstance(field, ForeignObjectRel):
                return self.is_valid_field(field.model, components[1])

            # foreign key
            if field.remote_field and len(components) == 2:
                return self.is_valid_field(field.related_model, components[1])
            return True
        except FieldDoesNotExist:
            return False

    def remove_invalid_fields(self, queryset, fields, ordering, view):
        return [term for term in fields
                if self.is_valid_field(queryset.model, term.lstrip('-'))]

さて、この号の4歳の誕生日に、この便利な注文をサポートするために、DRFと一緒にインストールする外部パッケージを一緒にハックしてみることにしました。

https://github.com/apiraino/djangorestframework_custom_filters_ordering

仕事を終えるために、次の日に取り組んでいきます(つまり、コードを適切にパッケージ化して因数分解し、テストを改良し、アクティブにサポートされているDjangoバージョンのサポートを確保する必要があります)。

もちろん、寄付は大歓迎です!

乾杯

参照番号5533。

私は非常に混乱しています。 これはデフォルトで機能するように見えますか?

私はこの問題に遭遇し、すべてのコメントを読み、 わかりました。

ただし、現在、API結果を並べ替えるために二重にネストされた関係に?ordering=job__customer__companyを使用しており、正常に機能しています。

@ halfnibble-これは#5533で修正されたと思います。

cc @carltongibson

@halfnibble 、どのバージョンを使用しましたか? 私は3.8.2を使用していますが、機能していません。 これらは私のバージョンです:

    "install_requires": [
        "django==2.0.3",
        "coreapi==2.3.3",
        "django-filter==1.1.0",
        "djangorestframework-filters==0.10.2.post0",
        "djangorestframework-queryfields==1.0.0",
        "djangorestframework==3.8.2",
        "django-bulk-update==2.2.0",
        "django-cors-headers==2.4.0",
        "django-rest-auth[with_social]==0.9.2",
        "drf-yasg==1.6.0",
        "django-taggit==0.22.2",
        "google-api-python-client==1.6.2",
        "markdown==2.6.11",
        "pygments==2.2.0",
        "xlrd==1.1.0",
        "xlsxwriter==0.9.8",
        "factory-boy==2.10.0",
        "psycopg2-binary==2.7.4",
        "django-admin-tools==0.8.1"
    ]

申し訳ありませんが、わかりました。 デフォルトでは、関連する順序パラメーターがordering_fields含まれている場合にのみ機能します。 ただし、より一般的な解決策には、パッチが必要です。

こんにちは。私はDRFに比較的慣れておらず、データベースの構造を公開せずに注文フィールドに注文を追加する方法を探していました。 たとえば、zを探したい場合は、x__y__zと記述し、それをパラメーターとしてエンドポイントに渡すと、構造が公開されます。 これは、zとだけ言い、それがORMの関連フィールドであるかどうかを確認するために関数をチェックしたい場合に、私が探しているものですか?

OrderingFilter.get_orderingオーバーライドしてみてください。 何かのようなもの...

class CustomOrderingFilter(OrderingFilter):
    def get_ordering(self, request, queryset, view):
        ordering = super().get_ordering(request, queryset, view)
        field_map = {
            'z': 'x__y__z',
        }
        return [field_map.get(o, o) for o in ordering]

@rpkilbyどうもありがとうございました! これはまさに私が探していたものです。 私は助けに感謝します。

これが私がまとめた解決策です:

class RelatedOrderingFilter(filters.OrderingFilter):
    _max_related_depth = 3

    <strong i="6">@staticmethod</strong>
    def _get_verbose_name(field: models.Field, non_verbose_name: str) -> str:
        return field.verbose_name if hasattr(field, 'verbose_name') else non_verbose_name.replace('_', ' ')

    def _retrieve_all_related_fields(
            self,
            fields: Tuple[models.Field],
            model: models.Model,
            depth: int = 0
    ) -> List[tuple]:
        valid_fields = []
        if depth > self._max_related_depth:
            return valid_fields
        for field in fields:
            if field.related_model and field.related_model != model:
                rel_fields = self._retrieve_all_related_fields(
                    field.related_model._meta.get_fields(),
                    field.related_model,
                    depth + 1
                )
                for rel_field in rel_fields:
                    valid_fields.append((
                        f'{field.name}__{rel_field[0]}',
                        self._get_verbose_name(field, rel_field[1])
                    ))
            else:
                valid_fields.append((
                    field.name,
                    self._get_verbose_name(field, field.name),
                ))
        return valid_fields

    def get_valid_fields(self, queryset: models.QuerySet, view, context: dict = None) -> List[tuple]:
        valid_fields = getattr(view, 'ordering_fields', self.ordering_fields)
        if not valid_fields == '__all_related__':
            if not context:
                context = {}
            valid_fields = super().get_valid_fields(queryset, view, context)
        else:
            valid_fields = [
                *self._retrieve_all_related_fields(queryset.model._meta.get_fields(), queryset.model),
                *[(key, key.title().split('__')) for key in queryset.query.annotations]
            ]
        return valid_fields
````

Then I add this to wherever I want to be able to order by all related fields:
```python
filter_backends = (RelatedOrderingFilter,)
ordering_fields = '__all_related__'
このページは役に立ちましたか?
0 / 5 - 0 評価