مثال على الاستخدام سيكون الفرز حسب كائن متداخل ذي صلة.
تمثيل متداخل:
{
'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.
يعمل الطلب حاليًا فقط مع الحقول الخاصة بالنموذج المحدد المحدد في مجموعة الاستعلام.
https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/filters.py#L125
كنت أفكر في سحب الطلبات لهذا الأمر ، لكنه ليس شيئًا لدي وقت لأفعله بنفسي.
يحتاج أي شخص يتطلع إلى تنفيذ هذا إلى ضمان السلوك المعقول عند استخدام أسماء حقول غير صحيحة في التصفية. في أقل تقدير ، يجب أن تظل النتائج جيدة. (من الناحية المثالية ، يجب تجاهل أي أسماء حقول غير صحيحة ، ولكن يجب ترك الحقول الأخرى فيها.)
عند النظر ، سأغلق هذا. إذا أصدر شخص ما طلب سحب واختبارات تتعامل معه ، فقد أعيد النظر ، ولكن التطبيقات الأكثر اكتمالاً لكل فئة من فئات التصفية الأساسية هي حقًا شيء يمكن لأي شخص معالجته بشكل جيد حقًا في حزمة طرف ثالث ، ويمكن بعد ذلك الحفاظ عليه بشكل منفصل ، وربطها من المستندات الرئيسية.
لا يوجد طلب سحب لأنه لم يكن لدي الوقت لكتابة الاختبارات أو المستندات ، ولكن في حال كان هذا يساعد الأشخاص الآخرين في العثور على هذه المشكلة ، فأنا أستخدم:
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 :)
لا تنس تحديد 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 معتبرا أن هذا التصحيح
فقط سنتان
هذا التصحيح يعمل معي ، هل يمكننا دمجه في إصدارات الإصدار؟
كما ذكرنا سابقًا ، اجعله طرفًا ثالثًا (مفضلًا) أو يحتاج إلى علاقات عامة مناسبة مع الاختبارات والتوثيق.
filiperinaldi أعتقد أنه في Django> = 1.10 field.rel.to
يصبح field.related_model
. ها هو أحدث إصدار من التصحيح الذي نجح في اجتياز اختباراتنا.
ملحوظة نحن نستخدم 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('-'))]
حسنًا ، في عيد الميلاد الرابع لهذه المشكلة ، قررت أن أجربها واختراق حزمة خارجية معًا ليتم تثبيتها جنبًا إلى جنب مع DRF لدعم هذا الطلب المفيد:
https://github.com/apiraino/djangorestframework_custom_filters_ordering
سأعمل على ذلك في الأيام التالية لإنهاء المهمة (أي أحتاج إلى حزم الكود ومعاملته بشكل صحيح ، وتحسين الاختبار وضمان الدعم لإصدارات Django المدعومة بشكل فعال).
نرحب بالمساهمات بالطبع!
هتافات
المرجع # 5533.
انا محتار جدا. يبدو أن هذا يعمل بشكل افتراضي؟
جئت إلى هذه المشكلة ، وقرأت جميع التعليقات ، وشرعت في تنفيذ الحل بواسطة apiraino ، ولكن بعد ذلك اكتشفت أنني كتبت اسم المجال ذي الصلة بشكل خاطئ.
ومع ذلك ، أستخدم الآن ?ordering=job__customer__company
لعلاقة مزدوجة متداخلة لطلب نتائج API وهي تعمل بشكل جيد.
halfnibble - أعتقد أن هذا تم إصلاحه في # 5533.
cc carltongibson
halfnibble ، ما هو الإصدار الذي
"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:. الرابط دعنا إلى الإصلاح. هذا يعمل