個人でWebサービスを開発していてDjango REST frameworkを初めて使っていますが、日本語情報少ないですよね...。
後世のDjango REST frameworkユーザーのために、はまったとこと実装するために調べてわかったことをメモしていくことにします。
今回は、Django REST frameworkのFilterで検索する時に使えるTips。
事前の準備
すでにDjango REST frameworkが動くようになっている状態で、本・ユーザー・コメント(本とユーザーを外部キー荷持つ)テーブルを持つモデルを想定してAPIで検索していきます。
公式ドキュメント読むなり、ググるなりしてDjango REST frameworkが動くようにしておく。
https://www.django-rest-framework.org/
以下のバージョンで動作させました。
- Python 3.7.3
- django-filter 2.1.0
- djangorestframework 3.9.2
検索対象のデータ
今回は、本のタイトルで検索していく。検索対象のデータはこちら。
(DjangoのAdmin画面ほぼコード書かずに生成されて超便利。)
元々の状態のコード(完全一致検索)
最低限の記述をしたurls.py、models.py、views.py、serializers.pyを書いておきます。
from django.contrib import admin
from django.urls import include, path
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('app.urls')),
]
from django.urls import path, include
from .views import *
urlpatterns = [
path('bookcomments/', BookCommentListView.as_view()),
]
# coding=utf-8
from django.db import models
class User(models.Model):
auth_user = models.ForeignKey(AuthUser, on_delete=models.CASCADE, null=True)
user_name = models.CharField(max_length=128)
account_name = models.CharField(max_length=128, unique=True, db_index=True)
description = models.TextField(max_length=128, null=True)
profile_image_link = models.CharField(max_length=128, unique=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'user'
class Book(models.Model):
title = models.CharField(max_length=255, db_index=True)
book_status_choices = [
['PRE', 'Pre-Sales'],
['PUB', 'Published'],
['OOP', 'Out Of Print'],
]
book_status = models.CharField(choices=book_status_choices,
default='PUB',
max_length=10)
sale_date = models.DateField(null=True)
pages_count = models.IntegerField()
offer_price = models.IntegerField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'book'
class BookComment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
comment_text = models.TextField(max_length=10000)
comment_date = models.DateField(auto_now=True)
tweet_flag = models.BooleanField(default=False)
delete_flag = models.BooleanField(null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'book_comment'
from rest_framework import generics
from .serializers import *
class BookCommentListView(generics.ListCreateAPIView):
queryset = BookComment.objects.all()
serializer_class = BookCommentWithForeignSerializer
def get_queryset(self):
queryset = BookComment.objects.all()
title = self.request.query_params.get('title', None)
if title is not None:
queryset = queryset.filter(book__title=title)
return queryset
検索条件を変えたい場合はview.pyをいじる。
title = self.request.query_params.get('title', None)
で、クエリパラメータでtitleが指定されていれば、それをもとに検索するようになる。
BOOK_COMMENTテーブルに紐づいているBOOKテーブルのTITLEカラムを検索する。
from rest_framework import serializers
from .models import *
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'user_name', 'account_name', 'description', 'profile_image_link')
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('id', 'title', 'book_status', 'sale_date', 'pages_count', 'offer_price',)
class BookCommentWithForeignSerializer(serializers.ModelSerializer):
book = BookSerializer()
user = UserSerializer()
class Meta:
model = BookComment
fields = ('id', 'user', 'book', 'comment_text', 'comment_date', 'tweet_flag', 'delete_flag',)
この状態でAPIを叩くと、完全一致する結果が表示される。
部分一致検索
部分一致させたければ、「icontains」を使う。
from rest_framework import generics
from .serializers import *
class BookCommentListView(generics.ListCreateAPIView):
queryset = BookComment.objects.all()
serializer_class = BookCommentWithForeignSerializer
def get_queryset(self):
queryset = BookComment.objects.all()
title = self.request.query_params.get('title', None)
if title is not None:
queryset = queryset.filter(book__title__icontains=title) # 「__icontains」を追加する
return queryset
「?title=React」で検索する。
「React」がタイトルに含まれるコメントが一覧で取得できていますね。
前方一致検索
...
queryset = queryset.filter(book__title__istartswith=title) # 「__istartswith」に変更する
...
「?title=作りながら学ぶ」で検索する。
タイトルが「作りながら学ぶ」で始まるコメントが一覧で取得できていますね。
実装方法わかれば簡単ですね。
以上。
参考にした
- Django REST frameworkのGithub
https://github.com/encode/django-rest-framework - Django REST frameworkのGithubのfilter
https://github.com/encode/django-rest-framework/blob/master/rest_framework/filters.py