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,
        ...
    },
},
...

ํ•œ ๊ฐ€์ง€ ์˜ต์…˜์€ ๊ด€๋ จ ๋ชจ๋ธ์— ๋Œ€ํ•ด django orm ์ด์ค‘ ๋ฐ‘์ค„ ํ‘œ๊ธฐ๋ฒ• __์„ ์ง€์›ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ „. ?ordering=stats__facebook_friends ๋Š” facebook_friends๋ณ„๋กœ ์ •๋ ฌ๋ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ฃผ๋ฌธ์€ queryset์— ์ง€์ •๋œ ํŠน์ • ๋ชจ๋ธ์˜ ํ•„๋“œ์— ๋Œ€ํ•ด์„œ๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

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 ๋Œ“๊ธ€

์ด์— ๋Œ€ํ•œ ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ๊ณ ๋ คํ•˜๊ณ  ์‹ถ์ง€๋งŒ ์Šค์Šค๋กœ ํ•  ์‹œ๊ฐ„์ด ์—†์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋Š” ์‚ฌ๋žŒ์€ ํ•„ํ„ฐ๋ง์— ์ž˜๋ชป๋œ ํ•„๋“œ ์ด๋ฆ„์ด ์‚ฌ์šฉ๋  ๋•Œ ํ•ฉ๋ฆฌ์ ์ธ ๋™์ž‘์„ ๋ณด์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ ๊ฒฐ๊ณผ๋Š” ์ •์ƒ์ ์œผ๋กœ ๋ฐ˜ํ™˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (์ด์ƒ์ ์œผ๋กœ๋Š” ์ž˜๋ชป๋œ ํ•„๋“œ ์ด๋ฆ„์€ ๋ฌด์‹œํ•ด์•ผ ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ํ•„๋“œ๋Š” ๊ทธ๋Œ€๋กœ ๋‘์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.)

๊ณ ๋ คํ•˜์—ฌ ๋‚˜๋Š” ์ด๊ฒƒ์„ ๋‹ซ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๊ณ  ๊ทธ๊ฒƒ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ํ•œ๋‹ค๋ฉด ๋‚˜๋Š” ์žฌ๊ณ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ธฐ๋ณธ ํ•„ํ„ฐ๋ง ํด๋ž˜์Šค ๊ฐ๊ฐ์˜ ๋ณด๋‹ค ์™„์ „ํ•œ ๊ตฌํ˜„์€ ์‹ค์ œ๋กœ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ œ3์ž ํŒจํ‚ค์ง€์—์„œ ์ •๋ง ํ›Œ๋ฅญํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ  ๋ณ„๋„๋กœ ์œ ์ง€ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋ฌธ์„œ์—์„œ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ๋‚˜ ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•  ์‹œ๊ฐ„์ด ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๊ฒƒ์ด ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์ด ๋ฌธ์ œ๋ฅผ ์ฐพ๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

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 rest framework์˜ ๋งˆ์ง€๋ง‰ ๋ฒ„์ „์—๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค :)

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
  • ํ•„๋“œ ๋ชจ๋ธ

@tomchristie ๋Š” ์ด ํŒจ์น˜๊ฐ€ 4๋…„ ๋™์•ˆ ์ด ๋ฌธ์ œ์—์„œ ๋‚จ์•„ ์žˆ๊ณ (๊ทธ๋ฆฌ๊ณ  ์ง€์†์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๊ณ 

๊ทธ๋ƒฅ ๋‚ด 0.2 ์„ผํŠธ

์ด ํŒจ์น˜๊ฐ€ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋ฆด๋ฆฌ์Šค ๋ฒ„์ „์— ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์•ž์„œ ๋งํ–ˆ๋“ฏ์ด ํƒ€์‚ฌ๋กœ ๋งŒ๋“ค๊ฑฐ๋‚˜(์„ ํ˜ธ) ํ…Œ์ŠคํŠธ ๋ฐ ๋ฌธ์„œ์™€ ํ•จ๊ป˜ ์ ์ ˆํ•œ PR์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

@filiperinaldi ๋‚ด ์ƒ๊ฐ์— Django >= 1.10 field.rel.to ์€ field.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.

๋‚˜ ์—„์ฒญ ํ˜ผ๋ž€์Šค๋Ÿฌ์›Œ. ์ด๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๊นŒ?

๋‚˜๋Š” ์ด ๋ฌธ์ œ์— ์™€์„œ ๋ชจ๋“  ์ฃผ์„์„ ์ฝ๊ณ  @apiraino๋กœ ์†”๋ฃจ์…˜์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์ง€๋งŒ ๊ด€๋ จ ํ•„๋“œ์˜ ์ด๋ฆ„์„ ์ž˜๋ชป ์ž…๋ ฅํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด์ œ 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 ๋“ฑ๊ธ‰