从Django DateTimeField获取年份值()

时间:2015-11-02 15:44:22

标签: python django django-queryset

从DateTimeField中提取年份的正确方法是什么?

给出的例子:

class Article(models.Model):
    title = models.CharField(_('title'), max_length=250)
    slug = models.SlugField(_('slug'), max_length=250, unique=True, default='', blank=True)
    content = models.TextField(_('content'))
    author = models.ForeignKey(settings.AUTH_USER_MODEL)
    categories = models.ManyToManyField(Category)
    tags = models.ManyToManyField(Tag)
    created = models.DateTimeField(_('created'), default=timezone.now)
    publish_date = models.DateTimeField(_('publish date'), blank=True, null=True)

    STATUS_ARTICLE = (
        ('DRAFT', _('draft')),
        ('PUBLISHED', _('published'))
    )

    status = models.CharField(_('status'), max_length=100, choices=STATUS_ARTICLE, default='DRAFT')

class ExtractMonth(Func):
    template = "EXTRACT(MONTH FROM %(expressions)s)"

    def __init__(self, *expressions, **extra):
        extra['output_field'] = models.SmallIntegerField()
        super().__init__(*expressions, **extra)

尝试获取所有年份的列表以及每年的文章数量:

result = Article.objects.filter(status='PUBLISHED').annotate(Year=ExtractYear('publish_date')).values('Year').annotate(dcount=Count('Year'))

这会导致以下错误:

near "FROM": syntax error

生成的查询是:

SELECT EXTRACT(YEAR FROM "articles_article"."publish_date") AS "Year", COUNT(EXTRACT(YEAR FROM "articles_article"."publish_date")) AS "dcount" FROM "articles_article" WHERE "articles_article"."status" = PUBLISHED GROUP BY EXTRACT(YEAR FROM "articles_article"."publish_date"), "articles_article"."created" ORDER BY "articles_article"."created" DESC

3 个答案:

答案 0 :(得分:3)

这就是我最终做的事情。 Gocht的解决方案看起来应该可行,但我遇到了问题(Django 1.10,SQLite),Django处理结果。我不想使用RawSQL,但这是我唯一能够工作的。

try:
    from django.db.models.functions import ExtractYear
except ImportError:
    from django.db.models.expressions import RawSQL

qs = Article.objects.filter(...)

try:
    # Django >= 1.10
    qs = qs.annotate(Year=ExtractYear('publish_date'))
except NameError:
    # Django < 1.10
    qs = qs.annotate(Year=RawSQL(
            "CAST(SUBSTR('table_name'.'publish_date', 1, 4) AS integer)",
            ()
         ))

results = qs.values('Year').annotate(dcount=Count('Year'))

这使Year成为整数,而不是字符串;如果你想要一个字符串,请删除CAST(... AS integer)

您需要将table_name更改为您的模型数据库表。

我不确定except NameError是解决这个问题的最佳方法,但我无法确定如何更好地处理这个问题。

答案 1 :(得分:0)

我觉得这个问题很有意思。所以我在控制台玩了一段时间。

我按照@Ivan提供的链接获得了这个:

NSURLSessionTaskDelegate -> URLSession:task:didCompleteWithError:

这应该把你的年份作为一个字符串。

注意:如果在from django.db.models import F, Func from django.db.models.functions import Substr Article.objects.filter(...).annotate(_date=Func(F('publish_date'), function='LOWER')) .annotate(publish_year=Substr('_date', 1, 4)) .values('publish_year') 中你得到这样的内容,这将有效:_date,如果你得到一个不同的字符串,你可以更改u'2015-08-24 09:45:16'中的索引。您可以通过在Substr('_date', 1, 4)中添加_date来查看.values('_date', 'publish_year')中获得的字符串类型。

我希望这会有所帮助。

额外:

这是我得到的结果:

[{'date': datetime.datetime(2015, 8, 24, 9, 45, 16), 'date3': u'2015', 'date2': u'2015-08-24 09:45:16'}, ...]

在这种情况下,date3对我来说是最终结果。

编辑:

生成的SQL:

>>> print MyModel.objects.all().annotate(date2=Func(F('date'), function='LOWER')).annotate(date3=Substr('date2', 1, 4)).query
SELECT `app_model`.`id`, `app_model`.`date`, LOWER(`app_model`.`date`) 
AS `date2`, SUBSTRING(LOWER(`app_model`.`date`), 1, 4) 
AS `date3` FROM `app_model` ORDER BY `app_model`.`created` ASC

答案 2 :(得分:0)

Django具有内置函数TruncYear

尝试一下

result = Article.objects.filter(status='PUBLISHED').annotate(Year=TruncYear('publish_date'))