【问题标题】:Django: filtering on an annotated datetime?Django:过滤带注释的日期时间?
【发布时间】:2012-03-18 04:53:55
【问题描述】:

我有一系列与用户相关的事件(带有时间戳):

# models.py

from django.db import models

from django.contrib.auth.models import User

class Event(models.Model):
    name = models.CharField(max_length=255, blank=True, db_index=True)
    time = models.DateTimeField(db_index=True)
    user = models.ForeignKey(User, related_name='events', null=True)

我想为每个用户找到第一个此类事件的 id(用于以后的子查询)。应该很简单吧?

activation_event_ids = User.objects  \
    .annotate(first_event_time=Min('events__time'))  \
    .filter(events__time='first_event_time')  \
    .values_list('events__id', flat=True)

但这会爆炸:

ValidationError                           Traceback (most recent call last)
/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/core/management/commands/shell.pyc in <module>()
      1 activation_event_ids = User.objects  \
      2     .annotate(first_event_time=Min('km_events__time'))  \
----> 3     .filter(km_events__time='first_event_time')  \
      4     .values_list(km_events__id, flat=True)

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/query.pyc in filter(self, *args, **kwargs)
    619         set.
    620         """
--> 621         return self._filter_or_exclude(False, *args, **kwargs)
    622 
    623     def exclude(self, *args, **kwargs):

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/query.pyc in _filter_or_exclude(self, negate, *args, **kwargs)
    637             clone.query.add_q(~Q(*args, **kwargs))
    638         else:
--> 639             clone.query.add_q(Q(*args, **kwargs))
    640         return clone
    641 

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/sql/query.pyc in add_q(self, q_object, used_aliases, force_having)
   1251                 else:
   1252                     self.add_filter(child, connector, q_object.negated,
-> 1253                             can_reuse=used_aliases, force_having=force_having)
   1254                 if force_having:
   1255                     self.having.end_subtree()

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/sql/query.pyc in add_filter(self, filter_expr, connector, negate, trim, can_reuse, process_extras, force_having)
   1186         else:
   1187             self.where.add((Constraint(alias, col, field), lookup_type, value),
-> 1188                 connector)
   1189 
   1190         if negate:

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/sql/where.pyc in add(self, data, connector)
     67 
     68         if hasattr(obj, "prepare"):
---> 69             value = obj.prepare(lookup_type, value)
     70             super(WhereNode, self).add((obj, lookup_type, annotation, value),
     71                 connector)

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/sql/where.pyc in prepare(self, lookup_type, value)
    316     def prepare(self, lookup_type, value):
    317         if self.field:
--> 318             return self.field.get_prep_lookup(lookup_type, value)
    319         return value
    320 

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/fields/__init__.pyc in get_prep_lookup(self, lookup_type, value)
    707         if lookup_type in ('month', 'day', 'week_day'):
    708             return int(value)
--> 709         return super(DateField, self).get_prep_lookup(lookup_type, value)
    710 
    711     def get_prep_value(self, value):

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/fields/__init__.pyc in get_prep_lookup(self, lookup_type, value)
    308             return value
    309         elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
--> 310             return self.get_prep_value(value)
    311         elif lookup_type in ('range', 'in'):
    312             return [self.get_prep_value(v) for v in value]

/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/fields/__init__.pyc in get_prep_value(self, value)
    799 
    800     def get_prep_value(self, value):
--> 801         value = self.to_python(value)
    802         if value is not None and settings.USE_TZ and timezone.is_naive(value):
    803             # For backwards compatibility, interpret naive datetimes in local


/home/gabriel/.virtualenvs/gordon/local/lib/python2.7/site-packages/django/db/models/fields/__init__.pyc in to_python(self, value)
    783 
    784         msg = self.error_messages['invalid'] % value
--> 785         raise exceptions.ValidationError(msg)
    786 
    787     def pre_save(self, model_instance, add):

ValidationError: [u"'first_event_time' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."]

我目前正在使用本地 SQLite 数据库。真的不可能过滤带注释的日期时间吗?还是我只是在做一些愚蠢的事情?

谢谢!

【问题讨论】:

    标签: django group-by django-orm


    【解决方案1】:

    如果要引用注解字段,需要使用F

    from django.db.models import F
    
    ...
    
    .filter(events__time=F('first_event_time'))
    

    参考见:https://docs.djangoproject.com/en/dev/topics/db/queries/#query-expressions

    【讨论】:

    • 哇。所以,是的,我只是在做一些愚蠢的事情。但是使用 F(),这个查询似乎花费了非常长的时间(它已经运行了大约一个小时),而没有那个过滤器(即只有注释)它会很快返回。我原以为注释会比过滤花费更长的时间。知道怎么了? (或者如何在不实际运行查询的情况下查看 SQL?inspecting queries after the fact 的常用方法不起作用,因为我必须杀死进程)
    • 使用 django-devserver 或 django-debug-toolbar 之类的东西。前者取代了 runserver,除其他外,还显示了在 shell 中运行的每个查询。后者在您的网站上显示为覆盖,还详细说明了所有已运行的查询以及许多其他有用信息。如果您愿意,您可以在技术上同时使用两者。
    • 我的理解是,这两个都只显示查询执行之后(DDT 肯定是这种情况,因为它显示来自上一个请求的查询已经渲染的页面),如果查询永远不会完成,这将无济于事。我将仔细研究 devserver,但我想一定有其他方法,隐藏在 Django 内部,以找出将执行的查询,而无需实际运行它。
    • 您可以尝试:str(MyModel.objects.all().query) 其中MyModel.objects.all() 是您正在使用的QuerySet
    猜你喜欢
    • 1970-01-01
    • 2011-06-22
    • 2012-04-05
    • 1970-01-01
    • 2021-03-10
    • 2018-01-25
    • 2020-03-11
    • 2021-06-09
    • 2016-04-28
    相关资源
    最近更新 更多