Django adminでレコードに登録されていないキーワードで検索したい (get_search_resultsのカスタマイズ)

技術備忘録

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に同じ数字があるとそれにもひっかかってしまいとても見にくかったです… 名前で検索できるようになっただけでかなりストレスは減りました。

コメント

タイトルとURLをコピーしました