Djangoのモデルを使いこなそう

Djangoのモデルは非常に強力な機能です。
データベースの値にアクセスするのが主な使い方ですが、それだけではありません。簡潔なコードでびっくりするようなことができます。
例として、titleとnumとdateという三つのフィールドを持つ簡単なモデルを考えていきましょう。マイグレーションは済ませておいてください。

# models.py
from django.db import models
from datetime import date

class MyModel(models.Model):
    title = CharField(max_length=50) #50文字までの文字列
    num = IntegerField() #数値
    date = DateField() #日付

    def __str__(self):
        return self.title

基本の使い方

# views.py
from django.views.generic import ListView
from .models import MyModel

class MyView(ListView):
    template_name = "template.html"

    def get_queryset(self):
        queryset = MyModel.objects.all()
        return queryset

レコードの中身をテンプレート内で利用したいだけならこれでOK。get_querysetで返したクエリセットが、{{object_list}}で参照できるようになります。

読み書き

全レコードを取得するには MyModel.objects.all() です。返値はQuerySetというオブジェクトで、要はリストのようなものなのでfor文で処理することもできるし、queryset[1]のようにインデックスを付けて指定することもできます。(インデックス範囲外エラーに注意)

一つのレコードを指定して取得するには MyModel.objects.get(pk=1) のように指定します。pkというのは主キー(Primary Key)のことで、モデルに主キーが指定されていない時に自動で追加されるフィールドです。レコードが追加された順番に1から増えていきます。
実はgetの引数はfilterと同じように好きなフィールドを指定できます。しかし複数のレコードがヒットした場合や、ひとつもヒットしなかった場合にエラーが出てしまうので、おとなしく主キーを指定しましょう。

書き込みはオブジェクトの値を代入してから save() することでデータベースに保存されます。

# 主キーが1のレコードを取得
myobj = MyModel.objects.get(pk=1)
# numの値を10に
myobj.num = 10
# データベースに保存
myobj.save()

フィルタ

レコードの中から特定の条件を満たすものを抽出します。

# dateが今日の日付に等しいレコードを抽出
myquery = MyModel.objects.filter(date=date.today())
# numが10以上のレコードを抽出
myquery = MyModel.objects.filter(num__gte=10)
# titleに「AI」を含むレコードを抽出(部分一致)
myquery = MyModel.objects.filter(title__contains="AI")

フィールド名そのままなら完全一致検索、__containsをつけると部分一致検索、数値の大小などを比較する時はgteやltなどのキーワードをつけます。

応用編

モデルオブジェクトはPythonのクラスなので、好きなメソッドを追加することが出来ます。これがとても便利。

疑似フィールドを追加しよう

例えばdateフィールドの値に対して、今日から何日後の日付なのかを表示させたいとします。

class MyModel(models.Model):
### 中略 ###
    @property
    def days_left(self) -> int:
        if self.until_date is not None:
            return (self.until_date - date.today()).days
        return 0

なんと、これだけでMyModelにdays_leftというフィールドが追加されたかのように扱うことができるんですね。{{mymodel.days_left}}のようにテンプレート内からもアクセスできます。

モデルの中でモデルを使うこともできるので、応用範囲は無限大です。

class Children(models.Model):
    parent = ForeignKey(Parent, on_delete=models.CASCADE)

class Parent(models.Model):
    @property
    def children_count(self):
        '''子供の数を返す'''
        return Children.objects.filter(parent=self).count()

この例では、ParentがChildrenのリストなどを保持することなく個数を数えられることを示しています。しかもこの数字は参照されるたびに計算されるのでいつでも最新です。

参照されるたびにモデルの数値を更新したり、ログを出力したり、好きな処理を挟むことができるので、デバッグの時にも大いに役立つでしょう。

この記事が気に入ったらサポートをしてみませんか?