Django Rest Framework: Фильтрация QuerySet работает не так, как ожидалось
Я использую Django 3.2 и Django Rest Framework для API. У меня возникают трудности с тем, чтобы заставить его работать так, как я ожидаю. У меня есть таблица position с примерными данными, подобными следующим:
[
{ id: 1, position_date: '2022-01-01', symbol: 'AAPL', shares: 100 },
{ id: 2, position_date: '2022-01-01', symbol: 'TSLA', shares: 100 },
{ id: 3, position_date: '2022-01-01', symbol: 'GOOG', shares: 100 },
{ id: 4, position_date: '2022-01-02', symbol: 'AAPL', shares: 200 },
{ id: 5, position_date: '2022-01-02', symbol: 'TSLA', shares: 200 },
{ id: 6, position_date: '2022-01-02', symbol: 'GOOG', shares: 200 },
{ id: 7, position_date: '2022-01-05', symbol: 'AAPL', shares: 300 },
{ id: 8, position_date: '2022-01-05', symbol: 'TSLA', shares: 300 },
{ id: 9, position_date: '2022-01-05', symbol: 'GOOG', shares: 300 },
]
Эти данные содержат позиции акций на определенную дату. Эта ежедневная моментальная информация о позициях создается только в будние дни/дни, когда рынок открыт, поэтому в датах имеются пробелы на выходные/праздники.
Я хочу запросить позиции по position_date /api/v1/positions?position_date=<YYYY-MM-DD>, с оговоркой, что когда вы передаете дату, которая не связана напрямую с позицией, то вы получаете позиции для наибольшей даты, которая меньше или равна переданной дате. Например, если бы я передал дату воскресенья, с которой не связаны позиции, я бы ожидал получить позиции, начиная с пятницы. В приведенном выше примере данных, если бы я передал position_date '2022-01-04', я бы ожидал получить три записи с position_date '2022-01-02'.
Ниже представлен мой ViewSet. В настоящее время, если я не передаю position_date, он работает как ожидалось и возвращает последние позиции. Однако, когда я передаю position_date, он возвращает позиции только на эту дату. Похоже, что функция find max_date перезаписывается где-то/каким-то образом, когда передается дата. Есть идеи, как это исправить?
class PositionViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows positions to be viewed
"""
def get_queryset(self):
# only include params from filterset_fields list
params = dict(self.request.GET)
filtered_params = dict((k, params[k][0]) for k in self.filterset_fields if k in params and params[k][0])
# set default value to latest positions
if 'position_date' not in filtered_params:
filtered_params['position_date'] = date.today().isoformat()
# return all positions
if 'position_date' in filtered_params and ['position_date'] == 'all':
del filtered_params['position_date']
# find the greatest date that is lte to the passed in date
if 'position_date' in filtered_params:
max_date = Position.objects.filter(position_date__lte=filtered_params['position_date']).aggregate(position_date=Max('position_date'))['position_date'].isoformat()
# use the max date in place of original date
filtered_params['position_date'] = max_date
# fix some janky nested route issues between django and drf
filtered_kwargs = {k.replace('_pk', '_id'): v for k, v in self.kwargs.items()}
# merge kwargs and params
qs_filter = {**filtered_params, **filtered_kwargs}
qs = Position.objects.filter(**qs_filter)
return qs
serializer_class = PositionSerializer
filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
filterset_fields = [
'symbol',
'shares',
'position_date',
]
search_fields = [
'^symbol',
]
ordering_fields = [
'symbol',
'shares',
'position_date',
]
ordering = ['position_date', 'symbol']
Вы должны попробовать удалить position_date из filterset_fields, потому что я думаю, что DjangoFilterBackend переопределяет ваш queryset.
И еще один совет: вы должны делать все свои фильтрационные штуки относительно поля position_date в custom filter, а не в get_queryset функции.