рдирдорд╕реНрдХрд╛рд░! рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдореИрдВ рдЖрдкрдХреЛ рдЗрд╕ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж рджреЗрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ, рдЬреЛ рд╣рдорд╛рд░реЗ рдЖрд╡реЗрджрди рдореЗрдВ Django REST рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рд╕рдВрдпреЛрдЬрди рдореЗрдВ рдЕрддреНрдпрдВрдд рдЙрдкрдпреЛрдЧреА рд╕рд╛рдмрд┐рдд рд╣реБрдИред
рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдореИрдВ рд╕рдВрдмрдВрдзрд┐рдд рдореЙрдбрд▓реЛрдВ рдХреЗ рдХреНрд╖реЗрддреНрд░реЛрдВ рджреНрд╡рд╛рд░рд╛ рдХреНрд░рдордмрджреНрдз рдХрд░рдиреЗ, рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рдиреЗ рдФрд░ рдСрд░реНрдбрд░ рдХрд░рдиреЗ рдХреА рд╕рдорд╕реНрдпрд╛ рд╕реЗ рдЬреВрдЭ рд░рд╣рд╛ рд╣реВрдВред рдЗрд╕ рдЕрдВрдХ рдореЗрдВ рдореИрдВ рдХреЗрд╡рд▓ рдЖрджреЗрд╢ рджреЗрдиреЗ рдкрд░ рдзреНрдпрд╛рди рджреЗрдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ред
рдореБрдЭреЗ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдореЙрдбрд▓ рдХреЗ рд╕рд╛рде рдЕрдкрдиреЗ рдЗрд░рд╛рджреЗ рд╕реНрдкрд╖реНрдЯ рдХрд░рдиреЗ рджреЗрдВ:
# models.py
from django.contrib.auth.models import User
from django.db import models
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT, null=False, blank=False)
created = models.DateTimeField(null=False, blank=False, auto_now_add=True)
submitted = models.DateTimeField(null=True, blank=True)
def __str__(self):
return f'Order #{self.id}'
class Product(models.Model):
name = models.CharField(null=False, blank=False, max_length=256)
price = models.DecimalField(null=False, blank=False, decimal_places=2, max_digits=12)
def __str__(self):
return self.name
class OrderLine(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT, null=False, blank=False, related_name='order_lines')
quantity = models.IntegerField(null=False, blank=False)
product_price = models.DecimalField(null=False, blank=False, decimal_places=2, max_digits=12)
total_price = models.DecimalField(null=False, blank=False, decimal_places=2, max_digits=12)
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=False, blank=False, related_name='order_lines')
def __str__(self):
return f'{self.order}: {self.product.name} x{self.quantity}'
рдлрд┐рд░, рдЖрдЗрдП рдмрд┐рдирд╛ рдХрд┐рд╕реА рдлрд╝рд┐рд▓реНрдЯрд░рд┐рдВрдЧ рдФрд░ рдСрд░реНрдбрд░рд┐рдВрдЧ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд┐рдП DRF рд╡реНрдпреВрд╕реЗрдЯ рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░реЗрдВ:
# views.py
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend, FilterSet, OrderingFilter
from .models import Order
class OrderFilterSet(FilterSet):
pass
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer # the definition of OrderSerializer is irrelevant and not shown here
filter_backends = (DjangoFilterBackend, OrderingFilter)
filterset_class = OrderFilterSet
ordering_fields = ()
class Meta:
model = Order
рдЗрд╕ рдореБрджреНрджреЗ рдХреЗ рджрд╛рдпрд░реЗ рдореЗрдВ рдореЗрд░рд╛ рд▓рдХреНрд╖реНрдп рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдЗрди рдХреНрд╖реЗрддреНрд░реЛрдВ рджреНрд╡рд╛рд░рд╛ рдСрд░реНрдбрд░ рдХреА рдЕрдиреБрд░реЛрдзрд┐рдд рд╕реВрдЪреА рдХреЛ рдСрд░реНрдбрд░ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрдирд╛ рд╣реИ:
ordering=[-]created
- рдЖрджреЗрд╢ рдирд┐рд░реНрдорд╛рдг рддрд┐рдерд┐ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдЖрджреЗрд╢ ( [-]
рдПрдХ рд╡реИрдХрд▓реНрдкрд┐рдХ -
рдХрд╛ рд╕рдВрдХреЗрдд рджреЗрддрд╛ рд╣реИ рддрд╛рдХрд┐ рдЖрджреЗрд╢ рджрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗред)ordering=[-]user
- рдСрд░реНрдбрд░ рджреЗрдиреЗ рд╡рд╛рд▓реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдкреВрд░реЗ рдирд╛рдо рд╕реЗ рдСрд░реНрдбрд░ рдХрд░реЗрдВordering=[-]total_quantity
- рдСрд░реНрдбрд░ рдореЗрдВ рдЙрддреНрдкрд╛рджреЛрдВ рдХреА рдХреБрд▓ рдорд╛рддреНрд░рд╛ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдСрд░реНрдбрд░ рдХрд░реЗрдВordering_fields
ViewSet
рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ рдЬреЛ рдХреЗрд╡рд▓ рдореЙрдбрд▓ рдлрд╝реАрд▓реНрдб рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдСрд░реНрдбрд░ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, p.1ред рд╕реМрднрд╛рдЧреНрдп рд╕реЗ, рд╣рдо https://django-filter.readthedocs.io/en/master/ref/filters.html#orderingfilter рдореЗрдВ рд╡рд░реНрдгрд┐рдд filters.OrderingFilter
рдХреЛ рдЙрдк-рд╡рд░реНрдЧреАрдХреГрдд рдХрд░рдиреЗ рдХреА рдЕрдзрд┐рдХ рдЙрдиреНрдирдд рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
# views.py
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend, FilterSet, OrderingFilter
from .models import Order
class OrderOrderingFilter(OrderingFilter):
def __init__(self):
super().__init__(fields={
# example: model field
# Order by order creation date
'created': 'created',
# example: expression on related model
# Order by user full name
'user': '', # ???
# example: aggregate expression
'total_quantity': '' # ???
})
class OrderFilterSet(FilterSet):
ordering = OrderOrderingFilter()
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer # the definition of OrderSerializer is irrelevant and not shown here
filter_backends = (DjangoFilterBackend,)
filterset_class = OrderFilterSet
class Meta:
model = Order
рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЗрд╕ рдЙрдиреНрдирдд рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╕рдордп рднреА, django-filter рдХреЛ рдлрд╝реАрд▓реНрдб рдирд╛рдо рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рд╕реЗ рдмрдЪрдиреЗ рдХрд╛ рдПрдХрдорд╛рддреНрд░ рддрд░реАрдХрд╛ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд┐рдП рдЧрдП рдХреНрд╡реЗрд░реАрд╕реЗрдЯ рдореЗрдВ annotate
'рджреНрд╡рд╛рд░рд╛ рд╣реИ:
# ... the rest is omitted to brevity ...
class OrderOrderingFilter(OrderingFilter):
def __init__(self):
super().__init__(fields={
# ... the rest is omitted to brevity ...
'user_order': 'user',
})
def filter(self, qs, value):
if value:
qs = self._annotate(qs)
return super().filter(qs, value)
def _annotate(self, qs, value):
if 'user' in value or '-user' in value:
qs = qs.annotate(user_order=functions.Concat(
models.F('user__first_name'),
models.Value(' '),
models.F('user__last_name'),
))
return qs
рдХреБрдЫ рд▓реЛрдЧ рдХрд╣ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣рд╛рдВ .annotate
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдПрдХ рднрдпрд╛рдирдХ рд╡рд┐рдЪрд╛рд░ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ https://docs.djangoproject.com/en/2.1 рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдЕрдиреБрд╕рд╛рд░ .annotate
рдХреЛ .filter
$ рдХреЗ рд╕рд╛рде рдордЬрд╝рдмреВрддреА рд╕реЗ рдирд╣реАрдВ рдорд┐рд▓рд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред /рд╡рд┐рд╖рдп/рдбреАрдмреА/рдПрдЧреНрд░реАрдЧреЗрд╢рди/#рдСрд░реНрдбрд░ -рдСрдл-рдПрдиреЛрдЯреЗрдЯ-рдПрдВрдб-рдлрд┐рд▓реНрдЯрд░-рдХреНрд▓реЙрдЬред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЬрдм рддрдХ рдЖрдк рдЕрдкрдиреЗ .annotate
рдХреЙрд▓ рдореЗрдВ рдПрдХрддреНрд░реАрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рддрдм рддрдХ рдЖрдк рдареАрдХ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЕрдм рддрдХ рдЕрдЪреНрдЫреЗ рд╣реИрдВред (_рдореИрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдмрд╛рдж рдореЗрдВ рдПрдХрддреНрд░реАрдХрд░рдг рдкрд░ рд▓реМрдЯреВрдВрдЧрд╛_)
рдирд╡реАрдирддрдо рдХреЛрдб рдирдореВрдиреЗ рдХреЗ рд╕рд╛рде рдПрдХ рдФрд░ рдореБрджреНрджрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдХреЛрдб рджреЛрд╣рд░рд╛рд╡рджрд╛рд░ рд╣реИ рдФрд░ рдмреЗрд╕ рдХреНрд▓рд╛рд╕ рдореЗрдВ рдирд┐рдХрд╛рд▓рд╛ рдЬрд╛рдирд╛ рдмреЗрд╣рддрд░ рд╣реИ рдЬрд┐рд╕реЗ рдЖрд╕рд╛рдиреА рд╕реЗ рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рдкреНрд░рд╛рдкреНрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдЕрднреА рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ рдЕрдкрдирд╛ рдЦреБрдж OrderingFilter
рд╡рд░реНрдЧ рд▓рд┐рдЦрд╛ рд╣реИ, рдЬреЛ django-filter рджреНрд╡рд╛рд░рд╛ рдкреНрд░рджрд╛рди рдХрд┐рдП рдЧрдП рдПрдХ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреВрд░реНрдг рд╡рд┐рдХрд▓реНрдк рд╣реИред рдЗрд╕рдХрд╛ рдкреВрд░рд╛ рд╕реЛрд░реНрд╕ рдХреЛрдб рдиреАрдЪреЗ рд╣реИред рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣ django-filter рд╕реЗ рдмрд╣реБрдд рдХреБрдЫ рдЙрдзрд╛рд░ рд▓реЗрддрд╛ рд╣реИ:
import typing
from django.db import models
from django.db.models import expressions
from django.forms.utils import pretty_name
from django.utils.translation import ugettext_lazy as _
from django_filters import filters
from django_filters.constants import EMPTY_VALUES
__all__ = ('OrderingFilter',)
class OrderingFilter(filters.BaseCSVFilter, filters.ChoiceFilter):
"""
An alternative to django_filters.filter.OrderingFilter that allows to specify any Django ORM "expression" for ordering.
Usage examples:
class MyOrderingFilter(ddf.OrderingFilter):
def __init__(self):
super().__init__(fields={
# a model field
'id': 'id'
# an expression
'published_by':
functions.Concat(
expressions.F(f'published_user__first_name'),
expressions.Value(' '),
expressions.F(f'published_user__last_name')
),
# a complete field descriptor with custom field label
'reported_by': {
'label': 'Reporter',
'desc_label': 'Reporter (descending)', # optional, would be derived from 'label' anyway
'expr': functions.Concat(
expressions.F(f'reported_user__first_name'),
expressions.Value(' '),
expressions.F(f'reported_user__last_name')
),
}
})
For more information about expressions, please see the official Django documentation at
https://docs.djangoproject.com/en/2.1/ref/models/expressions/
"""
_fields: typing.Mapping[str, 'FieldDescriptor']
def __init__(self, fields: typing.Mapping[str, typing.Any]):
self._fields = normalize_fields(fields)
super().__init__(choices=build_choices(self._fields))
# <strong i="15">@override</strong>
def filter(self, qs: models.QuerySet, value: typing.Union[typing.List[str], None]):
return qs if value in EMPTY_VALUES else qs.order_by(*(expr for expr in map(self.get_ordering_expr, value)))
def get_ordering_expr(self, param) -> expressions.Expression:
descending = param.startswith('-')
param = param[1:] if descending else param
field_descriptor = self._fields.get(param)
return None if field_descriptor is None else \
field_descriptor.expr if not descending else field_descriptor.expr.desc()
def normalize_fields(fields: typing.Mapping[str, typing.Any]) -> typing.Mapping[str, 'FieldDescriptor']:
return dict((
param_name,
FieldDescriptor(param_name, {'expr': normalize_expr(field)} if isinstance(field, (str, expressions.Expression)) else field)
) for param_name, field in fields.items())
def normalize_expr(expr: typing.Union[str, expressions.Expression]):
return models.F(expr) if isinstance(expr, str) else expr
descending_fmt = _('%s (descending)')
class FieldDescriptor:
expr: models.Expression
def __init__(self, param_name: str, data: typing.Mapping[str, typing.Any]):
self.expr = normalize_expr(data['expr'])
self.label = data.get('label', _(pretty_name(param_name)))
self.desc_label = data.get('desc_label', descending_fmt.format(self.label))
def build_choices(fields: typing.Mapping[str, 'FieldDescriptor']):
choices = []
for param_name, field_descriptor in fields.items():
choices.append((param_name, field_descriptor.label))
choices.append((f'-{param_name}', field_descriptor.desc_label))
return choices
рдЗрд╕ рд╡рд░реНрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, OrdersOrderingFilter
рдЕрдЪреНрдЫрд╛ рдФрд░ рд╕рдВрдХреНрд╖рд┐рдкреНрдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ:
# ... the rest is omitted to brevity ...
class OrderOrderingFilter(ddf.OrderingFilter):
def __init__(self):
super().__init__(fields={
# ... the rest is omitted to brevity ...
'user': functions.Concat(
models.F('user__first_name'),
models.Value(' '),
models.F('user__last_name'),
),
})
рдпрд╣ рдЙрд╕ рдкреНрд░рд╢реНрди рдХреЛ рд╕рд╛рдордиреЗ рд▓рд╛рддрд╛ рд╣реИ рдЬрд┐рд╕рдХреЗ рд╕рд╛рде рдореИрдВ рдпрд╣рд╛рдВ рдЖрдпрд╛ рдерд╛:
_ django_filters.rest_framework.OrderingFilter
рдореЗрдВ Django ORM рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдХреЗ рд╕рд╛рде рдСрд░реНрдбрд░рд┐рдВрдЧ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЖрдк рдХреНрдпрд╛ рд╕реЛрдЪрддреЗ рд╣реИрдВ? _
рд▓реЗрдХрд┐рди рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рдХрд┐ рдЖрдк рдЙрддреНрддрд░ рджреЗрдВ, рдореИрдВ рдЖрдкрдХреЛ рдпрд╛рдж рджрд┐рд▓рд╛ рджреВрдВ рдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреЗрд╡рд▓ рдкреАрдкреА 1 рдФрд░ 2 рд╣рд▓ рд╣реИрдВред p.3 рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдореЗрдВ рдПрдХ рдПрдХрддреНрд░реАрдХрд░рдг рд╡реНрдпрдВрдЬрдХ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА:
aggregates.Sum(models.F('order_lines__quantity'))
рд╣рдо рдЗрд╕ рд╡реНрдпрдВрдЬрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ .annotate
рдореЗрдВ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ, рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕реЗ QuserySet рдлрд╝рд┐рд▓реНрдЯрд░рд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдорд┐рд▓рд╛рдиреЗ рдХреЗ рдкрд░рд┐рдгрд╛рдо рдЕрдкреНрд░рддреНрдпрд╛рд╢рд┐рдд рд╣реЛрдВрдЧреЗред рд╣рдо рдЗрд╕ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рд╕реАрдзреЗ Query.order(...)
рдкрд░ рдХреЙрд▓ рдореЗрдВ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐
django.core.exceptions.FieldError: Using an aggregate in order_by() without also including it in annotate() is not allowed: Sum(F(order_lines__quantity))
рддреЛ p.3 рдкрд╣реБрдВрдЪ рд╕реЗ рдмрд╛рд╣рд░ рд╣реИ, рдПрдХ рд╕рдорд╛рдзрд╛рди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдЬреЛ рд╡реНрдпреВрд╕реЗрдЯ рд╕реЗ рд╢реБрд░реВ рд╣реЛрдиреЗ рд╡рд╛рд▓реА рд╣рд░ рдЪреАрдЬ рдХреЛ рдЫреЗрдж рджреЗрдЧрд╛ред рдЗрд╕рдХрд╛ django-filters рдХреА рддреБрд▓рдирд╛ рдореЗрдВ Django REST рдлреНрд░реЗрдорд╡рд░реНрдХ рд╕реЗ рдЕрдзрд┐рдХ рд▓реЗрдирд╛-рджреЗрдирд╛ рд╣реИред рдХреНрдпрд╛ рд╣рдореЗрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ django_filters.rest_framework.OrderingFilter
рдореЗрдВ рд╕рдореЗрдХрди рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рд╕рдорд░реНрдерди рдХреЗ рдмрд┐рдирд╛ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рд╕рдорд░реНрдерди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ? рдпрд╣ рдЕрдзрд┐рдХ рд▓рд╛рдн рд▓рд╛рдП рдмрд┐рдирд╛ рдХреЗрд╡рд▓ django-filter рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рднреНрд░рдорд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред
рдореИрдВ рдЖрдкрдХреА рд░рд╛рдп рд╕реБрдирдиреЗ рдХреЗ рд▓рд┐рдП рдЙрддреНрд╕реБрдХ рд╣реВрдВред рдореБрдЭреЗ рдкрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдкрдЪрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рд╕реА рдЬрд╛рдирдХрд╛рд░реА рд╣реИред рдЙрдореНрдореАрдж рд╣реИ, рдореЗрд░реА рдкрд░реАрдХреНрд╖рдг рдкрд░рд┐рдпреЛрдЬрдирд╛ рдорджрдж рдХрд░ рд╕рдХрддреА рд╣реИ: https://github.com/earshinov/django_sample/tree/master/django_sample/ordering_by_expression
рд╣рд╛рдп @earshinovред рд░рд┐рдкреЛрд░реНрдЯ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред рдмрд╣реБрдд рд╣реА рд░реЛрдЪрдХред рдореБрдЭреЗ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕реЛрдЪрдиреЗ рджреЛред рдорд╛рд░реНрдЪ рдореЗрдВ рдореБрдЭреЗ рдкрд┐рдВрдЧ рдХрд░реЗрдВ рдЕрдЧрд░ рдореИрдВрдиреЗ рддрдм рддрдХ рдЬрд╡рд╛рдм рдирд╣реАрдВ рджрд┐рдпрд╛ рд╣реИред рдореИрдВ
рдПрдХ рдФрд░ рд╡рд┐рдЪрд╛рд░: рдПрдХ рдХрджрдо рдЖрдЧреЗ рдЬрд╛рдирд╛ рдФрд░ рдлрд╝рд┐рд▓реНрдЯрд░ рдХреЛ рдПрдХ рдирд╣реАрдВ, рдмрд▓реНрдХрд┐ рдХрдИ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдпреЛрдВ рдпрд╛ рдлрд╝реАрд▓реНрдб рдирд╛рдореЛрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрдирд╛ рд╕рдВрднрд╡ рд╣реИред
рдкрд╣рд▓реЗ (рд╡рд┐рдХрд▓реНрдк 1):
class OrderOrderingFilter(ddf.OrderingFilter):
def __init__(self):
super().__init__(fields={
'user': functions.Concat(
models.F('user__first_name'),
models.Value(' '),
models.F('user__last_name'),
),
})
рдХреЗ рдмрд╛рдж (рд╡рд┐рдХрд▓реНрдк 2):
class OrderOrderingFilter(ddf.OrderingFilter):
def __init__(self):
super().__init__(fields={
'user': ('user__first_name', 'user__last_name'),
})
рджреЛрдиреЛрдВ рд╣реА рдорд╛рдорд▓реЛрдВ рдореЗрдВ ordering=user
рдкрд╣рд▓реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдкрд╣рд▓реЗ рдирд╛рдо рд╕реЗ рдбреЗрдЯрд╛ рдСрд░реНрдбрд░ рдХрд░рддрд╛ рд╣реИ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХрд╛ рджреВрд╕рд░рд╛ рдирд╛рдо рджреВрд╕рд░рд╛, рд▓реЗрдХрд┐рди рд╡рд┐рдХрд▓реНрдк 2 рд▓рд┐рдЦрдирд╛ рдЖрд╕рд╛рди рд╣реЛрддрд╛ рд╣реИ, рдФрд░ рдЬрдм рдбреАрдмреА рдкреНрд░рд╢реНрдиреЛрдВ рдХреА рдмрд╛рдд рдЖрддреА рд╣реИ рддреЛ рд╢рд╛рдпрдж рдЕрдзрд┐рдХ рдХреБрд╢рд▓ рд╣реЛрддрд╛ рд╣реИред
рд╣реИрд▓реЛ @carlongibson ,
рдЖрдкрдиреЗ рдореБрдЭреЗ рдореИрдЪ рдореЗрдВ рдЖрдкрдХреЛ рдкрд┐рдВрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣рд╛ред рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдордИ рд╣реИ :)
рднрд╛рд╡реЛрдВ (рдПрдХ рдпрд╛ рдПрдХрд╛рдзрд┐рдХ) рджреНрд╡рд╛рд░рд╛ рдЖрджреЗрд╢ рджреЗрдиреЗ рдХреЗ рдЕрдкрдиреЗ рдкреНрд░рд╕реНрддрд╛рд╡ рдкрд░ рд╡рд╛рдкрд╕ рд▓реМрдЯрддреЗ рд╣реБрдП, рдореБрдЭреЗ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдкрд╣реЗрд▓реА рдХрд╛ рдПрдХ рд▓рд╛рдкрддрд╛ рд╣рд┐рд╕реНрд╕рд╛ рд╣реИред
рдореИрдВ рдЪрд╛рд░ рдмреБрдирд┐рдпрд╛рджреА рддрд╛рд▓рд┐рдХрд╛ рд╕рдВрдЪрд╛рд▓рди рджреЗрдЦрддрд╛ рд╣реВрдВ:
рдПред рдХреНрд░рдордмрджреНрдзрддрд╛
рдмреАред ~рдкреЗрдЬрд┐рдиреЗрд╢рди~ (рдЗрд╕ рдЪрд░реНрдЪрд╛ рдХреЗ рд▓рд┐рдП рдЕрдкреНрд░рд╛рд╕рдВрдЧрд┐рдХ)
рд╕реАред рдЫрд╛рдирдиреЗ
рдбреАред рдЫрдВрдЯрд╛рдИ
DRF рдФрд░ django-filter рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЗрди рд╕рднреА рдХреЛ рд╕рдВрдмрдВрдзрд┐рдд рдореЙрдбрд▓реЛрдВ рдХреЗ рд╕рдорд░реНрдерди рдХреЗ рд╕рд╛рде рд▓рд╛рдЧреВ рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИ, рдЫрдБрдЯрд╛рдИ рдХреЛ рдЫреЛрдбрд╝рдХрд░ :
рдПред рдХреНрд░рдордмрджреНрдзрддрд╛ рдХреЗ рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдиреЗрд╕реНрдЯреЗрдб рдзрд╛рд░рд╛рд╡рд╛рд╣рд┐рдХ рд╣реИрдВред рд╕рд╛рде рд╣реА, рдЕрдЧрд░ рд╣рдореЗрдВ рдПрдХ рд╕рдореЗрдХрд┐рдд рдлрд╝реАрд▓реНрдб рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ ( count
рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕реЛрдЪреЗрдВ), рддреЛ рд╣рдо рдХреНрд╡реЗрд░реАрд╕реЗрдЯ .annotate()
рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
from django.db.models import aggregates, functions, F, Value
class ModelViewSet(...):
def get_queryset(self):
qs = Model.objects.all()
if self.action == 'list':
qs = qs.annotate(
author_full_name=functions.Trim(functions.Concat(
F('author__first_name'),
Value(' '),
F('author__last_name'),
)),
submodel_count=aggregates.Count('submodel'))
)
return qs
рд╕реАред Django рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдХреЛрдИ рдХрд╕реНрдЯрдо рдлрд╝рд┐рд▓реНрдЯрд░ рд▓рд╛рдЧреВ рдХрд░ рд╕рдХрддрд╛ рд╣реИ (рдиреАрдЪреЗ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджреЗрдЦреЗрдВ)ред рд╕рд╛рдорд╛рдиреНрдп рд╕реНрдХреЗрд▓рд░ рдлрд╝рд┐рд▓реНрдЯрд░ ( filters.NumberFilter
) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рд╕рдореЗрдХрд┐рдд рдлрд╝реАрд▓реНрдб ( submodel_count
) рджреНрд╡рд╛рд░рд╛ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИред
from django_filters import filters
from django_filters.rest_framework import FilterSet
class ModelFilterSet(FilterSet):
author = UserFilter(field_name='author', label='author', lookup_expr='icontains')
class UserFilter(filters.Filter):
"""A django_filters filter that implements filtering by user's full name.
def filter(self, qs: QuerySet, value: str) -> QuerySet:
# first_name <lookup_expr> <value> OR last_name <lookup_expr> <value>
return qs if not value else qs.filter(
Q(**{f'{self.field_name}__first_name__{self.lookup_expr}': value}) |
Q(**{f'{self.field_name}__last_name__{self.lookup_expr}': value})
)
рдбреАред рдЫрдБрдЯрд╛рдИ рдХрд╛ рдХреЛрдИ рд╕рдорд╛рдзрд╛рди рдирд╣реАрдВ рд╣реИ :-(
рдпрд╣рд╛рдВ рд╣рдорд╛рд░реЗ рдХрд╕реНрдЯрдо OrderingFilter
рдХрд╛ рдкреВрд░реНрдг рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ, рдЬрд┐рд╕рдХрд╛ рд╣рдо рдЕрдм рддрдХ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВ:
class OrderingFilter(filters.BaseCSVFilter, filters.ChoiceFilter):
"""An alternative to :class:`django_filters.filters.OrderingFilter` that allows to specify any Django ORM expression for ordering.
Usage example:
.. code-block:: python
from django.db import models
from django.db.models import aggregates, expressions, fields
import ddl
class OrderOrderingFilter(ddl.OrderingFilter):
def __init__(self):
super().__init__(fields={
# a model field
'created': 'created'
# an expression
'submitted': expressions.ExpressionWrapper(
models.Q(submitted_date__isnull=False),
output_field=fields.BooleanField()
),
# multiple fields or expressions
'user': ('user__first_name', 'user__last_name'),
# a complete field descriptor with custom field label
'products': {
'label': 'Total number of items in the order',
# if not specified, `desc_label` would be derived from 'label' anyway
'desc_label': 'Total number of items in the order (descending)',
'expr': aggregates.Sum('order_lines__quantity'),
# it is also possible to filter by multiple fields or expressions here
#'exprs': (...)
},
})
For more information about expressions, see the official Django documentation at
https://docs.djangoproject.com/en/dev/ref/models/expressions/
"""
_fields: typing.Mapping[str, 'FieldDescriptor']
def __init__(self, fields: typing.Mapping[str, typing.Any]):
self._fields = normalize_fields(fields)
super().__init__(choices=build_choices(self._fields))
# <strong i="7">@override</strong>
def filter(self, qs: models.QuerySet, value: typing.Union[typing.List[str], None]):
return qs if value in EMPTY_VALUES else qs.order_by(*(itertools.chain(*(self.__get_ordering_exprs(param) for param in value))))
def __get_ordering_exprs(self, param) -> typing.Union[None, typing.List[expressions.Expression]]:
descending = param.startswith('-')
param = param[1:] if descending else param
field_descriptor = self._fields.get(param)
return () if field_descriptor is None else \
field_descriptor.exprs if not descending else \
(expr.desc() for expr in field_descriptor.exprs)
def normalize_fields(fields: typing.Mapping[str, typing.Any]) -> typing.Mapping[str, 'FieldDescriptor']:
return dict((
param_name,
FieldDescriptor(param_name, field if isinstance(field, collections.Mapping) else {'exprs': normalize_exprs(field)})
) for param_name, field in fields.items())
def normalize_exprs(exprs: typing.Union[
typing.Union[str, expressions.Expression],
typing.List[typing.Union[str, expressions.Expression]]
]) -> typing.List[expressions.Expression]:
# `exprs` is either a single expression or a Sequence of expressions
exprs = exprs if isinstance(exprs, collections.Sequence) and not isinstance(exprs, str) else (exprs,)
return [normalize_expr(expr) for expr in exprs]
def normalize_expr(expr: typing.Union[str, expressions.Expression]) -> expressions.Expression:
return models.F(expr) if isinstance(expr, str) else expr
descending_fmt = _('%s (descending)')
class FieldDescriptor:
exprs: typing.List[models.Expression]
def __init__(self, param_name: str, data: typing.Mapping[str, typing.Any]):
exprs = data.get('exprs') or data.get('expr')
if not exprs:
raise ValueError("Expected 'exprs' or 'expr'")
self.exprs = normalize_exprs(exprs)
self.label = data.get('label', _(pretty_name(param_name)))
self.desc_label = data.get('desc_label', descending_fmt.format(self.label))
def build_choices(fields: typing.Mapping[str, 'FieldDescriptor']):
choices = []
for param_name, field_descriptor in fields.items():
choices.append((param_name, field_descriptor.label))
choices.append((f'-{param_name}', field_descriptor.desc_label))
return choices
_Update_: normalize_fields
рдФрд░ рдЕрдиреНрдп рд╕рд╣рд╛рдпрдХ рд╡рд┐рдзрд┐рдпреЛрдВ рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╢рд╛рдорд┐рд▓ рд╣реИред
рдЕрдЪреНрдЫрд╛ред рдореИрдВ
рдкрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред
рдЯреАрдмреАрдПрдЪ рдореЗрд░реЗ рдкрд╛рд╕ рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рд╕реЛрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкрд▓ рднреА рдирд╣реАрдВ рд╣реИред
рдЖрдкрдХреЗ рд╕рднреА рдорд╣рд╛рди рд╡рд┐рдЪрд╛рд░реЛрдВ рдореЗрдВ рдХреНрдпрд╛ рдЖрдкрдХреЗ рдкрд╛рд╕ рдПрдХ рдЫреЛрдЯреЗ рд╕реЗ рдмрджрд▓рд╛рд╡ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рд╕реБрдЭрд╛рд╡ рд╣реИ рдЬреЛ рд╣рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ? (рд╢рд╛рдпрдж рдкреАрдЖрд░ рдХреЗ рд╕рд╛рде рдЖрдЧреЗ рдмрдврд╝рдирд╛ рдЖрд╕рд╛рди рд╣реИ)ред
рд╣рд╛рдБ, рдпрд╣ рддреБрд░рдВрдд рд╕реНрдкрд╖реНрдЯ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдРрд╕реЗ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рдкрд┐рдЫрдбрд╝реЗ рд╕рдВрдЧрдд рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рдП, рдореБрдЭреЗ рдЗрд╕ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЬрд▓реНрдж рд╣реА рдПрдХ рдкреАрдЖрд░ рдХреА рдЙрдореНрдореАрдж рди рдХрд░реЗрдВ (рдЙрди рдЬрдЧрд╣реЛрдВ рдкрд░ рдЫреБрдЯреНрдЯреА рд╣реЛрдЧреА рдЬрд╣рд╛рдВ рдЗрдВрдЯрд░рдиреЗрдЯ рджреБрд░реНрд▓рдн рд╣реИ)ред
рдХреЛрдИ рдмрд╛рдд рдирд╣реАрдВред рдореИрдВ
рдпрд╣рд╛рдВ рдХреЛрдИ рдЬрд▓реНрджреА рдирд╣реАрдВ рд╣реИред рдмреЗрд╣рддрд░ рд╣реЛрдЧрд╛ рдХрд┐ рд╣рдо рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ, рдпрджрд┐ рдмрд┐рд▓реНрдХреБрд▓ред
@carltongibson , рдареАрдХ рд╣реИ, рд╣рдордиреЗ рдереЛрдбрд╝рд╛ рд╡рд┐рдЪрд╛рд░ рдХрд┐рдпрд╛, рд▓реЗрдХрд┐рди OrderingFilter
рдХреЗ рдкреБрд░рд╛рдиреЗ рдФрд░ рдирдП рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╕рдВрдпреЛрдЬрди рдХрд╛ рдПрдХ рд╕реНрд╡реАрдХрд╛рд░реНрдп рддрд░реАрдХрд╛ рдирд╣реАрдВ рдорд┐рд▓рд╛ред
рдЕрдЧрд░ рд╣рдо рд╕рдм рдХреБрдЫ рдПрдХ рд╡рд░реНрдЧ рдореЗрдВ рд░рдЦрдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВ, рддреЛ рд╕рдмрд╕реЗ рдмрдбрд╝реА рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдкреБрд░рд╛рдиреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ рдлрд╝реАрд▓реНрдб model_field
- parameter_name
рдЬреЛрдбрд╝реЗ рдХреЛ рд╕реНрдЯреЛрд░ рдХрд░рддреЗ рд╣реИрдВ, рдЬрдмрдХрд┐ рдирдП рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ рдпрд╣ рд╡рд┐рдкрд░реАрдд parameter_name
рд╣реИред model_field
(рдпрд╣ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП, рдХреНрдпреЛрдВрдХрд┐ model_field
рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдкрд╛рд░рд┐рдд рдХреА рдЬрд╛ рд╕рдХрддреА рд╣реИ, рдЬрд┐рд╕реЗ рдХреЗрд╡рд▓ рдПрдХ рдореВрд▓реНрдп рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рд╢рдмреНрджрдХреЛрд╢ рдореЗрдВ рдПрдХ рдХреБрдВрдЬреА рдореЗрдВ рдирд╣реАрдВ) .
рддрдХрдиреАрдХреА рд░реВрдк рд╕реЗ, "рдкреБрд░рд╛рдиреЗ рддрд░реАрдХреЗ рд╕реЗ" рд╢рдмреНрджрдХреЛрд╢ рдХреА рд╡реНрдпрд╛рдЦреНрдпрд╛ рдХрд░рдХреЗ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рджреВрд░ рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИ рдпрджрд┐ рдЗрд╕рдореЗрдВ рдХреЗрд╡рд▓ рддрд╛рд░ рд╣реЛрдВ, рдФрд░ "рдирдП рддрд░реАрдХреЗ рд╕реЗ" рдЕрдиреНрдпрдерд╛ред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рджреНрд╡рд╛рд░рд╛ рдЖрджреЗрд╢ рджреЗрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЙрддреНрдкрдиреНрди рд╣реЛрдиреЗ рдкрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рд╢рдмреНрджрдХреЛрд╢ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпреЛрдВ рдХреЛ "рдлреНрд▓рд┐рдк" рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдФрд░ рд╢рдмреНрджрдХреЛрд╢ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпреЛрдВ рдХреЛ рд╡рд╛рдкрд╕ "рдлреНрд▓рд┐рдк" рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд╛рд╡рдзрд╛рди рд░рд╣реЗрдВ, рдХреНрдпрд╛ рдСрд░реНрдбрд░рд┐рдВрдЧ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдХреЛ рд╣рдЯрд╛ рджрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП ... рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдПрдХ рднрдпрд╛рдирдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЕрдиреБрднрд╡ рдХреА рддрд░рд╣ рд▓рдЧрддрд╛ рд╣реИред рдпрд╣ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рднреА рдЬрдЯрд┐рд▓ рдмрдирд╛ рджреЗрдЧрд╛ред
рдХреНрдпрд╛ рдЖрдкрдХреЛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ ExpressionOrderingFilter
рдЬреИрд╕реЗ рдХрд┐рд╕реА рднрд┐рдиреНрди рдирд╛рдо рд╕реЗ рдкреБрд░рд╛рдиреЗ рдХреЗ рд╕рд╛рде рдирдпрд╛ рдСрд░реНрдбрд░рд┐рдВрдЧрдлрд╝рд┐рд▓реНрдЯрд░ рдкреЗрд╢ рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИ?
рдпрд╣ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реИ, рдареАрдХ рдЗрд╕ рд╕рдордп рдореБрдЭреЗ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдереА! рдзрдиреНрдпрд╡рд╛рдж @earshinov!
рдореИрдВ рдХреБрдЫ рдРрд╕рд╛ рдХреИрд╕реЗ рдХрд░реВрдВрдЧрд╛:
MyModel.objects.all().order_by(F('price').desc(nulls_last=True))
рдЖрдкрдХреЗ рдСрд░реНрдбрд░рд┐рдВрдЧ рдлрд╝рд┐рд▓реНрдЯрд░ рдХреЗ рд╕рд╛рде?
o = ExpressionOrderingFilter(
fields={
'price': F('price').desc(nulls_last=True)
}
)
рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдХрд╛рдо рдХрд░рдиреЗ рд▓рдЧрддрд╛ рд╣реИ!
рдЕрдм рдХреЗрд╡рд▓ рдПрдХ рдЪреАрдЬ рдмрдЪреА рд╣реИ, рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдирд╛ рд╣реИ рдХрд┐ рдПрдХрд╛рдзрд┐рдХ рдлрд┐рд▓реНрдЯрд░ рдХреЛ рдХреИрд╕реЗ рд╕рдВрдпреЛрдЬрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдП, рдПрдХреЗред 'рдХреАрдордд' рдФрд░ 'рд╕реНрдЯреЙрдХ' рдПрдХ рдореЗрдВред
рдЕрдЪ - рдЗрд╕ рдкрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рджреЗрдирд╛ рд╢реБрд░реВ рдХрд░ рджрд┐рдпрд╛ рд▓реЗрдХрд┐рди рдореЗрд░рд╛ рдХрдВрдкреНрдпреВрдЯрд░ рдЦрд░рд╛рдм рд╣реЛ рдЧрдпрд╛ред
рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣рд╛рдВ рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рдкрд░рд┐рд╡рд░реНрддрди рд╕рдордЭрджрд╛рд░ рд╣реИрдВред model_field
- parameter_name
рдЬреЛрдбрд╝реА рдЙрд╕ рд╕рдордп рд╕рдордЭ рдореЗрдВ рдЖрдИ рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рдЖрдо рддреМрд░ рдкрд░ рдореЙрдбрд▓ рд╕реЗ рдЙрдЬрд╛рдЧрд░ рдкреИрд░рд╛/рдлреЙрд░реНрдо/рдЖрджрд┐ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдРрд╕рд╛ рдХреЛрдИ рдХрд╛рд░рдг рдирд╣реАрдВ рд╣реИ рдХрд┐ рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рдХреНрдпреЛрдВ рд╣реИред рдореИрдкрд┐рдВрдЧ рдХреА рдЕрджрд▓рд╛-рдмрджрд▓реА рдХрд░рдирд╛ рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИ, рдФрд░ рд╣рдореЗрдВ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдпреЛрдВ рдХрд╛ рд▓рд╛рдн рдЙрдард╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред
рд╕рд╛рде рд╣реА, рдореБрдЭреЗ рдирд╣реАрдВ рд▓рдЧрддрд╛ рдХрд┐ рдмрд╣рд┐рд╖реНрдХрд░рдг рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдмреЗрд╣рдж рдХрдард┐рди рд╣реЛрдЧреА, рдФрд░ рдореБрдЭреЗ рдЗрд╕рдореЗрдВ рдорджрдж рдХрд░рдиреЗ рдореЗрдВ рдЦреБрд╢реА рд╣реЛрдЧреАред рдореВрд▓ рд░реВрдк рд╕реЗ, fields
рдФрд░ field_labels
params
рдФрд░ param_labels
рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рд╣реЛ рдЬрд╛рдПрдВрдЧреЗред рдкрд╢реНрдЪ рд╕рдВрдЧрддрддрд╛ рдмрдирд╛рдП рд░рдЦрддреЗ рд╣реБрдП, рдФрд░ рдкреБрд░рд╛рдиреЗ рддрд░реНрдХреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдмрд╣рд┐рд╖реНрдХрд░рдг рдиреЛрдЯрд┐рд╕ рдЬрд╛рд░реА рдХрд░рддреЗ рд╣реБрдП, рдПрдХ рд╕реЗ рджреВрд╕рд░реЗ рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░рдирд╛ рдХрд╛рдлреА рдЖрд╕рд╛рди рд╣реИред
рдЕрд╡рд░реЛрд╣реА рдорд╛рдорд▓реЗ ( param
рдмрдирд╛рдо -param
) рдХреЗ рд▓рд┐рдП рдЬрдЯрд┐рд▓ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдпреЛрдВ рдХрд╛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдкрд╛рдВрддрд░рдг рд╡рд┐рдЪрд╛рд░ рдХрд░рдиреЗ рд╡рд╛рд▓реА рдПрдХ рдмрд╛рдд рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдпрджрд┐ рдЖрд░реЛрд╣реА .asc(nulls_last=True)
рд╣реИ, рддреЛ рдХреНрдпрд╛ рдЗрд╕рдХрд╛ рд╡реНрдпреБрддреНрдХреНрд░рдо .desc(nulls_first=True)
рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП, рдпрд╛ рдирд▓ рдХреЛ рдХреНрд░рдо рдХреА рджрд┐рд╢рд╛ рдХреА рдкрд░рд╡рд╛рд╣ рдХрд┐рдП рдмрд┐рдирд╛ рдЕрдВрддрд┐рдо рд░рд╣рдирд╛ рдЪрд╛рд╣рд┐рдП?
рдареАрдХ рд╣реИ, рд╕реБрдкрд░ @rpkilbyред
рд╣рдореЗрдВ рдпрд╣рд╛рдВ рдЖрдЧреЗ рдмрдврд╝рддреЗ рд╣реБрдП рджреЗрдЦрдХрд░ рдЦреБрд╢реА рд╣реБрдИред рдореЗрд░реА рдореБрдЦреНрдп рдЪрд┐рдВрддрд╛ рдпрд╣ рд╣реИ рдХрд┐ рд╣рдо рдЬреЛ рдХрд░ рд░рд╣реЗ рд╣реИрдВ рдЙрд╕рдХрд╛ рд╕рд╣реА рддрд░реАрдХреЗ рд╕реЗ рджрд╕реНрддрд╛рд╡реЗрдЬреАрдХрд░рдг рдХрд░реЗрдВред рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА df рдбреЙрдХреНрд╕ рдХреЛ рдереЛрдбрд╝рд╛ _terse_ рдвреВрдВрдврддреЗ рд╣реИрдВ, рдХреНрдпрд╛ рд╣рдо рдХрд╣реЗрдВрдЧреЗ ЁЯЩВ - рдПрдкреАрдЖрдИ рдЬреЛрдбрд╝рдиреЗ рдореЗрдВ рдЦреБрд╢реА рд╣реИ рд▓реЗрдХрд┐рди рд╣рдореЗрдВ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╕реНрдкрд╖реНрдЯ рд╣реИред
рдРрд╕реА рдЪреАрдЬ рдХреЗ рд▓рд┐рдП рдПрдХ рд╡реНрдпрдХреНрддрд┐ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рд╣реЛрдирд╛ рдЬрд░реВрд░реА рдирд╣реАрдВ рд╣реИред
@rpkilby , @carltongibson , рдЕрдЧрд░ рдЖрдкрдХреЛ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рдореЗрд░реЗ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдореЗрдВ рдореЗрд░реА рдорджрдж рдХреА рдЬрд╝рд░реВрд░рдд рд╣реИ, рддреЛ рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдореИрдВ рдХреБрдЫ рд╕рдордп рд▓рдЧрд╛ рд╕рдХрддрд╛ рд╣реВрдВред рд▓реЗрдХрд┐рди рдореБрдЭреЗ рджрд┐рд╢рд╛-рдирд┐рд░реНрджреЗрд╢ рдЪрд╛рд╣рд┐рдПред рдореИрдВ рдХрд╣рд╛рдВ рдФрд░ рдХреИрд╕реЗ рд╢реБрд░реВ рдХрд░реВрдВ?
рд╣рд╛рдп @earshinovред рдореИрдВ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реЛрдВ рдХрд╛ рдорд╕реМрджрд╛ рддреИрдпрд╛рд░ рдХрд░рдХреЗ рд╢реБрд░реВ рдХрд░реВрдВрдЧрд╛ред рд╣рдо рдХреНрдпрд╛ рдХрд╣рд╛рдиреА рд╕реБрдирд╛рддреЗ рд╣реИрдВ? рд╡рд╣рд╛рдВ рд╕реЗ рдХреЛрдб рдмрджрд▓ рдЬрд╛рддрд╛ рд╣реИред рдПрдХ рдЬрдирд╕рдВрдкрд░реНрдХ, рднрд▓реЗ рд╣реА рд╕рд┐рд░реНрдл рдПрдХ рдорд╕реМрджрд╛ рд╣рдореЗрдВ рдмрд╛рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рджреЗрддрд╛ рд╣реИред
рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрджрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ рдорд╛рдорд▓реЗ рд╣реИрдВ рддреЛ рдкрд░реАрдХреНрд╖рдг рдХрд░реЗрдВред рдЕрдВрддрд┐рдо рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рд╕реЗ рдореЗрд▓ рдЦрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдиреНрд╣реЗрдВ рд╕рдорд╛рдпреЛрдЬрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛ рд╕рдХрддреА рд╣реИ, рд▓реЗрдХрд┐рди рд╡рд┐рднрд┐рдиреНрди рдорд╛рдорд▓реЛрдВ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдирд╛ рдмрд╣реБрдд рдорджрджрдЧрд╛рд░ рд╣реЛрдЧрд╛ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, .asc
рдХреЛ .desc
рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рдирд╛, nulls_fist
/ nulls_last
рдХреЛ рд╕рдВрднрд╛рд▓рдирд╛
рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА
рдЕрдЪ - рдЗрд╕ рдкрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рджреЗрдирд╛ рд╢реБрд░реВ рдХрд░ рджрд┐рдпрд╛ рд▓реЗрдХрд┐рди рдореЗрд░рд╛ рдХрдВрдкреНрдпреВрдЯрд░ рдЦрд░рд╛рдм рд╣реЛ рдЧрдпрд╛ред
рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣рд╛рдВ рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рдкрд░рд┐рд╡рд░реНрддрди рд╕рдордЭрджрд╛рд░ рд╣реИрдВред
model_field
-parameter_name
рдЬреЛрдбрд╝реА рдЙрд╕ рд╕рдордп рд╕рдордЭ рдореЗрдВ рдЖрдИ рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рдЖрдо рддреМрд░ рдкрд░ рдореЙрдбрд▓ рд╕реЗ рдЙрдЬрд╛рдЧрд░ рдкреИрд░рд╛/рдлреЙрд░реНрдо/рдЖрджрд┐ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдРрд╕рд╛ рдХреЛрдИ рдХрд╛рд░рдг рдирд╣реАрдВ рд╣реИ рдХрд┐ рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рдХреНрдпреЛрдВ рд╣реИред рдореИрдкрд┐рдВрдЧ рдХреА рдЕрджрд▓рд╛-рдмрджрд▓реА рдХрд░рдирд╛ рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИ, рдФрд░ рд╣рдореЗрдВ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдпреЛрдВ рдХрд╛ рд▓рд╛рдн рдЙрдард╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИредрд╕рд╛рде рд╣реА, рдореБрдЭреЗ рдирд╣реАрдВ рд▓рдЧрддрд╛ рдХрд┐ рдмрд╣рд┐рд╖реНрдХрд░рдг рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдмреЗрд╣рдж рдХрдард┐рди рд╣реЛрдЧреА, рдФрд░ рдореБрдЭреЗ рдЗрд╕рдореЗрдВ рдорджрдж рдХрд░рдиреЗ рдореЗрдВ рдЦреБрд╢реА рд╣реЛрдЧреАред рдореВрд▓ рд░реВрдк рд╕реЗ,
fields
рдФрд░field_labels
params
рдФрд░param_labels
рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рд╣реЛ рдЬрд╛рдПрдВрдЧреЗред рдкрд╢реНрдЪ рд╕рдВрдЧрддрддрд╛ рдмрдирд╛рдП рд░рдЦрддреЗ рд╣реБрдП, рдФрд░ рдкреБрд░рд╛рдиреЗ рддрд░реНрдХреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдмрд╣рд┐рд╖реНрдХрд░рдг рдиреЛрдЯрд┐рд╕ рдЬрд╛рд░реА рдХрд░рддреЗ рд╣реБрдП, рдПрдХ рд╕реЗ рджреВрд╕рд░реЗ рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░рдирд╛ рдХрд╛рдлреА рдЖрд╕рд╛рди рд╣реИредрдЕрд╡рд░реЛрд╣реА рдорд╛рдорд▓реЗ (
param
рдмрдирд╛рдо-param
) рдХреЗ рд▓рд┐рдП рдЬрдЯрд┐рд▓ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдпреЛрдВ рдХрд╛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдкрд╛рдВрддрд░рдг рд╡рд┐рдЪрд╛рд░ рдХрд░рдиреЗ рд╡рд╛рд▓реА рдПрдХ рдмрд╛рдд рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдпрджрд┐ рдЖрд░реЛрд╣реА.asc(nulls_last=True)
рд╣реИ, рддреЛ рдХреНрдпрд╛ рдЗрд╕рдХрд╛ рд╡реНрдпреБрддреНрдХреНрд░рдо.desc(nulls_first=True)
рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП, рдпрд╛ рдирд▓ рдХреЛ рдХреНрд░рдо рдХреА рджрд┐рд╢рд╛ рдХреА рдкрд░рд╡рд╛рд╣ рдХрд┐рдП рдмрд┐рдирд╛ рдЕрдВрддрд┐рдо рд░рд╣рдирд╛ рдЪрд╛рд╣рд┐рдП?