在 Django 中创建通用搜索视图

Creating a generic search view in Django

我正在努力在 Django 中创建我的自定义通用视图,以便轻松地为某些模型创建搜索页面。我想像这样使用它:

class MyModelSearchView(SearchView):
    template_name = 'path/to/template.html'
    model = MyModel
    fields = ['name', 'email', 'whatever']

这将导致 returns GET 上的搜索表单以及 POST 上的表单和结果。 fields 指定 MyModel 的哪些字段可供用户搜索。

class SearchView(FormView):
    def get_form(self, form_class=None):
        # what I'v already tried:
        class SearchForm(ModelForm):
            class Meta:
                model = self.model
                fields = self.fields
        return SearchForm()

    def post(self, request, *args, **kwargs):
        # perform searching and return results

上述代码的问题在于,如果某些字段未正确填写,则不会提交表单。应该允许用户只提供部分字段进行搜索,但使用我提供的代码,使用 ModelForm 生成的表单可以防止这种情况发生(例如,因为模型中的字段不能为空)。

我的问题是:

如果可能的话,我不想手动写表格。

完成此操作的一种方法是在 MyModel 中的字段上设置 blank=True,如 the docs:

中所示

If the model field has blank=True, then required is set to False on the form field. Otherwise, required=True.

但要将其作为通用解决方案,您不能指望能够修改模型字段。您可以在创建实例后立即将字段的 required 属性设置为 False

class SearchForm(ModelForm):

    class Meta:
        model = self.model
        fields = self.fields

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for (field_name, field) in self.fields.items():
            field.required = False

由于您正在使用 ModelForm 进行搜索,因此您应该通过覆盖 __init__ 方法将所有字段设置为 required=False

def get_form(self, form_class=None):
    # what I'v already tried:
    class SearchForm(ModelForm):
        class Meta:
            model = self.model
            fields = self.fields

        def __init__(self, *args, **kwargs):
            super(SearchForm, self).__init__(*args, **kwargs)
            for field in self.fields:
                self.fields[field].required = False

            return SearchForm()

虽然我建议您应该使用 django-filter,这样可以更轻松、更清晰地过滤您的搜索。首先你需要安装它:

pip install django-filter

然后将其添加到您的 INSTALLED_APPS。之后,您可以在您的应用程序中创建一个 filters.py 文件:

# myapp/filters.py

import django_filters as filters
from .models import MyModel

MyModelFilterSet(filters.FilterSet):

    class Meta:
        model = MyModel
        fields = ['name', 'email', 'whatever']

默认情况下,它将使用 __exact 查找进行过滤。您可以通过多种方式更改它,只需看一下 here and here. To know which lookups you can use, take a look here.

创建 filters.py 文件后,您可以将其添加到视图,如 ListView:

# myapp/views.py
from django.views.generic import ListView
from .filters import MyModelFilterSet
from .models import MyModel

class MyModelSearchView(ListView):
    template_name = 'path/to/template.html'
    model = MyModel

    def get_queryset(self):
        qs = self.model.objects.all()
        filtered_model_list = MyModelFilterSet(self.request.GET, queryset=qs)
        return filtered_model_list.qs

您可以使用 django-filter 做更多事情。这是完整的 documentation.