日記

日々のことと、Python/Django/PHP/Laravel/nodejs などソフトウェア開発のことを書き綴ります

Model その3

サボりすぎて自分もあんまり覚えてませんが(笑) Modelの三回目です。

リレーションシップ

Django の Model でも当然ながら、外部キーを使ったテーブル間の連携にも対応しています。ではありがちの会社と所属する社員で、サンプルを書いてみます。

from django.db import models

class Company(models.Model):
    name    = models.TextField(null=False)

class Employee(models.Model):
    company = models.ForeignKey(Company)
    name    = models.TextField(null=False)

こんな感じで、 models.ForeignKeyが外部キーを表すフィールド型です。では、このモデルを使ってデータ操作をしてみます。

from app1.models import Employee
from app1.models import Company
company1 = Company()
company1.name = u'テスト会社1'
company1.save()
employee1 = Employee()
employee1.name = u'会社員1'
employee1.company = company1
employee1.save()
employee2 = Employee()
employee2.name = u'会社員2'
employee2.company = company1
employee2.save()

これでデータを保存しました。では、会社員のデータを検索して、会社員名と会社名を表示してみます。

employees = Employee.objects.all()
for emp in employees:
  emp.name
  emp.company.name
会社員1
テスト会社1
会社員2
テスト会社2

と、こんな感じになります。
また会社員の情報を引っ張って来るときに、会社のIDや会社名を条件にしたい時は次のようにします。

Employee.objects.filter(company__id=1, company__name__contain=u'会社')

アンダースコアを二つ繋げると、リレーションシップが貼られている先のフィールドを指定できます。このアンダースコアを二つ繋げるのは、Django Modelの特殊ルールで、リレーション先のフィールドを指定する以外に上の例のような __contain (SQLになると LIKE)だとか、条件式を指定する場合に使用します。

ちなみに Django の model では、この例のような inner join は対応しているものの、outer joinの問い合わせには対応していないので、データ設計時に工夫が必要になったり、直接SQLを発行しなければ行けない場面が度々でてきます。意外と出来ないことが多いので、SQLにすると大したことない処理でも、Djangoでは結構面倒だったりするので、注意が要ります。

その他のデータを結果に含める

Employee.objects.filter(company__id=1, company__name__contain=u'会社')

などで取得できるデータは、何も指定しないとテーブルから取得できるデータだけです。しかし、実際のアプリケーションでは count関数の結果だったり、min/max/averageなどが必要な場合もあります。データベースの isolation levelを考えると、できれば一回のSQLで副問い合わせを使って結果を取得したいこともあります。そんなときは、QueryManagerのextraという関数を使用します。

employees = Employee.objects.extra(select={'emp_num': 'select count(*) from app1_employee'}).all()
for emp in employees
  emp.emp_num
  emp.name

これを実行すると

2L
会社員1
2L
会社員2

となります。
注意点としては、extraはQueryManagerが発行するSQLに直接バインドされるため、SQLレベルでのエラーが発生します。AliasなんかもSQLを意識して使わないといけないので、結構デバッグが大変です。(データベース側のSQLログを出力するようにしておかないとデバッグはままならないはず)extraを実際に使う時には常にSQLを意識しましょう。


そんなわけで、今回はここまで〜
ここまでの内容で、ほとんどのアプリは作れちゃうと思うので、Djangoネタはそろそろ終わりかな。
次は何にしようかな。