DjangoでDBの管理を行っていると、DBのレコードに登録されていないキーワードで検索したいということがあると思います。例えば、ユーザの名前を別テーブルで管理している場合Django admin内の検索機能でユーザの名前で検索しても検索結果に表れてくれません。
今回は他のテーブルで管理しているキーワードによる検索にも対応できるようにしたいと思います。
get_search_resultsのオーバーライド
Djangoのadmin.ModelAdmin
には検索用のメソッドである、get_search_results
が用意されています。このメソッドはadmin.ModelAdmin
内のsearch_fields
で指定されたfieldに基づいて検索クエリを発行するというものです。
公式にはこのメソッドのオーバライドの一例が記載されています。
queryset |= self.model.objects.filter(age=search_term_as_int)
このような記載によって、別のテーブルの検索クエリを追加しています。
名前による検索の実装
まず、前提となるテーブル構造は以下の通りです。Userテーブルでユーザの苗字と名前を保管し、TableDataではユーザidを参照しています。
class User(models.Model): # 実際はAbstractBaseUser, PermissionsMixinを使っていますが簡易版としてmodels.Model
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class TableData(models.Model):
user_id = models.IntegerField()
field1 = models.IntegerField()
上記のTableDataをadmin画面で表示し、名前で検索をかけるためのModelAdminの実装は以下の通りです。
class TableDataAdmin(admin.ModelAdmin):
list_display = ('user_id', 'name', 'column1')
search_fields = ('user_id', 'field1')
def name(self, obj): # TableDataにnameというfieldはないのでカスタム実装
user = User.objects.filter(id=obj.user_id).first()
if user is not None:
return '{} {}'.format(user.last_name, user.first_name)
return None
def get_search_results(self, request, queryset, search_term):
queryset, may_have_duplicates = super().get_search_results(
request, queryset, search_term,
)
possible_name_list = [ # ありうる名前の組み合わせを作成
(
user.id,
user.last_name,
user.first_name,
user.last_name + user.first_name,
user.last_name + ' ' + user.first_name
) for user in User.objects.all()]
names = list(filter(lambda x: (search_term in x), possible_name_list)) # マッチした名前リストを抽出
for name in names:
queryset |= self.model.objects.filter(user_id=name[0])
return queryset, may_have_duplicates
admin.site.register(TableData, TableDataAdmin)
このようにテーブル内で直接的には管理していないキーワードでも検索をかけることができるようになりました。
今までユーザidで検索をかけていたのですが、これだと他のfieldに同じ数字があるとそれにもひっかかってしまいとても見にくかったです… 名前で検索できるようになっただけでかなりストレスは減りました。
コメント