【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は自動コミットをしますが、いつコミットするか知るために一読したほうが良いかもしれません。
以上、お疲れさまでした。