Django:QuerySetを使ってリレーション先のカラムにfilterし抽出する方法
目次
Django:QuerySetを使ってリレーション先のカラムにfilterし抽出する方法
Laravel のEloquentではコレがなかなか面倒だったりするけど、DjangoのQuerySetこの処理がとても簡単です。
__でテーブル名をつなぐだけでカラムまでたどり着ける
具体的にはこんなコードで処理できます。
1 |
ベースのモデル.objects.filter(リレーション先のモデル1__さらにその先のモデル2__モデル2のカラム名__exact=抽出する値) |
リレーション先の値で抽出する具体例
具体的に今作っているスクリプトを例にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# Models.py from django.db import models from accounts.models import CustomUser class Skill(models.Model): CATEGORY_CHOICES = ( (1, 'スクリプト'), (2, 'データベース'), (3, 'フレームワーク/ライブラリ'), (4, '環境/OS'), (5, 'その他'), ) category = models.IntegerField(verbose_name='分類区分', choices=CATEGORY_CHOICES) skillname = models.CharField(verbose_name="スキル名", max_length=100, blank=False) created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True) class Meta: verbose_name_plural = 'Skill' def __str__(self): name = self.skillname #return self.category + ' ' + self.skillname return name class Operation(models.Model): EMPLOY_CHOICES = ( (1, '正社員'), (2, 'フリーランス'), (3, '派遣'), (4, 'その他'), ) WORKING_CHOICES = ( (1, '常駐'), (2, '一部リモート'), (3, 'フルリモート'), ) user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, verbose_name='ユーザーID') title = models.CharField(verbose_name='案件名', max_length=100, blank=False) employmentstatus = models.IntegerField(verbose_name='雇用形態', choices=EMPLOY_CHOICES) wayofworking = models.IntegerField(verbose_name='活動形態', choices=WORKING_CHOICES) ordering = models.CharField(verbose_name='依頼主', max_length=100, blank=True, null=True) start_date = models.DateField(verbose_name='開始日', blank=True, null=True) end_date = models.DateField(verbose_name='修了日', blank=True, null=True) casecontent = models.TextField(verbose_name='依頼内容', blank=True, null=True, default=None) handled = models.TextField(verbose_name='担当パート', blank=True, null=True) numberofteams = models.IntegerField(verbose_name='チーム人数', default=None) image = models.ImageField('画像1', upload_to='images/', blank=True, null=True, default=None) creations = models.URLField(verbose_name='参考URL', blank=True, null=True, default=None) created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True) class Meta: verbose_name_plural = 'Operation' def __str__(self): return self.title class UseSkill(models.Model): operation = models.ForeignKey(Operation, on_delete=models.CASCADE, verbose_name='案件ID') skill = models.ForeignKey(Skill, on_delete=models.CASCADE, verbose_name='スキルID') created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True) class Meta: verbose_name_plural = 'Useskill' def __str__(self): return self.Skill.skillname + ' '+ self.Operation.title |
これは今私が作成しているポートフォリオPDFの生成システムのテーブルです。
案件=operationに対して、どんなスキルを使ったかをUseSkillというテーブルに記載していきます。
スキルの活用期間を算出したい場合どうすればいいか
UseSkillをスキルごとに抽出しスキルを利用した期間をoperationから計算しようとした場合、次のようなルートが考えられます。
- Skillをall()で全件取得、これをForループしUseSkillからOperation(案件)を抽出。さらに案件を回して自分の係わった案件の期間を計算し合計
- Operationから自分の係わった案件を抽出。これをForループしUseSkillを抽出。[スキル名,期間]のリストを作成しGroup_byで結合とSum
- UseSkillからリレーション先のOperationのuserに抽出条件をかけGroup_byし使用スキル名Listを作成。これをForループしOperationを抽出(enddate-startdate)の計算結果をSUMで集計
- UseSkillからリレーション先のOperationのuserに抽出条件をかけGroup_byし使用スキル名Listを作成。これをwhere in でOperationを抽出。pandasに入れてゴニョゴニョ
- UseSkillからリレーション先のOperationのuserに抽出条件をかけ抽出。結果をpandasに入れてゴニョゴニョ
泥臭く何回も『抽出=>Forループ』をかければ欲しい情報までは持っていけます。
でも勉強用なので3~5のどれかで行きたいです。
でこの時共通の課題となるのが【 リレーション先のテーブルのカラムに抽出条件をかける 】という操作です。
リレーション先の値で抽出するDjangoのスクリプト
1 |
us = Useskill.objects.filter(operation__customer__id__exact=ユーザーID) |
1行で完了、しかもわかりやすい!Djangoスゲー。
これをLaravelのEloquentで書くとこんな感じ
1 2 3 |
us = UseSkill::whereHas('operation',function($seach)use('ユーザーID'){ $search->where('user',ユーザーID) }->get(); |
functionが出てきたりなどなど慣れれば問題ないんですけど、敷居は高く見えますよね。
まとめ
QuerySetの一発で一気に日付計算までできないかなと試行錯誤しましたが、私にはまだ難易度高いようで挫折しました。
1 2 3 4 5 |
us = Useskill.objects.filter(operation__customer__id__exact=u_id)\ .extra( select={'val':"select startdate - enddate from managedcontents_operation where id=%s"}, select_params=(['operation']) ) |
こんなで行けるかと思ったのですが、パラメーターに’operation’という文字列が入ってしまい失敗。
素直にPandasに格納しようと思いました。
-
前の記事
GoogleAdSenseの広告数制限は何日間で解消されるか 2021.03.02
-
次の記事
Django:「pandasの集計結果をTemplateでForループしたらIndexが消えた」を解消する方法 2021.03.03
コメントを残す