LoginSignup
7
3

More than 5 years have passed since last update.

日時でレコードを絞り込むビューを作る時の注意点

Posted at

毎度やらかすので自分用のメモとして。
適切なタイトルが思いつかなかったので悔しい。。

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()

まとめ

ただの うっかりミス 以外の何物でもないんですが、以外と気付かずにハマったりするので(僕だけかもですけど!!)、お気をつけ下さいませ。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3