【Django】モデルについていろいろ書く#4
前回:【Django】モデルについていろいろ書く#3 - hikaru’s diary
最終回です。
アグリゲーション
アグリゲーション | Django ドキュメント | Django
チートシートです。
# ドキュメントから引用 # Total number of books. >>> Book.objects.count() 2452 # Total number of books with publisher=BaloneyPress >>> Book.objects.filter(publisher__name='BaloneyPress').count() 73 # Average price across all books. >>> from django.db.models import Avg >>> Book.objects.all().aggregate(Avg('price')) {'price__avg': 34.35} # Max price across all books. >>> from django.db.models import Max >>> Book.objects.all().aggregate(Max('price')) {'price__max': Decimal('81.20')} # Difference between the highest priced book and the average price of all books. >>> from django.db.models import FloatField >>> Book.objects.aggregate( ... price_diff=Max('price', output_field=FloatField()) - Avg('price')) {'price_diff': 46.85} # All the following queries involve traversing the Book<->Publisher # foreign key relationship backwards. # Each publisher, each with a count of books as a "num_books" attribute. >>> from django.db.models import Count >>> pubs = Publisher.objects.annotate(num_books=Count('book')) >>> pubs <QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]> >>> pubs[0].num_books 73 # Each publisher, with a separate count of books with a rating above and below 5 >>> from django.db.models import Q >>> above_5 = Count('book', filter=Q(book__rating__gt=5)) >>> below_5 = Count('book', filter=Q(book__rating__lte=5)) >>> pubs = Publisher.objects.annotate(below_5=below_5).annotate(above_5=above_5) >>> pubs[0].above_5 23 >>> pubs[0].below_5 12 # The top 5 publishers, in order by number of books. >>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5] >>> pubs[0].num_books 1323
私は高度な集計だと.query()を使いSQLで書くことが多いです。
もしSQLを書きたくない場合は以下のようにmodelsに定義するといいと思います。
class Kinmu(models.Model): name = models.ForeignKey(User, to_field='username', on_delete=models.CASCADE) hiduke = models.DateField(verbose_name="日付", default=timezone.now) syukkin = models.TimeField(verbose_name="出勤時間", blank=True, null=True) taikin = models.TimeField(verbose_name="退勤時間", blank=True, null=True) kyukei = models.TimeField(verbose_name="休憩時間", blank=True, null=True) bikou = models.CharField(verbose_name="備考", max_length=100, blank=True, default="") km = models.FloatField(verbose_name="走行距離", default=0) def __str__(self): return str(self.name) + str(self.hiduke) @property def sum(self): # ここ a = datetime.timedelta(hours=self.taikin.hour, minutes=self.taikin.minute) - \ datetime.timedelta(hours=self.syukkin.hour, minutes=self.syukkin.minute) - \ datetime.timedelta(hours=self.kyukei.hour) a = round(a.total_seconds()) b = str(a % 3600 // 60) if len(str(a % 3600 // 60)) == 2 else str(a % 3600 // 60) + "0" c = str(a // 3600) + ":" + b if len(b) == 2 else b + "0" return c
>>> from diary.models import Kinmu >>> Kinmu.objects.all()[0].sum '8:00' >>>
検索
完全一致
# ドキュメントから引用 # SELECT ... WHERE headline LIKE '%Lennon%';と同じ >>> Entry.objects.get(headline__icontains='Lennon')
PostgreSQL独自の検索機能だとicontainsよりかは強力で楽に実装できるそうです。
Full text search | Django ドキュメント | Django
マネージャ
ORMを使うときはモデル名.objects....と書きますが、objectsという変数名は変更することができます。
変更するとobjectsをフィールド名として扱うことができます。下記はobjectsをManagerにする例です。
from django.db import models class Person(models.Model): people = models.Manager()
マネージャをカスタマイズするにはQuerySetを定義します。
from django.db import models class PersonQuerySet(models.QuerySet): def authors(self): return self.filter(role='A') def editors(self): return self.filter(role='E') class Person(models.Model): people = PersonQuerySet.as_manager() # objectsでも可
データベースのトランザクション
データベースのトランザクション | Django ドキュメント | Django
Djangoは自動コミットをしますが、いつコミットするか知るために一読したほうが良いかもしれません。
以上、お疲れさまでした。
【Django】モデルについていろいろ書く#3
前回:【Django】モデルについていろいろ書く#2 - hikaru’s diary
クエリ作成を見ていきます。
クエリを作成する | Django ドキュメント | Django
下記のようなモデルを作成し、migrateまで実行したとします。
# ドキュメントから引用 from datetime import date from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=200) email = models.EmailField() def __str__(self): return self.name class Entry(models.Model): blog = models.ForeignKey(Blog, on_delete=models.CASCADE) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField(default=date.today) authors = models.ManyToManyField(Author) number_of_comments = models.IntegerField(default=0) number_of_pingbacks = models.IntegerField(default=0) rating = models.IntegerField(default=5) def __str__(self): return self.headline
CRUD
shellでCRUDします。
>>> from blog.models import Blog >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') >>> b.save() # create >>> b = Blog.objects.get(id=1) # read >>> all = Blog.objects.all() # 全件取得 >>> b.tagline = "The today Beatles news" >>> b.save() # update >>> b.delete() # delete
外部キーで参照列を取り出したい時は繋げて書く(?)と良いです。
templateでも出来ます。
>>> from blog.models import Entry >>> tagline = Entry.objects.get(blog_id=1).blog.tagline
参照列がunique=Trueであれば指定することが出来ます
https://docs.djangoproject.com/ja/4.0/ref/models/fields/#django.db.models.ForeignKey.to_field:~:text=special%20syntax.-,ForeignKey.to_field,-%C2%B6
リストのように取り出せます。どのデータでもいいなら一番簡単だと思います。
>>> Entry.objects.all()[0] >>> Entry.objects.all()[:5]
その他
# ドキュメントから引用 Entry.objects.filter(pub_date__year=2006)
filterの代わりにgetを使うと例外を出してくれるのでけっこう便利です。
https://docs.djangoproject.com/ja/4.0/topics/db/queries/#:~:text=%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84%E3%80%82-,get()%20%E3%82%92%E7%94%A8%E3%81%84%E3%81%A61%E3%81%A4%E3%81%AE%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B,-%C2%B6
.updateで一括更新する。
# ドキュメントから引用 Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
sql文を発行する。
Entry.objects.all().query
素のsql文を実行する。
ORMは集計などの複雑な処理が苦手なのでsqlで書いたほうが良い時もあります。
Person.objects.raw('SELECT * FROM myapp_person')
以上、お疲れさまでした。
【Django】モデルについていろいろ書く#2
前回: 【Django】モデルについていろいろ書く#1 - hikaru’s diary
リレーション
# ドキュメントから引用 リレーショナルデータベースの強力さがテーブル同士の関係によって決まることは疑いがありません。Dango では、最も一般的な 3 つのデータベースリレーションシップを定義しています: 多対 1、多対多、1 対 1 です。
多対1にはForeignKeyを使用し、多対多にはManyToManyFieldを使用するそうです。
ForeignKeyはORMを使うと
<p>名前:{{ p.number.name }}</p>
a = Person.objects.filter(number.name='name')
って感じで楽に書けます。
Metaオプション
メタデータを設定することができます。
# ドキュメントから引用 from django.db import models class Ox(models.Model): horn_length = models.IntegerField() class Meta: ordering = ["horn_length"] verbose_name_plural = "oxen"
そのほかのオプションについてはこちらにあります。
Model Meta options | Django ドキュメント | Django
継承
modelクラスを継承します。
# ドキュメントから引用 from django.db import models class CommonInfo(models.Model): # テーブルは作成されない name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True class Student(CommonInfo): # CommonInfoから継承 home_group = models.CharField(max_length=5)
他にconstraints,verbose_name,verbose_name_plural辺りは必須。
init.pyを作ってfrom .models import * と書くと複数ファイルに分けることが出来ます。
次回はクエリの書き方を書きます。お疲れ様でした。
【Django】モデルについていろいろ書く#1
はじめに
最近書けていなかったのでたまには更新しようかなと思います。
モデルのページを順に読んで気になったところはメモしていくっていう形で書いています。
モデルとデータベース | Django ドキュメント | Django
基本
# ドキュメントから引用 from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30)
こんな感じで適当に書いたらmaemigrationsコマンドを実行
アプリ/migratinosにファイルが生成されるのでmigrateでデータベースに入力される
python manage.py sqlmigrate app 0001でSQL文の発行ができる
フィールドオプション
null,blank,choices,default,help_text,primary_key,uniqueの7つです
これらはどのフィールドでも利用できるそうです。
DateFieldのdefaultには設定方法は複数はあります。
from django.utils import timezone class DateFieldSample(models.Model): a = models.DateField(auto_now=True) # 更新日時などに使う b = models.DateField(auto_now_add=True) # 作成日時などに使う c = models.DateField(default=timezone.now) # cと同じ、datetimeはダメ
a,bでは手動で日時を変えられないことに注意。
私はadminサイトでトラブルに合いました。
uniqueは複数フィールドでの設定もできます。
class Meta: db_table = 'kinmu' constraints = [ models.UniqueConstraint( fields=['name', 'hiduke'], name="name_hiduke_unique" ) ]
今日はここまでにします。お疲れ様でした。
【PostgreSQL】ログの文字化けを治す
postgresql.confを編集する。
私の環境ではC:\Program Files\PostgreSQL\14\dataにありました。
# lc_messages = 'Japanese_Japan.932' lc_messages = 'en_US'
再起動
pg_ctl -D "C:\Program Files\PostgreSQL\14\data" restart net start postgresql-x64-14 # 上ができない場合
確認
LOG: statement: select 1; # logの中身
以上、お疲れさまでした。
md-to-pdfでマークダウンからPDFにする
公式サイトからnode.jsをインストール
https://nodejs.org/ja/download/
LTS版の8.11.0をWindowsで使うと「npm WARN config global `–global`, `–local` are deprecated. Use `–location=global` instead.」と出てきて面倒なので私は最新版にしてます。
npm -v # 確認 8.15.0 npm i -g md-to-pdf # md-to-pdfをインストール added 163 packages, changed 1 package, and audited 165 packages in 21s 14 packages are looking for funding run `npm fund` for details md-to-pdf README.md # README.mdからREDME.pdfを作成 √ generating PDF from README.md
me-to-pdfのREADME.mdをpdfにしました。
以上、お疲れ様でした。
【Django】セッションの有効期限を設定する
ソースコード
settings.py
from datetime import timedelta ***省略*** SESSIOM_COOKIE_AGE = timedelta(days=365).total_seconds()