2015-11-02 24 views
6

)DateTimeFieldから年を抽出する正しい方法は何ですか?Django 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 
+0

これは、単純なユースケースと思われるものに対する複雑な解決策のようです。 datetimefieldから年を抽出しても、開発者はカスタムSQLを記述する必要はありません。また、どのモジュールで "Func"が定義されていますか? –

+0

推奨される解決策は機能しません。次のエラーが発生しました: "FROM"の近く:構文エラー –

+0

これは生成されているクエリです。これはsqliteデータベースでローカルにテストしています:SELECT EXTRACT(YEAR FROM "articles_article"。 "publish_date")AS "Year" "ステータス" = EXTRACTによって発行されたグループ(「articles_article」から「publish_date」)、「articles_article」、「articles_article」、「articles_article」、「articles_article」、 "作成" ORDER BY "articles_article"。 "作成" DESC –

答えて

0

この質問は非常に興味深いものです。だから私はしばらくコンソールで遊んでいた。

は私が@Ivanから提供されたリンクに続き、これを得た:これは、文字列として、あなたの年を与えるべき

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が私のために、最終的な結果である:

これは私が得た結果です。

EDIT:

生成された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 
+0

@Ivanこれは私がDjangoのORM関数だけを使用した高水準のソリューションです。私はMySQLデータベースでテストしましたが、これはsqliteの問題ではないと思います。ormから作成したSQL文で私の答えを更新しました。これは特別な機能を使用していません。 – Gocht

+0

しかし、EXTRACTはsqliteでは動作しません。そのため、私はORMだけを使った単純な関数でこの解決策を開発しました。 – Gocht

+0

これはすてきな解決策のようですが、(Sjiteを使ってDjango 1.10を使ってみると) 'AttributeError: 'NoneType'オブジェクトに属性 'tzinfo'がありません。トレースバックからはクエリが正常に実行されたように見えますが、DjangoはDatetimeフィールドだと思って何らかの変換をしようとしていますが、実際は ''2015 ''のような文字列です。 –

2

これは私がやってしまったものです。 Gochtの解決策はうまくいくはずですが、結果を扱うDjangoの問題(Django 1.10、SQLite)がありました。私は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がこれを処理する最善の方法であるとは確信していませんが、これをどのように処理するのがうまくいかないのでしょうか。

関連する問題