今年に入ってDjangoをやり始めたヒヨっこですが、そのヒヨっこがDjangoでアプリケーションを開発するにあたって「これはよいかもなー」などと思った、Model定義周辺の知見を書いていきます。
すでに長年使われている方々にとっては「何を今更」という話題かもしれませんが。
あと、ほとんどTwo Scoops of Django 1.11の受け売りです。
TimeStampedModelを定義する
DjangoでModleを定義するにあたって、大抵の場合レコードが作成された時の日時を格納するフィールドと、レコードが更新された時に日時を格納するフィールドを持っていると思います。
んで、Model定義ごとにそれらを定義してもいいんですが、テンプレートを作って、それを継承させたほうが楽だよね、という話です。
from django.db import models
class TimeStampedModel(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
abstract = True
にしておくと、マイグレーションしてもテーブルが作られることはない。
んで、これを以下のように継承するという感じです。take it easy.
from django.db import models
from core.models import TimeStampedModel
class Sample(TimeStampedModel):
title = models.CharField(max_length=200)
ENUMを使って定数/選択肢を定義する
通常、Modelに定数/選択肢を設定する場合は、以下のように書くと思います。
from django import models
class Idol(models.Model):
ATTRIBUTE_CUTE = 'cu'
ATTRIBUTE_COOL = 'co'
ATTRIBUTE_PASSION = 'pa'
ATTRIBUTE_CHOICES = (
(ATTRIBUTE_CUTE, 'Cute'),
(ATTRIBUTE_COOL, 'Cool'),
(ATTRIBUTE_PASSION, 'Passion')
)
name = models.CharField(maxlength=255)
attribute = models.CharField(
max_length=2,
choices=ATTRIBUTE_CHOICES
)
この場合、attributeを条件にオブジェクトを取得する場合、以下のようになります。
pa_idols = Idol.objects.filiter(attribute=Idol.ATTRIBUTE_PASSION)
Python3.4以降には標準ライブラリとして、enum
が追加されており、これを使って、定数/選択肢を記述することができます。(3.4以前の場合はpip install enum34
をする必要があります)
from django import models
from enum import Enum
class Idol(models.Model):
class ATTRIBUTE(Enum):
cute = ('cu', 'Cute')
cool = ('co', 'Cool')
passion = ('pa', 'Passion')
@classmethod
def get_value(cls, member):
return cls[member].value[0]
name = models.CharField(maxlength=255)
attribute = models.CharField(
max_length=2,
choices=[x.value for x in ATTRIBUTE]
)
オブジェクトの取得の仕方については、以下のようになります。
passion = Idol.ATTRIBUTE.get_value('passion')
pa_idols = Idol.objects.filter(passion)
Model Managerをカスタマイズする
DjangoのORMはModel ManagerというI/Fを使用していて、このI/Fを通してDBにアクセスしている、ということを最近知りました。
んで、このModel Manager自体をカスタマイズすることもできるとのこと。
例えば、models.pyに以下のように書く。
from django.db import models
from django.utils import timezone
class PublishedManager(models.Manager):
use_for_related_fields = True
def published(self, **kwargs):
return self.filter(pub_date__lte=timezone.now(), **kwargs)
class Sample(models.Model):
review = models.CharField(max_length=255)
pub_date = models.DateTimeField()
objects = PublishedManager()
このようにしておくと、以下のようにして、pub_data
が現在時刻以前のオブジェクトのみを取得することができる。
Sample.objects.published()
いちいち、filter()やexclude()を使わなくてもよくなる。