LoginSignup
1
5

Django Model操作(自分用メモ)

Last updated at Posted at 2023-07-26

Django自分用メモになります

今回はModelの操作についてまとめます

開発環境

OS:mac
エディタ:vscode
python:3.10.9
django:4.1.0

モデルを作成する基本的な構文

models.py
from django.db import models

class Person(models.Model):
    first_name=CharField(max_length=30)
    last_name=CharField(max_length=30)

マイグレーションファイルの作成

マイグレートをする前にマイグレーションファイルを作る作業が必要
マイグレーションファイルとはmodel.pyに書かれた内容をDBに保存できる形に処理をされたファイルのこと

ターミナル
$ python manage.py makemigrations <アプリ名> --name <つけたい名前>

python manage.py makemigrationsだけでも可
マイグレーションに自動生成された名前ではなく、
migrationsフォルダに意味のある名前のフォルダを作りたければ
makemigrationsのあとに--nameとするといい

マイグレートする

ターミナル
$ python manage.py migrate <アプリ名>

VScodeでテーブルの中身を確認する方法

command+shift+p
sqlite open Datebaseと入力
みたいsqliteを選択するとvscode左下にSQLITE EXPLORERというタブがでるので
その上で右クリック後Show Table 'sqlite_master'を選択すると中身が見れる

その他操作

ターミナル
$ python manage.py showmigrations <アプリ名>
    #適応されているマイグレーションファイルを見る
$ python manage.py migrate <アプリ名> <戻りたいマイグレーションファイル名>
    #マイグレーションファイルを指定したファイルまで適応されていない状態にする
$ python manage.py migrate <アプリ名> zero
    #全てのマイグレーションファイルの適応を外す

ファイルを適応されていない状態に戻したらmigrationsフォルダにある
適応しなかったファイルも消しておくこと

主なField一覧

field名 概要
BooleanField 論理型、TrueとFalseを入れられる
CharField 文字列型。VARCHARとしてカラムが作成される。max_lengthで長さを指定する
DateField,DatetimeField 日付型、タイムスタンプ型
EmailField メールアドレスを格納する。バリデーションあり。デフォルトでVARCHAR(254)
FileField ファイルのアップロードの際のファイルパスを格納。デフォルトでVARCHAR(100)
DecimalField 正確な数値を格納される
IntegerField,FloatField 整数型(INTEGER)、浮動小数点(FLOAT)を格納される
TextField 長い文字列を入れる。TEXT型としてカラムが作成される
URLField URLを入れる。VARCHAR(200)としてカラムが作成される

model作成時に新しく学んだこと

models.py
from django.db import models
from django.utils import timezone

class Person(models.Model):
    email = models.EmailField(db_index=True)#indexをはる という高速化の処理
#全てにdb_index=Trueとするとメモリを喰うので検索したい項目にだけ設定すると⚪︎
    create_at =models.DateTimeField(default =timezone.datetime.now)
#デフォルト設定をすると登録時の時間を入れられる

作ったモデルを管理画面でみれるようにする

admin.py
from django.contrib import admin
from .models import Person

admin.site.register(Person)

Metaオプション

Metaはメタクラスのことでクラスの振る舞いを定義する

models.py
from django.db import models
from django.utils import timezone
import pytz #timezoneを設定するモジュール

class BaseMeta(models.Model): #抽象クラス
    create_at = models.DateTimeField(default=timezone.datetime.now(pytz.timezone('Asia/Tokyo')))
    update_at = models.DateTimeField(default=timezone.datetime.now(pytz.timezone('Asia/Tokyo')))
    
    class Meta:
        abstract = True #! 1.抽象クラス(abstract)として定義するためメタクラスを設定する


class Person(BaseMeta):#! 2.共通のカラムを持ちたいとき抽象クラスを継承する
    first_name=models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    birthday = models.DateField(default='1900-01-01')
    email = models.EmailField(db_index=True)#!indexをはる という高速化の処理
    salary = models.FloatField(null=True)
    memo = models.TextField()
    web_site = models.URLField(null=True,blank = True)
    # create_at =models.DateTimeField(default =timezone.now) #! 継承したカラムを使うためこちらは削除
    
    class Meta:
        db_table = 'person' #!テーブル名をpersonに設定
        index_together = [['first_name','last_name']] #!二つセットで検索する際に高速化できて便利
        ordering = ['salary'] #DBから取り出す際のデフォルトのorder「順番」を設定
        #降順にするときは['-salary']とする

    def __str__(self):
        return f'{self.first_name} {self.last_name}' #!データを取得した際にわかりやすくするために名前を設定

Metaオプションとしては他に
unique_together=[['first_name','last_name']]とすると
セットでUNIQUEとするフィールドの設定などもできる。

詳しくはDjango公式ページ

DBにINSERTする方法3つ

#1. インスタンスのsaveメゾット(DBに同じものが存在しないときDBに挿入、存在する場合は更新
web_site=Website(url='www.sample.com',name='sample')
web_site.save() #Websiteというクラスで作られたweb_siteインスタンスをsaveする

#2. クラスメゾットのcreateメゾット(DBに同じものが存在しないときDBに挿入、存在する場合はエラー
Website.objects.create(
    url = 'www.sample.com', name = 'sample'
) #存在した場合IntegrityError発生

#3. get_or_createメゾット(DBに同じものが存在しない場合作成して返す。存在する場合はそのものを返す
obj,created = Website.objects.get_or_create(url='www.sample.com',name='sample')
#2つ変数が返ってくる。objには作成したデータを。createdには存在しなくて作られた場合はTrueがはいり、
#作成しなかったときはFalseがはいる

実際にDBにINSERTしてみる

manage.pyと同じ階層にmain.pyファイルを作成

main.py
#! djangoの設定を読み込んでDBに保存する
import os

os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()


from ModelApp.models import Person

#インスタンスを作成
p = Person(
    first_name = 'Taro',
    last_name = 'Sato',
    birthday = '2000-01-01',
    email = 'aa@example.com',
    salary=None,
    memo='memo taro',
    web_site = 'https://www.com',
)
#インスタンスをsave
p.save()

#classmethod create
Person.objects.create(
    first_name = 'Jiro',
    last_name = 'Ito',
    email = 'bb@example.com',
    salary = 2000,
    memo='class method実行',
    web_site=None,
)

#get_or_create(取得or作成)
obj,created = Person.objects.get_or_create(
    first_name = 'Saburo',
    last_name = 'Ito',
    #birthdayはdefaultを設定しているため記述しない場合デフォルト値がはいる
    email = 'bb@example.com',
    salary = 2000,
    memo='class method実行',
    web_site=None,
)

print(obj) #models.pyで設定したdef __str__(self)の名前で返る
print(created) #作られた場合はTrue、すでに存在している場合はFalse

ファイル作成後、main.pyを右クリック→ターミナルで実行をするとDBに保存されている

※DBにおけるNullと空白は別物ということを覚えておく

DBからSELECTする方法3つ(取得)

manage.pyと同じ階層にデータを取得するためのファイルselect_sample.pyファイルを作成

select_sample.py
#データの取得
#Djangoの設定を読み込む
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()


from ModelApp.models import Person

#全て取得
persons = Person.objects.all()
for person in persons:
    print(person.id,person,person.salary)
    
#1件だけ取得
# person = Person.objects.get(first_name='Taro') #複数取得できるからエラーに
# person = Person.objects.get(first_name='taro') #1件も獲得できない時もエラーに
person = Person.objects.get(pk=1) #主キーは必ず1件なので取得できる
print(person.id,person)

#filter(絞り込み、エラーにならない、複数取得可)
persons = Person.objects.filter(first_name='Taro').all()

print(persons[0].email) #配列なのでこの形で取り出せる
for person in persons:
    print(person.id , person) #for文でも取り出せる

参考となるQiita(Queryset集)

Django逆引きチートシート(QuerySet編)

そもそもobjectsはモデルマネージャ。.all()や.first()はメゾットであることを知ってた?
下記が参考になるDjango objectsの使い方を簡単解説

DBからの取り出し(get_object_or_404 get_list_or_404)

from django.shortcuts import get_object_or_404
get_object_or_404
#指定したモデルを呼び出し、getを行う。値を取得できなかった場合raise Http404を送出する
#例)item = get_object_or_404(Items,pk=id)

get_list_or_404
#指定したモデルを呼び出し、filterを行う。同上
#リストで取り出すためリストの中身がない状態で取り出すと404を返す
#例)items = get_list_or_404(Items,id__gt=2) idが2より大きいものを取り出す

Django Viewの応用
こちらでエラーハンドリングについてもまとめた

データの更新(update)

manage.pyと同じ階層にデータを更新するためのファイルupdate_sample.pyファイルを作成

update_sample.py
#データの更新(update)
import os

os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()

from ModelApp.models import Person
from django.utils import timezone
import pytz

#1件だけ取得してbirthdayとupdate_atを更新する
person = Person.objects.get(id=1)
person.birthday='2001-09-01'
person.update_at=timezone.datetime.now(pytz.timezone('Asia/Tokyo'))
person.save() #saveで更新完了

#first_nameが'Taro'であるデータを全て更新する
persons = Person.objects.filter(first_name='Taro').all()
for person in persons:
    person.first_name = person.first_name.lower()
    person.update_at = timezone.datetime.now(pytz.timezone('Asia/Tokyo'))
    person.save()
#上記のやり方は一つ一つクエリを作成するため、何万件ともなると処理が遅くなってしまう

#下記はupdateで一気に実行するためこちらのほうが処理が軽くなりおすすめ
Person.objects.filter(first_name='Saburo').update(
    web_site='http://sample.jp',
    update_at = timezone.datetime.now(pytz.timezone('Asia/Tokyo'))
)

データの削除(delete)

manage.pyと同じ階層にデータを削除するためのファイルdelete_sample.pyファイルを作成

delete_sample.py
#データの削除(delete)
import os

os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()

from ModelApp.models import Person

#filterを使って条件にあったデータを削除する
Person.objects.filter(first_name='Saburo').delete()
Person.objects.filter(first_name = 'taro',birthday='2001-09-01').delete()

#全件削除
Person.objects.all().delete()

modelに外部キーを持たせる

テーブルに外部キー(ForeignField)を持たせることによってテーブル間の紐付けを行う

models.py
#外部キー練習
class Students(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField()
    major = models.CharField(max_length=20)
    school = models.ForeignKey(
        'Schools', on_delete=models.CASCADE
    )
    
    class Meta:
        db_table = 'students'
        
class Schools(models.Model):
    name = models.CharField(max_length=20)
    prefecture = models.ForeignKey(
        'Prefectures',on_delete=models.CASCADE
    )
    
    class Meta:
        db_table = 'schools'
        
class Prefectures(models.Model):
    name=models.CharField(max_length=20)
    
    class Meta:
        db_table='prefectures'

StudentテーブルはShcoolsテーブルと1対多の関係になり
SchooolsテーブルとPrefectureテーブルも1対多の関係になっている

on_delete=models.CASCADEを体感する

manage.pyと同じ階層にforeignkey_sample.pyファイルを作成

foreignkey_sample.py
#外部キーの練習
import os

os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()

from ModelApp.models import Students,Schools,Prefectures

prefectures = ['東京','大阪']
schools = ['東高校','西高校','北高校','南高校']
students = ['太郎','二郎','三郎']

#データの用意
def insert_records():
    for prefecture_name in prefectures:
        prefecture = Prefectures(
            name = prefecture_name
        )
        prefecture.save()
        for school_name in schools:
            school = Schools(
                name = school_name,
                prefecture = prefecture
                #左のprefectureはmodels.pyで定義した外部キーのprefecture
                #右のprefectureはfor内で作成したインスタンスのprefecture
                #こうすることで紐付けされる
            )
            school.save()
            for student_name in students:
                student = Students(
                    name = student_name,age=17,
                    major = '物理',school = school
                )
                student.save()
#データを一旦保存させてコメントアウト              
# insert_records()

#データを表示するための関数
def select_students():
    students = Students.objects.all()
    for student in students:
        print(student.id,student.name,student.age,student.major,student.school.id,student.school.name,student.school.prefecture.id,student.school.prefecture.name)
#student.school.nameやstudent.school.prefeture.nameのように外部キーを伝って紐付けされたテーブルのデータににアクセスすることが可能
        
# select_students()

#東高校を削除すると同時にstudentが3人削除される
Schools.objects.filter(id=1).delete()
# select_students()
#prefectureの'東京'を削除すると東京を持つデータがすべて削除される
Prefectures.objects.filter(name='東京').delete()
select_students()

on_delete=models.CASCADE以外のオプション

models.py
school = models.ForeignKey(
        #親を削除しようとするとエラーがでる
        'Schools', on_delete=models.PROTECT
        #親を参照している項目はすべてNullをいれる
        'Schools', on_delete=models.SET_NULL,null=True
)

on_delete=models.CASCADE以外のオプション2 RESTRICT

Schoolsを削除したときはStudentsのRESTRICTによって保護され、
Prefectureを削除したときはCASCADEで紐付けしたために下位が全て削除される。

参照先(親)が参照先(祖父母)にCASCADEで紐付けされていれば、
祖父母を削除した時に親も子も一緒に削除というイメージ

models.py
#外部キー練習
class Students(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField()
    major = models.CharField(max_length=20)
    #ここからポイント
    school = models.ForeignKey(
        'Schools', on_delete=models.RESTRICT #
    )
    prefecture = models.ForeignKey(
        'Prefectures',on_delete=models.CASCADE #
    )
    class Meta:
        db_table = 'students'
        
class Schools(models.Model):
    name = models.CharField(max_length=20)
    prefecture = models.ForeignKey(
        'Prefectures',on_delete=models.CASCADE #
    )
    class Meta:
        db_table = 'schools'
        
class Prefectures(models.Model):
    name=models.CharField(max_length=20)
    class Meta:
        db_table='prefectures'
foreignkey_sample.py
def insert_records():
    for prefecture_name in prefectures:
        prefecture = Prefectures(
            name = prefecture_name
        )
        prefecture.save()
        for school_name in schools:
            school = Schools(
                name = school_name,
                prefecture = prefecture
                #左のprefectureはmodels.pyで定義した外部キーのprefecture
                #右のprefectureはfor内で作成したインスタンスのprefecture
                #こうすることで紐付けされる
            )
            school.save()
            for student_name in students:
                student = Students(
                    name = student_name,age=17,
                    major = '物理',school = school,
                    #RESTRICTの場合のみ追加 
                    prefecture = prefecture, #idを持たせるためこちらを追加
                )
                student.save()



OneToOneFieldは1対1でテーブルを紐づける

models.py
#OneToOne
class Places(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)
    
    class Meta:
        db_table = 'places'
        
class Restaurant(models.Model):
    place = models.OneToOneField( #
        Places,
        on_delete=models.CASCADE,
        primary_key=True #
    )
    name = models.CharField(max_length=50)
    
    class Meta:
        db_table = 'restaurants'

manage.pyと同じ階層にonetoone_sample.pyファイルを作成

onetoone_sample.py
#OneToOne
#import等省略。上記のファイルと同じ

places = [
    ('Motomachi','Yokohama'),('Tsukiji','Tokyo')
]
restaurants=['restaurantA','restaurantB']

for place_name,place_address in places:
    p = Places(name=place_name, address=place_address)
    p.save()
    for restaurant_name in restaurants:
        r = Restaurant(place=p, name=restaurant_name)
        r.save()
        #>>> 結果はrestaurantsテーブルにはplace_id1,2とrestaurantBの2つのデータしか入らない
        #インスタンスのsaveメゾットは
        #DBに同じものが存在しないときはDBに保存、存在する場合は更新なので今回はplaceが
        #primarykeyで同じものなのでnameがrestaurantBに更新される
    
        Restaurant.objects.create(
            place =p,name=restaurant_name
        )
        #クラスメゾットのcreateメゾットを使う場合
        #こちらもDBにすでに存在する場合はエラーを返すので保存はできない

OneToOneは1対1でテーブル間を紐づけるフィールドなので一つの他テーブルidに対して
一つのデータを持つ。

ManyToMany

多対多の関係のテーブルを紐づけるフィールド
内部的に2つのテーブル間にもう1つ新たなテーブルが作成され、そのテーブルを使って紐づける

models.py
#ManyToMany
class Authors(models.Model):
    name = models.CharField(max_length=50)
    
    def __str__(self):
        return self.name
    
    class Meta:
        db_table = 'authors'
        
class Books(models.Model):
    name = models.CharField(max_length=50)
    authors = models.ManyToManyField(Authors) #
    
    def __str__(self):
        return self.name
    
    class Meta:
        db_table = 'books'

manage.pyと同じ階層にmanytomany_sample.pyファイルを作成

manytomany.py
#manytomany
#import等省略

#データの挿入
def insert_books():
    book1 = Books(name='Book1')
    book2 = Books(name='Book2')
    book3 = Books(name='Book3')
    book1.save()
    book2.save()
    book3.save()
    
def insert_authors():
    author1 = Authors(name='Author1')
    author2 = Authors(name='Author2')
    author3 = Authors(name='Author3')
    author1.save()
    author2.save()
    author3.save()

#関数の実行
insert_books()
insert_authors()

# データを取り出して
book1 = Books.objects.get(pk=1)
book3 = Books.objects.get(pk=3)
author1 = Authors.objects.get(pk=1)
author2 = Authors.objects.get(pk=2)
author3 = Authors.objects.get(pk=3)

#book1にaddで追加する
book1.authors.add(author1,author2)
print(book1.authors.all())

book3.authors.add(author1,author2,author3)
print(book3.authors.all())

print(book1.authors)

addメゾットで追加でき、saveはいらない
authorsは複数のbooksをもち
booksも複数のauthorsをもつという関係になる

結合したレコードからデータを取り出す

1を相手にするのか多を相手にするのかで取り出し方が異なる
manage.pyと同じ階層にselect_foreign_table.pyファイルを作成

select_foreign_table.py
#! 結合したデータの取り出し
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()

from ModelApp.models import Students,Schools,Prefectures
from ModelApp.models import Places,Restaurant
from ModelApp.models import Authors,Books


s=Schools.objects.first()
print(type(s))
print(dir(s)) #このオブジェクトが持つ使える属性を返す中にprefectureとstudents_setが存在することがわかる
# SchoolsのオブジェクトはStudentsに対しては1対多、Prefectureに対しては多対1であるため
print(s.prefecture.name)

print('*'*100)
st = s.students_set #多を取り出すには_set
print(type(st))
print(dir(st))
print(st.all())
st = st.all() #allで取得する
for s in st:
    print(s.school.prefecture.name) #大阪 を取り出せる
    
#OnetoOneからデータを取り出す
print('********onetoone*********')
p = Places.objects.first()
print(f'レストランの名前は:{p.restaurant.name}')
#逆も同じように取り出せる
r = Restaurant.objects.first()
print(f'場所は{r.place.address}{r.place.name}')


#ManyToManyからデータを取り出す
print('***********manytomany****************')
b = Books.objects.first()
print(type(b))
print(dir(b))
print(b.authors.all())
bs = b.authors.all()
for b in bs:
    print(b.name) #Author1,Author2が表示される

a= Authors.objects.first()
print(a.books_set.all())
#models.pyのAuthorsクラスはBooksと紐づけるプロパティを持っていないのでAuthorsとBooksが1対多のような
# 関係になるからこちらは_setを使う?

a_s = a.books_set.all()
for a in a_s:
    print(a.name) # Book1,Book3が表示される

条件を指定してデータを取得

manage.pyと同じ階層にquery_01.py~query_04.pyファイルを作成
以下はすべてデータを取り出す練習

query_01.py
#! データを絞り込んで取得
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()

from ModelApp.models import Students

#全件取得
print(Students.objects.all())

#頭五件取得
print(Students.objects.all()[:5])

#5件目よりあと
print(Students.objects.all()[5:])

#5~7件目
print(Students.objects.all()[:8])
print(Students.objects.all()[5:8].query) #実際に発行しているqueryを見ることができる

#一番最初の一件
print(Students.objects.first())

#等価のものだけに絞り込む
print(Students.objects.filter(name='太郎').all())
print(Students.objects.filter(age=17).all())

#AND条件
print(Students.objects.filter(name='太郎',pk=13).all().query)
print(Students.objects.filter(name='太郎',pk__gte=13).all().query)
print(Students.objects.filter(name='太郎',pk__lte=17).all())
print(Students.objects.filter(name='太郎',pk__gte=13,pk__lte=20).all().query)

#前方一致、後方一致
print(Students.objects.all())
print(Students.objects.filter(name__startswith='').all())
print(Students.objects.filter(name__endswith='').all())

#or
from django.db.models import Q
print(Students.objects.filter(Q(name='太郎')|Q(pk__gt=19)).all())

__gteとはgreater than equalの略
__lteとはless than equealの略

query_02.py
##! データを絞り込んで取得 その2
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()

from ModelApp.models import Students,Person

#IN
ids = [13,14,15]
print(Students.objects.filter(pk__in=ids).all())
print(Students.objects.filter(pk__in=[15,16,17]).all())
print(Students.objects.filter(name__in=['二郎','']).all()) #二郎は取り出せるが三では三郎は取り出せない

# contain 部分一致
print(Students.objects.filter(name__contains='').all())

#is null
print(Person.objects.filter(salary__isnull=True).all())

#レコードを取り除くexclude(filterの逆を取り出す)
print(Person.objects.exclude(salary__isnull=True).all())
print(Students.objects.exclude(name='太郎').all())

#カラムを取り出す
print(Students.objects.values('id','name','age').all().query)
students = Students.objects.values('id','name','age').all()
for student in students:
    print('id:{},名前:{}'.format(student['id'],student['name']))
    
#並び替え
print(Students.objects.order_by('name','-id').all()) #第一条件nameを昇順、第二条件idを降順に並び替える
#filterとorder_byを組み合わせることも可
print(Students.objects.filter(name='太郎').order_by('-id').all())
query_03.py
##! データを絞り込んで取得 その3
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()

from ModelApp.models import Students

# #件数
print(Students.objects.count()) #12
print(Students.objects.filter(name='太郎').count()) #4
print(Students.objects.filter(name='Taro').count()) #0

# #件数、最大値、最小値、平均値、合計
from django.db.models import Count,Max,Min,Avg,Sum
print(Students.objects.aggregate(Count('pk'),Max('pk'),Min('pk'),Avg('pk'),Sum('age')))
aggregate_student = Students.objects.aggregate(Count('pk'),Max('pk'),Min('pk'),Avg('pk'),Sum('age'))
#{'pk__count':12,'pk__max':24}といったように辞書型で返されるため下記のような記述で取り出せる
print(aggregate_student['pk__max'])
#別名をつけることもできる
print(Students.objects.aggregate(counted_pk= Count('pk'),max_pk=Max('pk'),min_pk=Min('pk'),avg_pk=Avg('pk'),sum_pk=Sum('age')))

#GROUP BY:ある特定のカラムで集計して、合計、最大などを求める
print(Students.objects.values('name').annotate(
    Max('pk'),Min('pk')
).query) #queryを見る
print(Students.objects.values('name','age').annotate(
    max_id = Max('pk'),min_id = Min('pk')#キーをmax_idと min_idに変更している
)) 
#辞書型が配列の中にはいって渡されるのでfor文で一つ一つ取り出してキーを指定して取り出すことができる
for student in Students.objects.values('name','age').annotate(
    max_id = Max('pk'),min_id = Min('pk')
):
    print(student['name'],student['max_id'])
query_04.py
#! データを絞り込んで取得 その4 外部テーブルをつかった絞り込み
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelProject.settings')
from django import setup
setup()

from ModelApp.models import Students,Schools

# for student in Students.objects.all():
#     print(student.name,student.school.name,student.school.prefecture.name)

#外部テーブルを使ったfilterができる(外部テーブル名__カラムとする)
for student in Students.objects.filter(school__name='南高校').all():
    print(student.name,student.school.name,student.school.prefecture.name)

#excludeもできる
for student in Students.objects.exclude(school__name='南高校').all():
    print(student.name,student.school.name,student.school.prefecture.name)
    
#schoolsテーブルからstudentsを取り出す
print(Schools.objects.filter(students__name='太郎').all())
for school in Schools.objects.filter(students__name='太郎').all():
    print(student.name,school.name,school.prefecture.name)
    
print('-'*100)
#順番を並び替える(order_by)
for student in Students.objects.order_by('-school__name').all():
    print(student.name,student.school.name)
print('-'*100)
for student in Students.objects.filter(school__name='西高校').order_by('-school__name').all():
    print(student.name,student.school.name)
print('*'*100)
#リストをつくってinで条件指定すると複数のvalueから一括取得できる(自作)
school_names=['南高校','西高校']
for student in Students.objects.filter(school__name__in=school_names).order_by('school__name').all():
    print(student.name,student.school.name)
    
#外部テーブルを使ったGROUP BY
from django.db.models import Count,Max
print(Students.objects.values('school__name').annotate(Count('id'),Max('id')))
#studentからSchoolのnameごとに数をカウントして返す

演習問題

20230728-121553.png

手順
1.プロジェクトとアプリをつくる。マイグレートまで済ませる。
2.models.pyにモデルを作成する
3.makemigration、migrateをしてテーブルを作成。
4.データを格納する
5.テーブルからデータを取り出す

1は省略。

2から記述していく

models.py
from django.db import models

class Tests(models.Model):
    name = models.CharField(max_length=50)
    
    class Meta:
        db_table = 'tests'
        
class Test_Results(models.Model):
    test=models.ForeignKey(
        'Tests',on_delete=models.CASCADE
    )
    student = models.ForeignKey(
        'Students',on_delete=models.CASCADE
    )
    score = models.IntegerField()
    
    class Meta:
        db_table = 'test_results'
        

class Students(models.Model):
    name =models.CharField(max_length=50)
    grade = models.IntegerField()
    classes = models.ForeignKey(
        'Classes',on_delete=models.CASCADE
    )
    
    class Meta:
        db_table = 'students'
        
class Classes(models.Model):
    name = models.CharField(max_length=50)
    
    class Meta:
        db_table = 'classes'

3も省略
4.データを格納する

main.py
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelExam.settings')
from django import setup
setup()

from ModelApp.models import Tests,Test_Results,Students,Classes
import random

#classesにデータをいれる
# classes_list=['classA','classB','classC','classD','classE','classF','classG','classH','classI','classJ']
classes_list = ['class'+ c for c in 'ABCDEFGHIJ']
# students_list=['studentA','studentB','studentC','studentD','studentE','stundenF','studentG','studentH','studentI','studentJ']
students_list=['student' + c for c in 'ABCDEFGHIJ']
# print(classes_list)
tests_list=['数学','英語','国語']


#testのインスタンスを作ってリストに格納する
tests_instance_list = []
for test_name in tests_list:
    test_instance = Tests(
        name = test_name
    )
    test_instance.save()
    tests_instance_list.append(test_instance)

#データを格納する関数 
def insert_records():
    for classes_name in classes_list: 
        classes = Classes(
            name=classes_name
        )
        classes.save()
        
        for students in students_list:
            #studentインスタンスを作る
            name = classes_name +' '+students
            student = Students(
                name = name,
                grade = 1,
                classes = classes
            )
            student.save()
            for test_name in tests_instance_list:
                test_results = Test_Results(
                    test = test_name,
                    student = student,
                    score = random.randint(50,100)
                    )
                test_results.save()
    
insert_records()

5.テーブルからデータを取り出す

show_result.py
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','ModelExam.settings')
from django import setup
setup()

from ModelApp.models import Tests,Test_Results,Students,Classes
from django.db.models import Sum,Avg,Max,Min

print('idが1のstudentの名前とテスト科目と点数を表示する')
for test_result in Test_Results.objects.filter(student__id = 1).all():
    print(
        '生徒名:{}\
        テストの科目:{}\
        点数:{}'.format(test_result.student.name,test_result.test.name,test_result.score)
    )
    
print('クラスごとの各科目のテストの合計、平均、最大、最小を表示する')
# s=Test_Results.objects.values('student__classes__name','test__name').annotate(
#     Max('score'),Min('score'),Sum('score'),Avg('score')
# )
# print(s)
for test_result in Test_Results.objects.values('student__classes__name','test__name').annotate(
    Max('score'),Min('score'),Sum('score'),Avg('score')
):
    print(
        'クラス:{}\
        科目:{}\
        最大値:{}\
        最小値:{}\
        平均値:{}\
        合計:{}'.format(test_result['student__classes__name'],test_result['test__name'],test_result['score__max'],test_result['score__min'],test_result['score__avg'],test_result['score__sum']))
    
print('idが1のstudentの名前とテスト科目と点数を表示する2')
student = Students.objects.get(id = 1) #idも可
# student = Students.objects.get(pk = 1) #pkも可
# student = Students.objects.filter(id = 1) #? こちらではtest_result_setが使えなかったのはなぜ?
#!filterはクエリセットを返し、getはオブジェクトを返す
print(student)
for test_result in student.test_results_set.all():
    print(
        '生徒名:{}\
        テストの科目:{}\
        点数:{}'.format(test_result.student.name,test_result.test.name,test_result.score)
    )
    
print('クエリをみてみる')
print(Test_Results.objects.values('student__classes__name','test__name').annotate(
    Max('score'),Min('score'),Sum('score'),Avg('score')
).query)

filterとgetの違いは
filterはクエリセットを返し
getはオブジェクトを返す。

以上、modelに関して新しい学びがあれば追記していく

1
5
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
1
5