如何在django admin中创建高级自定义搜索表单并使用django管理员更改列表显示

时间:2022-01-19 07:21:51

How can I create an advanced custom search form in Django admin and use Django admins change list display. My advanced search form has several fields, including:

如何在Django admin中创建高级自定义搜索表单并使用Django管理员更改列表显示。我的高级搜索表单有几个字段,包括:

  • region
  • city
  • province

admin.py:

class PropertyAdmin(ModelAdmin):
    change_list_template = "property/admin/property_change_list.html"
    list_per_page = 20
    list_display_links = ('property_country_province_city',)
    search_fields = ('id',)
    list_filter = ('is_sale','is_rent','is_presales','estate_type','water')
    list_display_links = ('property_type',)

Models.py

class Property(models.Model):
    objects = PublicPropertyManager()
    title = models.CharField(_("title"), max_length = 80, blank=True)
    country = models.ForeignKey(Country, verbose_name=_("Country"))
    province = models.ForeignKey(Province, verbose_name=_("Province"))
    city = models.ForeignKey(City, verbose_name=_("City"))
    region = models.ForeignKey(Region, verbose_name=_("Region"))
    address = models.CharField(
        verbose_name=_("address"), max_length = 250, blank=True, null=True
    )

2 个解决方案

#1


2  

class CandidateAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.TextField: {'widget': Textarea(attrs={'rows': 4, 'cols': 40})},
    }
    list_display = ('id', 'first_name', 'last_name', 'current_company',
                    'title', 'gender', 'country', 'status', 'consultant',
                    'mobile_number', 'civil_reg_number', 'added_date')
    list_display_links = ('id', 'first_name', 'last_name')
    list_filter = ('consultant', 'status', 'gender', 'country', 'city')
    form = CandidateForm
    advanced_search_form = AdvancedSearchForm()
    other_search_fields = {}
    search_fields = ['first_name', 'last_name', 'civil_reg_number',
                     'status', 'cvmn', 'cven',
                     'address', 'mobile_number', 'notes']
    actions = [recruited_by_scc]

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'consultant':
            kwargs['initial'] = Consultant.objects.get(user=request.user)
            return db_field.formfield(**kwargs)
        return super(CandidateAdmin, self).\
            formfield_for_foreignkey(db_field, request, **kwargs)

    def changelist_view(self, request, extra_context=None, **kwargs):
        extra_context = {'asf':self.advanced_search_form}
        request.POST._mutable=True
        post_keys = []
        search_keys = []
        for key in request.POST.keys():
            value=request.POST.get(key)
            if value!='' and key!='csrfmiddlewaretoken':
                post_keys.append(key)
        for key in self.other_search_fields.keys():
            value = [x for x in self.other_search_fields.get(key) if x!='']
            if value:
                search_keys.append(key)
        if post_keys!=search_keys and len(post_keys)>0 and len(search_keys)>0:
            self.other_search_fields = {}
        for key in self.advanced_search_form.fields.keys():
            try:
                temp = request.POST.pop(key)
            except KeyError:
                pass
            else:
                if temp!=['']:
                    self.other_search_fields[key] = temp
        request.session[request.user.username] = self.other_search_fields
        self.other_search_fields = {}
        request.POST._mutable=False
        return super(CandidateAdmin, self).changelist_view(request, extra_context=extra_context)

    def queryset(self, request):
        qs = super(CandidateAdmin, self).queryset(request)
        search_query = []
        #self.other_search_fields = request.session[request.user.username]
        if request.session[request.user.username]:
            other_search_fields = request.session[request.user.username]
            for key in other_search_fields.keys():
                key_values = other_search_fields.get(key)
                key_values =[value for value in key_values if value!='']
                if key_values:
                    questions = [('{0}__icontains'.format(key), value) for value in key_values]
                    q_list = [Q(x) for x in questions]
                    query = reduce(operator.or_, q_list)
                    search_query.append(query)
            if search_query:
                make_query = reduce(operator.and_, search_query)
                return qs.filter(make_query)
        return qs

admin/search_form.html

   {% load i18n grp_tags %}
    {% if cl.search_fields %}
        <!-- Search Form -->
        {% if asf %}
        <form action="" method="POST"> {% csrf_token %}
            {{asf}}
            <input type="submit" value="search" />
        </form>
        {% else %}
         <!-- Search Form -->
         <form id="grp-changelist-search" action="" method="get">
             <input type="text" name="{{ search_var }}" id="grp-changelist-search" class="grp-search-field" value="{{ cl.query }}" />
             <button type="submit" value="" class="grp-search-button"></button>
             {% for pair in cl.params.items %}
                 {% ifnotequal pair.0 search_var %}<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}"/>{% endifnotequal %}
             {% endfor %}
         </form>
        {% endif %}
    {% endif %}

#2


0  

I'm not sure if you meant that you've already written a custom search, but in this case, depending on what you want to do, you may not need that. You can search on related model fields through the default search_fields with the caveat that this'll generate related lookups/joins.

我不确定你是否意味着你已经编写了自定义搜索,但在这种情况下,根据你想要做什么,你可能不需要那样做。您可以通过默认的search_fields搜索相关的模型字段,但需要注意这将生成相关的查找/连接。

I'm, not sure what City, Province, and Region look like, but assuming they have a name field, this should work (if they have an id field, this would just be province__id, etc.):

我不确定City,Province和Region是什么样子,但假设他们有一个名字字段,这应该有用(如果他们有一个id字段,这只是省___,等等):

# admin.py

class PropertyAdmin(ModelAdmin):
...
search_fields = (
    'region__name',
    'city__name',
    'province__name',
)

Note that if you render any of the ForeignKeys in the list_display, you'll likely want to override the queryset to use select_related on any fields that you show to avoid generating a lot of unnecessary SQL queries:

请注意,如果您在list_display中呈现任何ForeignKeys,您可能希望覆盖查询集以在您显示的任何字段上使用select_related,以避免生成大量不必要的SQL查询:

# admin.py
class PropertyAdmin(ModelAdmin):
    ...
    def get_queryset(self, request);
        qs = super(PropertyAdmin, self).get_queryset(request)
        return qs.select_related('province', 'city', 'region',)

P.S. It also looks like you define list_display_links twice there, so the second definition is overriding the first one, which may not be the behavior you want.

附:它看起来像你在那里定义了list_display_links两次,所以第二个定义覆盖了第一个定义,这可能不是你想要的行为。

#1


2  

class CandidateAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.TextField: {'widget': Textarea(attrs={'rows': 4, 'cols': 40})},
    }
    list_display = ('id', 'first_name', 'last_name', 'current_company',
                    'title', 'gender', 'country', 'status', 'consultant',
                    'mobile_number', 'civil_reg_number', 'added_date')
    list_display_links = ('id', 'first_name', 'last_name')
    list_filter = ('consultant', 'status', 'gender', 'country', 'city')
    form = CandidateForm
    advanced_search_form = AdvancedSearchForm()
    other_search_fields = {}
    search_fields = ['first_name', 'last_name', 'civil_reg_number',
                     'status', 'cvmn', 'cven',
                     'address', 'mobile_number', 'notes']
    actions = [recruited_by_scc]

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'consultant':
            kwargs['initial'] = Consultant.objects.get(user=request.user)
            return db_field.formfield(**kwargs)
        return super(CandidateAdmin, self).\
            formfield_for_foreignkey(db_field, request, **kwargs)

    def changelist_view(self, request, extra_context=None, **kwargs):
        extra_context = {'asf':self.advanced_search_form}
        request.POST._mutable=True
        post_keys = []
        search_keys = []
        for key in request.POST.keys():
            value=request.POST.get(key)
            if value!='' and key!='csrfmiddlewaretoken':
                post_keys.append(key)
        for key in self.other_search_fields.keys():
            value = [x for x in self.other_search_fields.get(key) if x!='']
            if value:
                search_keys.append(key)
        if post_keys!=search_keys and len(post_keys)>0 and len(search_keys)>0:
            self.other_search_fields = {}
        for key in self.advanced_search_form.fields.keys():
            try:
                temp = request.POST.pop(key)
            except KeyError:
                pass
            else:
                if temp!=['']:
                    self.other_search_fields[key] = temp
        request.session[request.user.username] = self.other_search_fields
        self.other_search_fields = {}
        request.POST._mutable=False
        return super(CandidateAdmin, self).changelist_view(request, extra_context=extra_context)

    def queryset(self, request):
        qs = super(CandidateAdmin, self).queryset(request)
        search_query = []
        #self.other_search_fields = request.session[request.user.username]
        if request.session[request.user.username]:
            other_search_fields = request.session[request.user.username]
            for key in other_search_fields.keys():
                key_values = other_search_fields.get(key)
                key_values =[value for value in key_values if value!='']
                if key_values:
                    questions = [('{0}__icontains'.format(key), value) for value in key_values]
                    q_list = [Q(x) for x in questions]
                    query = reduce(operator.or_, q_list)
                    search_query.append(query)
            if search_query:
                make_query = reduce(operator.and_, search_query)
                return qs.filter(make_query)
        return qs

admin/search_form.html

   {% load i18n grp_tags %}
    {% if cl.search_fields %}
        <!-- Search Form -->
        {% if asf %}
        <form action="" method="POST"> {% csrf_token %}
            {{asf}}
            <input type="submit" value="search" />
        </form>
        {% else %}
         <!-- Search Form -->
         <form id="grp-changelist-search" action="" method="get">
             <input type="text" name="{{ search_var }}" id="grp-changelist-search" class="grp-search-field" value="{{ cl.query }}" />
             <button type="submit" value="" class="grp-search-button"></button>
             {% for pair in cl.params.items %}
                 {% ifnotequal pair.0 search_var %}<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}"/>{% endifnotequal %}
             {% endfor %}
         </form>
        {% endif %}
    {% endif %}

#2


0  

I'm not sure if you meant that you've already written a custom search, but in this case, depending on what you want to do, you may not need that. You can search on related model fields through the default search_fields with the caveat that this'll generate related lookups/joins.

我不确定你是否意味着你已经编写了自定义搜索,但在这种情况下,根据你想要做什么,你可能不需要那样做。您可以通过默认的search_fields搜索相关的模型字段,但需要注意这将生成相关的查找/连接。

I'm, not sure what City, Province, and Region look like, but assuming they have a name field, this should work (if they have an id field, this would just be province__id, etc.):

我不确定City,Province和Region是什么样子,但假设他们有一个名字字段,这应该有用(如果他们有一个id字段,这只是省___,等等):

# admin.py

class PropertyAdmin(ModelAdmin):
...
search_fields = (
    'region__name',
    'city__name',
    'province__name',
)

Note that if you render any of the ForeignKeys in the list_display, you'll likely want to override the queryset to use select_related on any fields that you show to avoid generating a lot of unnecessary SQL queries:

请注意,如果您在list_display中呈现任何ForeignKeys,您可能希望覆盖查询集以在您显示的任何字段上使用select_related,以避免生成大量不必要的SQL查询:

# admin.py
class PropertyAdmin(ModelAdmin):
    ...
    def get_queryset(self, request);
        qs = super(PropertyAdmin, self).get_queryset(request)
        return qs.select_related('province', 'city', 'region',)

P.S. It also looks like you define list_display_links twice there, so the second definition is overriding the first one, which may not be the behavior you want.

附:它看起来像你在那里定义了list_display_links两次,所以第二个定义覆盖了第一个定义,这可能不是你想要的行为。