Djangoでクエリのfilterを動的に連結する方法
Djangoでクエリのfilterを動的に連結する方法をご紹介します。
目次
条件
- Django 2.1.7
- Python 3.7.0
前提
以下のようなModelが存在するとします。
models.py
from django.db import models from django.urls import reverse from django.contrib.auth.models import AbstractUser class CustomUser(AbstractUser): def __str__(self): return self.username + ":" + self.email class Post(models.Model): """投稿モデル""" class Meta: db_table = 'post' title = models.CharField(verbose_name='タイトル', max_length=255) text = models.CharField(verbose_name='内容', max_length=255, default='', blank=True) author = models.ForeignKey( 'sample.CustomUser', on_delete=models.CASCADE, ) created_at = models.DateTimeField(verbose_name='登録日時', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True) def __str__(self): return self.title + ',' + self.text @staticmethod def get_absolute_url(self): return reverse('sample:index')
filterの連結方法
以下のようにします。
from django.db.models import Q condition_1 = Q() condition_2 = Q() Post.objects.select_related().filter(condition_1 & condition_2)
実装例
DBレコード
以下のような3つのレコードが存在すると仮定します。
views.py 例1
import logging from django.contrib.auth.mixins import LoginRequiredMixin from django.views import generic from .models import Post from django.db.models import Q logger = logging.getLogger('development') class IndexView(LoginRequiredMixin, generic.ListView): template_name = 'sample/index.html' model = Post def get_queryset(self): # 検索条件 condition_user = Q() condition_title = Q() title = "" current_user = self.request.user # 現在のユーザを取得 if current_user.is_superuser: # スーパーユーザの場合 condition_user = Q(author=current_user.id) if title: # タイトルが指定されている場合 condition_title = Q(title__icontains=title) return Post.objects.select_related().filter(condition_user & condition_title)
以下のようなSQLが発行されます。
SELECT “post”.”id”, “post”.”title”, “post”.”text”, “post”.”author_id”, “post”.”created_at”, “post”.”updated_at”, “sample_customuser”.”id”, “sample_customuser”.”password”, “sample_customuser”.”last_login”, “sample_customuser”.”is_superuser”, “sample_customuser”.”username”, “sample_customuser”.”first_name”, “sample_customuser”.”last_name”, “sample_customuser”.”email”, “sample_customuser”.”is_staff”, “sample_customuser”.”is_active”, “sample_customuser”.”date_joined” FROM “post” INNER JOIN “sample_customuser” ON (“post”.”author_id” = “sample_customuser”.”id”) WHERE “post”.”author_id” = 1; args=(1,)
リスト画面を表示させると以下のようになります。
views.py 例2
例1との違いは、title = “test2″で絞り込んでいることです。
import logging from django.contrib.auth.mixins import LoginRequiredMixin from django.views import generic from .models import Post from django.db.models import Q logger = logging.getLogger('development') class IndexView(LoginRequiredMixin, generic.ListView): template_name = 'sample/index.html' model = Post def get_queryset(self): # 検索条件 condition_user = Q() condition_title = Q() title = "test2" current_user = self.request.user # 現在のユーザを取得 if current_user.is_superuser: # スーパーユーザの場合 condition_user = Q(author=current_user.id) if title: # タイトルが指定されている場合 condition_title = Q(title__icontains=title) return Post.objects.select_related().filter(condition_user & condition_title)
以下のようなSQLが発行されます。
SELECT “post”.”id”, “post”.”title”, “post”.”text”, “post”.”author_id”, “post”.”created_at”, “post”.”updated_at”, “sample_customuser”.”id”, “sample_customuser”.”password”, “sample_customuser”.”last_login”, “sample_customuser”.”is_superuser”, “sample_customuser”.”username”, “sample_customuser”.”first_name”, “sample_customuser”.”last_name”, “sample_customuser”.”email”, “sample_customuser”.”is_staff”, “sample_customuser”.”is_active”, “sample_customuser”.”date_joined” FROM “post” INNER JOIN “sample_customuser” ON (“post”.”author_id” = “sample_customuser”.”id”) WHERE (“post”.”author_id” = 1 AND “post”.”title” LIKE ‘%test2%’ ESCAPE ‘\’); args=(1, ‘%test2%’)
WHERE句に「AND “post”.”title” LIKE ‘%test2%’ ESCAPE ‘\’」が追加されていることがわかります。
リスト画面を表示させると以下のようになります。
参考
stackoverflow
https://stackoverflow.com/questions/769843/how-do-i-use-and-in-a-django-filter