数据库函数

下面记述的类为用户提供了一些方法,来在Django中使用底层数据库提供的函数用于注解、聚合或者过滤器等操作。 函数也是expressions,所以可以像aggregate functions一样混合使用它们。

我们会在每个函数的实例中使用下面的模型:

class Author(models.Model):
    name = models.CharField(max_length=50)
    age = models.PositiveIntegerField(null=True, blank=True)
    alias = models.CharField(max_length=50, null=True, blank=True)
    goes_by = models.CharField(max_length=50, null=True, blank=True)

我们并不推荐在Coalesce上允许CharField,因为这会允许字段有两个“空值”,但是对于下面的null=True示例来说它很重要。

Cast

class Cast(expression, output_field)[source]
Django中的新功能1.10。

强制expression的结果类型为output_field

使用范例:

>>> from django.db.models import FloatField
>>> from django.db.models.functions import Cast
>>> Value.objects.create(integer=4)
>>> value = Value.objects.annotate(as_float=Cast('integer', FloatField())).get()
>>> print(value.as_float)
4.0

Coalesce

class Coalesce(*expressions, **extra)[source]

接受一个含有至少两个字段名称或表达式的列表,返回第一个非空的值(注意空字符串不被认为是一个空值)。 每个参与都必须是相似的类型,所以掺杂了文本和数字的列表会导致数据库错误。

使用范例:

>>> # Get a screen name from least to most public
>>> from django.db.models import Sum, Value as V
>>> from django.db.models.functions import Coalesce
>>> Author.objects.create(name='Margaret Smith', goes_by='Maggie')
>>> author = Author.objects.annotate(
...    screen_name=Coalesce('alias', 'goes_by', 'name')).get()
>>> print(author.screen_name)
Maggie

>>> # Prevent an aggregate Sum() from returning None
>>> aggregated = Author.objects.aggregate(
...    combined_age=Coalesce(Sum('age'), V(0)),
...    combined_age_default=Sum('age'))
>>> print(aggregated['combined_age'])
0
>>> print(aggregated['combined_age_default'])
None

警告

传递给MySQL上的Coalesce的Python值可能会转换为不正确的类型,除非显式转换为正确的数据库类型:

>>> from django.db.models import DateTimeField
>>> from django.db.models.functions import Cast, Coalesce
>>> from django.utils import timezone
>>> now = timezone.now()
>>> Coalesce('updated', Cast(now, DateTimeField()))

Concat

class Concat(*expressions, **extra)[source]

接受一个含有至少两个文本字段的或表达式的列表,返回连接后的文本。 每个参数都必须是文本或者字符类型。 如果你想把一个output_field和一个CharField()连接, 一定要告诉DjangoTextField()应该为TextField()类型。 当连接Value时,也需要指定output_field,如下例所示。

这个函数不会返回null。 在后端中,如果一个null参数导致了整个表达式都是null,Django会确保把每个null的部分转换成一个空字符串。

使用范例:

>>> # Get the display name as "name (goes_by)"
>>> from django.db.models import CharField, Value as V
>>> from django.db.models.functions import Concat
>>> Author.objects.create(name='Margaret Smith', goes_by='Maggie')
>>> author = Author.objects.annotate(
...     screen_name=Concat(
...         'name', V(' ('), 'goes_by', V(')'),
...         output_field=CharField()
...     )
... ).get()
>>> print(author.screen_name)
Margaret Smith (Maggie)

Greatest

class Greatest(*expressions, **extra)[source]

接受至少两个字段名称或表达式的列表,并返回最大值。 每个参与都必须是相似的类型,所以掺杂了文本和数字的列表会导致数据库错误。

使用范例:

class Blog(models.Model):
    body = models.TextField()
    modified = models.DateTimeField(auto_now=True)

class Comment(models.Model):
    body = models.TextField()
    modified = models.DateTimeField(auto_now=True)
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)

>>> from django.db.models.functions import Greatest
>>> blog = Blog.objects.create(body='Greatest is the best.')
>>> comment = Comment.objects.create(body='No, Least is better.', blog=blog)
>>> comments = Comment.objects.annotate(last_updated=Greatest('modified', 'blog__modified'))
>>> annotated_comment = comments.get()

annotated_comment.last_updated将是最新的blog.modifiedcomment.modified

警告

当一个或多个表达式可能为null时,Greatest的行为在数据库之间有所不同:

  • PostgreSQL: Greatest will return the largest non-null expression, or null if all expressions are null.
  • SQLite,Oracle和MySQL:如果任何表达式为nullGreatest将返回null

如果您知道一个明智的最小值作为默认值,PostgreSQL行为可以使用Coalesce进行仿真。

Least

class Least(*expressions, **extra)[source]

接受至少两个字段名称或表达式的列表,并返回最小值。 每个参与都必须是相似的类型,所以掺杂了文本和数字的列表会导致数据库错误。

警告

当一个或多个表达式可能为null时,Least的行为在数据库之间有所不同:

  • PostgreSQL: Least will return the smallest non-null expression, or null if all expressions are null.
  • SQLite,Oracle和MySQL:如果任何表达式为nullLeast将返回null

如果您知道一个明智的最大值作为默认值,PostgreSQL行为可以使用Coalesce进行仿真。

Length

class Length(expression, **extra)[source]

接受一个文本字段或表达式,返回值的字符个数。 如果表达式是null,长度也会是null。

使用范例:

>>> # Get the length of the name and goes_by fields
>>> from django.db.models.functions import Length
>>> Author.objects.create(name='Margaret Smith')
>>> author = Author.objects.annotate(
...    name_length=Length('name'),
...    goes_by_length=Length('goes_by')).get()
>>> print(author.name_length, author.goes_by_length)
(14, None)

它也可以注册为转换。 像这样:

>>> from django.db.models import CharField
>>> from django.db.models.functions import Length
>>> CharField.register_lookup(Length, 'length')
>>> # Get authors whose name is longer than 7 characters
>>> authors = Author.objects.filter(name__length__gt=7)

Lower

class Lower(expression, **extra)[source]

接受一个文本字符串或表达式,返回它的小写表示形式。

它还可以注册为Length中所述的转换。

使用范例:

>>> from django.db.models.functions import Lower
>>> Author.objects.create(name='Margaret Smith')
>>> author = Author.objects.annotate(name_lower=Lower('name')).get()
>>> print(author.name_lower)
margaret smith

Now

class Now[source]

返回数据库服务器执行查询的当前日期和时间,通常使用SQL CURRENT_TIMESTAMP

使用范例:

>>> from django.db.models.functions import Now
>>> Article.objects.filter(published__lte=Now())
<QuerySet [<Article: How to Django>]>

PostgreSQL注意事项

在PostgreSQL上,SQL CURRENT_TIMESTAMP返回当前事务开始的时间。 因此,对于跨数据库兼容性,Now()替代使用STATEMENT_TIMESTAMP 如果您需要交易时间戳,请使用django.contrib.postgres.functions.TransactionNow

Substr

class Substr(expression, pos, length=None, **extra)[source]

返回这个字段或者表达式的,以length位置开始,长度为pos的子字符串。 位置从下标为1开始,所以必须大于0。 如果lengthNone,会返回剩余的字符串。

使用范例:

>>> # Set the alias to the first 5 characters of the name as lowercase
>>> from django.db.models.functions import Substr, Lower
>>> Author.objects.create(name='Margaret Smith')
>>> Author.objects.update(alias=Lower(Substr('name', 1, 5)))
1
>>> print(Author.objects.get(name='Margaret Smith').alias)
marga

Upper

class Upper(expression, **extra)[source]

接受一个文本字符串或表达式,返回它的大写表示形式。

它还可以注册为Length中所述的转换。

使用范例:

>>> from django.db.models.functions import Upper
>>> Author.objects.create(name='Margaret Smith')
>>> author = Author.objects.annotate(name_upper=Upper('name')).get()
>>> print(author.name_upper)
MARGARET SMITH

日期函数

Django中的新功能1.10。

我们会在每个函数的实例中使用下面的模型:

class Experiment(models.Model):
    start_datetime = models.DateTimeField()
    start_date = models.DateField(null=True, blank=True)
    start_time = models.TimeField(null=True, blank=True)
    end_datetime = models.DateTimeField(null=True, blank=True)
    end_date = models.DateField(null=True, blank=True)
    end_time = models.TimeField(null=True, blank=True)

Extract

class Extract(expression, lookup_name=None, tzinfo=None, **extra)[source]

将日期的组件提取为数字。

Takes an expression representing a DateField or DateTimeField and a lookup_name, and returns the part of the date referenced by lookup_name as an IntegerField. Django通常使用数据库的提取函数,因此您可以使用数据库支持的任何lookup_name 可以传递通常由pytz提供的tzinfo子类,以提取特定时区中的值。

给定datetime 2015-06-15 23:30:01.000321 + 00:00,内置lookup_name

  • “年”:2015年
  • “月”:6
  • “天”:15
  • “周”:25
  • “week_day”:2
  • “小时”:23
  • “分钟”:30
  • “第二”:1

如果不同的时区如Australia/Melbourne在Django中处于活动状态,则在提取值之前将datetime转换为时区。 上述示例中墨尔本的时区抵消是+10:00。 当该时区处于活动状态时返回的值将与上述相同,除了:

  • “天”:16
  • “week_day”:3
  • “小时”:9

week_day

计算与大多数数据库和Python标准函数不同的week_day lookup_type This function will return 1 for Sunday, 2 for Monday, through 7 for Saturday.

Python中的等效计算是:

>>> from datetime import datetime
>>> dt = datetime(2015, 6, 15)
>>> (dt.isoweekday() % 7) + 1
2

week

week lookup_type基于ISO-8601计算,即一个星期从星期一开始。 第一个星期是大部分时间,即星期四或之前开始的一周。 返回值在1到52或53之间。

以上每个lookup_name具有相应的Extract子类(下面列出),通常应该使用,而不是更冗长的等价物。使用ExtractYear(...)而不是Extract(..., lookup_name ='year')

使用范例:

>>> from datetime import datetime
>>> from django.db.models.functions import Extract
>>> start = datetime(2015, 6, 15)
>>> end = datetime(2015, 7, 2)
>>> Experiment.objects.create(
...    start_datetime=start, start_date=start.date(),
...    end_datetime=end, end_date=end.date())
>>> # Add the experiment start year as a field in the QuerySet.
>>> experiment = Experiment.objects.annotate(
...    start_year=Extract('start_datetime', 'year')).get()
>>> experiment.start_year
2015
>>> # How many experiments completed in the same year in which they started?
>>> Experiment.objects.filter(
...    start_datetime__year=Extract('end_datetime', 'year')).count()
1

DateField提取

class ExtractYear(expression, tzinfo=None, **extra)[source]
lookup_name = 'year'
class ExtractMonth(expression, tzinfo=None, **extra)[source]
lookup_name ='month'
class ExtractDay(expression, tzinfo=None, **extra)[source]
lookup_name ='day'
class ExtractWeekDay(expression, tzinfo=None, **extra)[source]
lookup_name ='week_day'
class ExtractWeek(expression, tzinfo=None, **extra)[source]
Django中的新功能1.11。
lookup_name ='week'

这些在逻辑上等同于Extract('date_field', lookup_name) 每个类也是在DateFieldDateTimeField上注册为__(lookup_name)Transform,例如。 __year

由于DateField没有时间分量,只能处理日期部分的Extract子类可以与DateField一起使用:

>>> from datetime import datetime
>>> from django.utils import timezone
>>> from django.db.models.functions import (
...     ExtractDay, ExtractMonth, ExtractWeek, ExtractWeekDay, ExtractYear,
... )
>>> start_2015 = datetime(2015, 6, 15, 23, 30, 1, tzinfo=timezone.utc)
>>> end_2015 = datetime(2015, 6, 16, 13, 11, 27, tzinfo=timezone.utc)
>>> Experiment.objects.create(
...    start_datetime=start_2015, start_date=start_2015.date(),
...    end_datetime=end_2015, end_date=end_2015.date())
>>> Experiment.objects.annotate(
...     year=ExtractYear('start_date'),
...     month=ExtractMonth('start_date'),
...     week=ExtractWeek('start_date'),
...     day=ExtractDay('start_date'),
...     weekday=ExtractWeekDay('start_date'),
... ).values('year', 'month', 'week', 'day', 'weekday').get(
...     end_date__year=ExtractYear('start_date'),
... )
{'year': 2015, 'month': 6, 'week': 25, 'day': 15, 'weekday': 2}

DateTimeField提取

除了以下内容之外,上述列出的DateField的所有提取也可以在DateTimeField上使用。

class ExtractHour(expression, tzinfo=None, **extra)[source]
lookup_name ='hour'
class ExtractMinute(expression, tzinfo=None, **extra)[source]
lookup_name ='分钟'
class ExtractSecond(expression, tzinfo=None, **extra)[source]
lookup_name ='second'

这些在逻辑上等同于Extract('datetime_field', lookup_name) 每个类也是在DateTimeField上注册为__(lookup_name)Transform,例如。 __minute

DateTimeField示例:

>>> from datetime import datetime
>>> from django.utils import timezone
>>> from django.db.models.functions import (
...     ExtractDay, ExtractHour, ExtractMinute, ExtractMonth, ExtractSecond,
...     ExtractWeek, ExtractWeekDay, ExtractYear,
... )
>>> start_2015 = datetime(2015, 6, 15, 23, 30, 1, tzinfo=timezone.utc)
>>> end_2015 = datetime(2015, 6, 16, 13, 11, 27, tzinfo=timezone.utc)
>>> Experiment.objects.create(
...    start_datetime=start_2015, start_date=start_2015.date(),
...    end_datetime=end_2015, end_date=end_2015.date())
>>> Experiment.objects.annotate(
...     year=ExtractYear('start_datetime'),
...     month=ExtractMonth('start_datetime'),
...     week=ExtractWeek('start_datetime'),
...     day=ExtractDay('start_datetime'),
...     weekday=ExtractWeekDay('start_datetime'),
...     hour=ExtractHour('start_datetime'),
...     minute=ExtractMinute('start_datetime'),
...     second=ExtractSecond('start_datetime'),
... ).values(
...     'year', 'month', 'week', 'day', 'weekday', 'hour', 'minute', 'second',
... ).get(end_datetime__year=ExtractYear('start_datetime'))
{'year': 2015, 'month': 6, 'week': 25, 'day': 15, 'weekday': 2, 'hour': 23,
 'minute': 30, 'second': 1}

USE_TZTrue时,数据库将以UTC存储在数据库中。 如果Django中的时区不同,则在提取值之前,将datetime转换为该时区。 以下示例转换为墨尔本时区(UTC +10:00),这会更改返回的日,工作日和小时值:

>>> import pytz
>>> melb = pytz.timezone('Australia/Melbourne')  # UTC+10:00
>>> with timezone.override(melb):
...    Experiment.objects.annotate(
...        day=ExtractDay('start_datetime'),
...        weekday=ExtractWeekDay('start_datetime'),
...        hour=ExtractHour('start_datetime'),
...    ).values('day', 'weekday', 'hour').get(
...        end_datetime__year=ExtractYear('start_datetime'),
...    )
{'day': 16, 'weekday': 3, 'hour': 9}

将时区显式传递给Extract函数的行为方式相同,并且优先于活动时区:

>>> import pytz
>>> melb = pytz.timezone('Australia/Melbourne')
>>> Experiment.objects.annotate(
...     day=ExtractDay('start_datetime', tzinfo=melb),
...     weekday=ExtractWeekDay('start_datetime', tzinfo=melb),
...     hour=ExtractHour('start_datetime', tzinfo=melb),
... ).values('day', 'weekday', 'hour').get(
...     end_datetime__year=ExtractYear('start_datetime'),
... )
{'day': 16, 'weekday': 3, 'hour': 9}

Trunc

class Trunc(expression, kind, output_field=None, tzinfo=None, **extra)[source]

截断一个日期到一个重要的组件。

When you only care if something happened in a particular year, hour, or day, but not the exact second, then Trunc (and its subclasses) can be useful to filter or aggregate your data. 例如,您可以使用Trunc来计算每天的销售数量。

Trunc采用单个expression,表示DateFieldTimeFieldDateTimeField表示日期或时间部分的kindoutput_field,它们是DateTimeField()TimeField()DateField() 它将根据output_field返回日期时间,日期或时间,其中最多kind设置为最小值。 如果output_field被省略,它将默认为expressionoutput_field 通常由pytz提供的tzinfo子类可以传递到截断特定时区中的值。

给定日期时间2015-06-15 14:30:50.000321 + 00:00,内置kind

  • “年”:2015-01-01 00:00:00 + 00:00
  • “月”:2015-06-01 00:00:00 + 00:00
  • “天”:2015-06-15 00:00:00 + 00:00
  • “小时”:2015-06-15 14:00:00 + 00:00
  • “分钟”:2015-06-15 14:30:00 + 00:00
  • “第二”:2015-06-15 14:30:50 + 00:00

如果像Australia/Melbourne之间的不同时区在Django中处于活动状态,那么在该值被截断之前,datetime将被转换为新的时区。 上述示例中墨尔本的时区抵消是+10:00。 当此时区处于活动状态时返回的值将为:

  • “年”:2015-01-01 00:00:00 + 11:00
  • “月”:2015-06-01 00:00:00 + 10:00
  • “天”:2015-06-16 00:00:00 + 10:00
  • “小时”:2015-06-16 00:00:00 + 10:00
  • “分钟”:2015-06-16 00:30:00 + 10:00
  • “第二”:2015-06-16 00:30:50 + 10:00

该年度的偏移量为+11:00,因为结果已转换为夏令时。

上述每个kind具有相应的Trunc子类(下面列出),通常应该使用而不是更冗长的等价物。使用TruncYear(...)而不是Trunc(..., kind ='year')

子类都被定义为转换,但是它们没有注册到任何字段,因为明显的查找名称已经由Extract子类保留。

使用范例:

>>> from datetime import datetime
>>> from django.db.models import Count, DateTimeField
>>> from django.db.models.functions import Trunc
>>> Experiment.objects.create(start_datetime=datetime(2015, 6, 15, 14, 30, 50, 321))
>>> Experiment.objects.create(start_datetime=datetime(2015, 6, 15, 14, 40, 2, 123))
>>> Experiment.objects.create(start_datetime=datetime(2015, 12, 25, 10, 5, 27, 999))
>>> experiments_per_day = Experiment.objects.annotate(
...    start_day=Trunc('start_datetime', 'day', output_field=DateTimeField())
... ).values('start_day').annotate(experiments=Count('id'))
>>> for exp in experiments_per_day:
...     print(exp['start_day'], exp['experiments'])
...
2015-06-15 00:00:00 2
2015-12-25 00:00:00 1
>>> experiments = Experiment.objects.annotate(
...    start_day=Trunc('start_datetime', 'day', output_field=DateTimeField())
... ).filter(start_day=datetime(2015, 6, 15))
>>> for exp in experiments:
...     print(exp.start_datetime)
...
2015-06-15 14:30:50.000321
2015-06-15 14:40:02.000123

DateField truncation

class TruncYear(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='年'
class TruncMonth(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='month'

这些在逻辑上等同于Trunc('date_field', 种类) 它们截断日期的所有部分,直到kind,允许以较少精度进行分组或过滤日期。 expression可以具有DateFieldDateTimeFieldoutput_field

由于DateField没有时间分量,只能处理日期部分的Trunc子类可以与DateField一起使用:

>>> from datetime import datetime
>>> from django.db.models import Count
>>> from django.db.models.functions import TruncMonth, TruncYear
>>> from django.utils import timezone
>>> start1 = datetime(2014, 6, 15, 14, 30, 50, 321, tzinfo=timezone.utc)
>>> start2 = datetime(2015, 6, 15, 14, 40, 2, 123, tzinfo=timezone.utc)
>>> start3 = datetime(2015, 12, 31, 17, 5, 27, 999, tzinfo=timezone.utc)
>>> Experiment.objects.create(start_datetime=start1, start_date=start1.date())
>>> Experiment.objects.create(start_datetime=start2, start_date=start2.date())
>>> Experiment.objects.create(start_datetime=start3, start_date=start3.date())
>>> experiments_per_year = Experiment.objects.annotate(
...    year=TruncYear('start_date')).values('year').annotate(
...    experiments=Count('id'))
>>> for exp in experiments_per_year:
...     print(exp['year'], exp['experiments'])
...
2014-01-01 1
2015-01-01 2

>>> import pytz
>>> melb = pytz.timezone('Australia/Melbourne')
>>> experiments_per_month = Experiment.objects.annotate(
...    month=TruncMonth('start_datetime', tzinfo=melb)).values('month').annotate(
...    experiments=Count('id'))
>>> for exp in experiments_per_month:
...     print(exp['month'], exp['experiments'])
...
2015-06-01 00:00:00+10:00 1
2016-01-01 00:00:00+11:00 1
2014-06-01 00:00:00+10:00 1

TimeField truncation

Django中的新功能1.11。
class TruncHour(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='hour'
class TruncMinute(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='分钟'
class TruncSecond(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='second'

这些在逻辑上等同于Trunc('time_field', 种类) 它们将时间的所有部分截断到kind,这样可以使精度更低的分组或过滤时间。 expression可以具有TimeFieldDateTimeFieldoutput_field

由于TimeField没有日期组件,只能处理时间部分的Trunc子类可以与TimeField一起使用:

>>> from datetime import datetime
>>> from django.db.models import Count, TimeField
>>> from django.db.models.functions import TruncHour
>>> from django.utils import timezone
>>> start1 = datetime(2014, 6, 15, 14, 30, 50, 321, tzinfo=timezone.utc)
>>> start2 = datetime(2014, 6, 15, 14, 40, 2, 123, tzinfo=timezone.utc)
>>> start3 = datetime(2015, 12, 31, 17, 5, 27, 999, tzinfo=timezone.utc)
>>> Experiment.objects.create(start_datetime=start1, start_time=start1.time())
>>> Experiment.objects.create(start_datetime=start2, start_time=start2.time())
>>> Experiment.objects.create(start_datetime=start3, start_time=start3.time())
>>> experiments_per_hour = Experiment.objects.annotate(
...    hour=TruncHour('start_datetime', output_field=TimeField()),
... ).values('hour').annotate(experiments=Count('id'))
>>> for exp in experiments_per_hour:
...     print(exp['hour'], exp['experiments'])
...
14:00:00 2
17:00:00 1

>>> import pytz
>>> melb = pytz.timezone('Australia/Melbourne')
>>> experiments_per_hour = Experiment.objects.annotate(
...    hour=TruncHour('start_datetime', tzinfo=melb),
... ).values('hour').annotate(experiments=Count('id'))
>>> for exp in experiments_per_hour:
...     print(exp['hour'], exp['experiments'])
...
2014-06-16 00:00:00+10:00 2
2016-01-01 04:00:00+11:00 1

DateTimeField truncation

class TruncDate(expression, **extra)[source]
lookup_name ='date'
output_field = DateField()

TruncDateexpression转换为日期,而不是使用内置的SQL truncate函数。 它也被注册为DateTimeField作为__date的转换。

class TruncTime(expression, **extra)[source]
Django中的新功能1.11:
lookup_name ='time'
output_field = TimeField()

TruncTimeexpression转换为一段时间,而不是使用内置的SQL truncate函数。 它也被注册为DateTimeField作为__time的转换。

class TruncDay(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='day'
class TruncHour(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='hour'
class TruncMinute(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='分钟'
class TruncSecond(expression, output_field=None, tzinfo=None, **extra)[source]
kind ='second'

这些在逻辑上等同于Trunc('datetime_field', 种类) 它们截断日期的所有部分,直到kind,并允许以更少的精度对数据集进行分组或过滤。 expression必须有DateTimeFieldoutput_field

使用范例:

>>> from datetime import date, datetime
>>> from django.db.models import Count
>>> from django.db.models.functions import (
...     TruncDate, TruncDay, TruncHour, TruncMinute, TruncSecond,
... )
>>> from django.utils import timezone
>>> import pytz
>>> start1 = datetime(2014, 6, 15, 14, 30, 50, 321, tzinfo=timezone.utc)
>>> Experiment.objects.create(start_datetime=start1, start_date=start1.date())
>>> melb = pytz.timezone('Australia/Melbourne')
>>> Experiment.objects.annotate(
...     date=TruncDate('start_datetime'),
...     day=TruncDay('start_datetime', tzinfo=melb),
...     hour=TruncHour('start_datetime', tzinfo=melb),
...     minute=TruncMinute('start_datetime'),
...     second=TruncSecond('start_datetime'),
... ).values('date', 'day', 'hour', 'minute', 'second').get()
{'date': datetime.date(2014, 6, 15),
 'day': datetime.datetime(2014, 6, 16, 0, 0, tzinfo=<DstTzInfo 'Australia/Melbourne' AEST+10:00:00 STD>),
 'hour': datetime.datetime(2014, 6, 16, 0, 0, tzinfo=<DstTzInfo 'Australia/Melbourne' AEST+10:00:00 STD>),
 'minute': 'minute': datetime.datetime(2014, 6, 15, 14, 30, tzinfo=<UTC>),
 'second': datetime.datetime(2014, 6, 15, 14, 30, 50, tzinfo=<UTC>)
}