Django — отдельные строки / объекты, отличающиеся датой / днем от поля datetime

#python #django #postgresql #datetime

#python #django #postgresql #datetime

Вопрос:

Я уже давно искал и знаю о нескольких ответах на sof, но ни одно из решений не работает с моей стороны, даже если моя проблема довольно проста:

Что мне нужно (используя postgres django 1.10): у меня много строк со множеством повторяющихся дат (= дней) в поле даты и времени. Я хочу, чтобы набор запросов содержал одну строку / объект на каждую дату / день.

 fk | col1 | colX | created (type: datetime)
----------------------------------------------
1  | info | info | 2016-09-03 08:25:52.142617 00:00 <- get it (time does not matter)
1  | info | info | 2016-09-03 16:26:52.142617 00:00
2  | info | info | 2016-09-03 11:25:52.142617 00:00
1  | info | info | 2016-09-14 16:26:52.142617 00:00 <- get it (time does not matter)
3  | info | info | 2016-09-14 11:25:52.142617 00:00
1  | info | info | 2016-09-25 23:25:52.142617 00:00 <- get it (time does not matter)
1  | info | info | 2016-09-25 16:26:52.142617 00:00
1  | info | info | 2016-09-25 11:25:52.142617 00:00
2  | info | info | 2016-09-25 14:27:52.142617 00:00
2  | info | info | 2016-09-25 16:26:52.142617 00:00
3  | info | info | 2016-09-25 11:25:52.142617 00:00
etc.
 

Каков наилучший (производительность pythionic / django) способ сделать это. В моей модели / таблице будет много строк (> миллионов).

РЕДАКТИРОВАТЬ 1

Сначала результаты должны быть отфильтрованы по fk (например, ГДЕ fk = 1).

Я уже пробовал самые очевидные вещи, такие как

 MyModel.objects.filter(fk=1).order_by('created__date').di‌​stinct('created__dat‌​e') 
 

но получил следующую ошибку:

django.core.exceptions.Ошибка поля: не удается преобразовать ключевое слово «дата» в поле. Объединение в «созданном» не разрешено.

…та же ошибка с all() и соответствующим упорядочением через класс Meta вместо запроса-метода order_by() …

Может быть, кто-нибудь знает больше об этой ошибке в данном конкретном случае?

Ответ №1:

Это, похоже, невозможно, учитывая текущую реализацию Django, поскольку для этого потребуется использовать расширенные серверные функции БД (например, оконные функции Postgres).

Самое близкое, что у вас есть, это использовать агрегации :

 MyModel.objects.annotate(
    created_date=TruncDay('created')
).values('created_date').annotate(id=Min('id'))
 

Это будет агрегироваться по аналогичным датам и получать минимальный идентификатор.

 [{'created_date': datetime.date(2017, 3, 16), 'id': 146},
 {'created_date': datetime.date(2017, 3, 28), 'id': 188},
 {'created_date': datetime.date(2017, 3, 24), 'id': 178},
 {'created_date': datetime.date(2017, 3, 23), 'id': 171},
 {'created_date': datetime.date(2017, 3, 22), 'id': 157}] ...
 

Если вам нужны целые объекты, вы можете связать это с a .values_list() и другим набором запросов, что приведет к подзапросу:

 MyModel.objects.filter(
    id__in=MyModel.objects.annotate(
        created_date=TruncDay('created')
    ).values('created_date').annotate(id=Min('id')).values_list(
        'id', flat=True
    )
)
 

К вашему сведению, это приводит к следующему запросу

 SELECT
    "myapp_mymodel"."id",
    "myapp_mymodel"."created",
    "myapp_mymodel"."col1",
    "myapp_mymodel"."colX"
FROM "myapp_mymodel"
WHERE "myapp_mymodel"."id" IN (
    SELECT MIN(U0."id") AS "id"
    FROM "myapp_mymodel" U0
    GROUP BY DATE(U0."created")
)
 

Ответ №2:

Я только что столкнулся с аналогичной проблемой — не с order_by() or distinct() , а с filter() . Я использую Django 1.9, но здесь это может не иметь никакого значения.

В одном из моих приложений в одной модели filter(datetime_field__date__lt=(date(2016, 12, 5))) работает нормально, в другой модели в другом приложении в том же проекте я получаю ту же ошибку, что и вы.

В моем случае это выглядит так, как будто django-money (https://github.com/django-money/django-money ) вызывает проблему. Насколько я могу судить, money_manager() функция from djmoney.models.managers прерывает __date поиск (https://docs.djangoproject.com/en/1.9/ref/models/querysets/#date ).

Когда я, например, присоединяю другой менеджер, не названный, к соответствующей модели, не вводя его, поиск снова работает нормально, без каких-либо других изменений в модели или базе данных. objects testmanager = models.Manager() money_manager() __date

Я еще не нашел полностью удовлетворяющего решения, но, может быть, вы также используете django-money или другое стороннее приложение, которое возится с менеджером по умолчанию? Возможно, обратная трассировка дает какие-либо подсказки о том, в каком пакете может быть проблема.

Моя трассировка выглядит так:
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/[...]/python3.4/site-packages/django/db/models/manager.py", line 122, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/[...]/python3.4/site-packages/djmoney/models/managers.py", line 164, in wrapper
args, kwargs = _expand_money_kwargs(model, args, kwargs, exclusions)
File "/[...]/python3.4/site-packages/djmoney/models/managers.py", line 136, in _expand_money_kwargs
elif isinstance(_get_field(model, name), MoneyField):
File "/[...]/python3.4/site-packages/djmoney/models/managers.py", line 63, in _get_field
field = qs.setup_joins(parts, opts, alias)[0]
File "/[...]/python3.4/site-packages/django/db/models/sql/query.py", line 1405, in setup_joins
names, opts, allow_many, fail_on_missing=True)
File "/[...]/python3.4/site-packages/django/db/models/sql/query.py", line 1373, in names_to_path
" not permitted." % (names[pos 1], name))
django.core.exceptions.FieldError: Cannot resolve keyword 'date' into field. Join on 'my_datetime_field' not permitted.

Комментарии:

1. К вашему сведению — проблема, с которой я столкнулся, django-money действительно была ошибкой, и теперь она уже исправлена в главной ветке: github.com/django-money/django-money/issues/241

Ответ №3:

вы можете использовать набор запросов для получения результатов из вашей таблицы с помощью distinct для созданного значения, потому что вы используете postgresql.

Возможно, подобный запрос должен выполнить эту работу :

 MyModel.objects.all().distinct('created__date')
 

Я также отсылаю вас к документации по набору запросов django: https://docs.djangoproject.com/fr/1.10/ref/models/querysets/#distinct

Комментарии:

1. если это работает так, как я думаю, не так ли .distinct('created__date') ?

2. Да, это верно, не видел «время не имеет значения» в сообщении. Отредактированный ответ.

3. …это одно из наиболее очевидных решений, которое при попытке выполнить со следующей ошибкой:

4. мой запрос немного отличается:

5. MyModel.objects.filter(xyz=123).order_by('created__date').distinct('created__date') django.core.exceptions.Ошибка поля: не удается преобразовать ключевое слово «дата» в поле. Объединение в «созданном» не разрешено.