์ฌ์ฉ๋ฒ์ ์๋ ์ค์ฒฉ๋ ๊ด๋ จ ๊ฐ์ฒด๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ๋ ๊ฒ์ ๋๋ค.
์ค์ฒฉ ํํ:
{
'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
์ด์ ๋ํ ํ ๋ฆฌํ์คํธ๋ฅผ ๊ณ ๋ คํ๊ณ ์ถ์ง๋ง ์ค์ค๋ก ํ ์๊ฐ์ด ์์ต๋๋ค.
์ด๋ฅผ ๊ตฌํํ๋ ค๋ ์ฌ๋์ ํํฐ๋ง์ ์๋ชป๋ ํ๋ ์ด๋ฆ์ด ์ฌ์ฉ๋ ๋ ํฉ๋ฆฌ์ ์ธ ๋์์ ๋ณด์ฅํด์ผ ํฉ๋๋ค. ์ต์ํ ๊ฒฐ๊ณผ๋ ์ ์์ ์ผ๋ก ๋ฐํ๋์ด์ผ ํฉ๋๋ค. (์ด์์ ์ผ๋ก๋ ์๋ชป๋ ํ๋ ์ด๋ฆ์ ๋ฌด์ํด์ผ ํ์ง๋ง ๋ค๋ฅธ ํ๋๋ ๊ทธ๋๋ก ๋์ด์ผ ํฉ๋๋ค.)
๊ณ ๋ คํ์ฌ ๋๋ ์ด๊ฒ์ ๋ซ์ ๊ฒ์ ๋๋ค. ๋๊ตฐ๊ฐ๊ฐ ํ ๋ฆฌํ์คํธ๋ฅผ ๋ฐํํ๊ณ ๊ทธ๊ฒ์ ์ฒ๋ฆฌํ๋ ํ ์คํธ๋ฅผ ํ๋ค๋ฉด ๋๋ ์ฌ๊ณ ํ ์ ์์ง๋ง, ๊ธฐ๋ณธ ํํฐ๋ง ํด๋์ค ๊ฐ๊ฐ์ ๋ณด๋ค ์์ ํ ๊ตฌํ์ ์ค์ ๋ก ๋๊ตฐ๊ฐ๊ฐ ์ 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์์ ์ฌ์ฉ ์ค๋จ ๊ฒฝ๊ณ ๋ฅผ ๋ฐ์์ํต๋๋ค. ์ด์ ๊ฐ๊ฐ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@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__'
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
ํ ๊ฐ์ฌํฉ๋๋ค :+1: . ๋งํฌ๋ ์์ ์ฌํญ์ ์๋ ค์ค๋๋ค. ์ด๊ฒ์ ์๋ํฉ๋๋ค