毎度やらかすので自分用のメモとして。
適切なタイトルが思いつかなかったので悔しい。。
Djangoでニュース記事とかを実装する際に、公開日時とか有効期限とかのフィールドを設けて、現在の日時と比較して公開・非公開を切り替えるってのはよくやると思いますが、以下の様に書いてはダメです。
from django.db import models
from django.utils import timezone
class ArticleManager(models.Manager):
def published(self):
qs = self.get_queryset()
return qs.filter(published_at__lte=timezone.now())
class Article(models.Model):
title = models.CharField('タイトル', max_length=100)
published_at = models.DateTimeField('公開日時', default=timezone.now)
objects = ArticleManager()
class ArticleView(ListView):
queryset = Article.objects.published() # これダメ
# queryset = Article.objects.filter(published_at__lte=timezone.now()) # 当然これもダメ
ダメな理由
ちょっと考えればわかる事なんですが、 ArticleView
で指定してる queryset
は クラス変数
なので、 サーバー起動時にクラスが読み込まれ、値が決定される
のです。
Article.objects.published()
はこの処理が実行された時点での日時で動作しますので、上記のケースでは サーバーが起動した時刻を現在時刻とする
様に動作します。
解決策
やりたい事は、 リクエストされた日時を基準として公開・非公開を判断したい
という事なので、以下の様にすれば正しく動作します。
from django.db import models
from django.utils import timezone
class ArticleManager(models.Manager):
def published(self):
qs = self.get_queryset()
return qs.filter(published_at=timezone.now())
class Article(models.Model):
title = models.CharField('タイトル', max_length=100)
published_at = models.DateTimeField('公開日時', default=timezone.now)
objects = ArticleManager()
class ArticleView(ListView):
queryset = Article.objects.all()
# リスエスト毎に動作
def get_queryset(self):
qs = super(ArticleView, self).get_queryset()
return qs.published()
まとめ
ただの うっかりミス
以外の何物でもないんですが、以外と気付かずにハマったりするので(僕だけかもですけど!!)、お気をつけ下さいませ。