Django 管理员自定义更改列表参数:覆盖 /?e=1

发布于 2024-12-21 04:10:41 字数 3240 浏览 2 评论 0原文

我正在尝试将自定义参数传递给 Django 管理更改列表视图,以便我可以以专门的方式过滤列表。我想根据名为“active_pp”的 GET 参数过滤 2 个字段(start_date 和 end_date)上的查询集。我已经使过滤正常工作,但无法传入指定是否应显示过滤结果或正常结果的 GET 查询参数。

我知道,出于安全考虑,Django 管理员会过滤掉传递给它的任何与指定模型字段无关的查询参数;一旦发现错误的参数,管理员会将用户重定向到当前视图,但将 GET 查询参数替换为 e=1。我想将我的自定义“active_pp”参数列入白名单,这样页面就不会被重定向,并且我将能够使用该参数。

以下是 admin.py 中具有查询集自定义的 ModelAdmin 示例。

class FeaduredAdmin(admin.ModelAdmin): 

    ....

    def get_changelist(self, request, **kwargs):
        from django.contrib.admin.views.main import ChangeList

        # Try to get the 'active_pp' query parameter
        active_pp = request.GET.get('active_pp',None)

        # Define a custom ChangeList class with a custom queryset
        class ActiveChangeList(ChangeList):
            def get_query_set(self, *args, **kwargs):
                now = datetime.datetime.now()
                qs = super(ActiveChangeList, self).get_query_set(*args, **kwargs)
                return qs.filter((Q(start_date=None) | Q(start_date__lte=now))
                             & (Q(end_date=None) | Q(end_date__gte=now)))

        # use the custom ChangeList class if the parameter exists
        if active_pp:
             return ActiveChangeList

        return ChangeList

有谁知道如何将传递给change_list的自定义GET查询字符串参数列入白名单?

感谢您的阅读和考虑, Joe

更新:

使用 Uvasal 提供的链接,我能够正确地将 GET 参数列入白名单。

class ActiveFilterAminForm(forms.Form):
    active_pp = forms.CharField()

class FeaduredAdmin(admin.ModelAdmin): 

    ....

    # Based on: http://djangosnippets.org/snippets/2322/
    advanced_search_form = ActiveFilterAminForm()

    def get_changelist(self, request, **kwargs):

        from django.contrib.admin.views.main import ChangeList
        active_pp = self.other_search_fields.get('active_pp',None)
        # now we have the active_pp parameter that was passed in and can use it.

        class ActiveChangeList(ChangeList):

            def get_query_set(self, *args, **kwargs):
                now = datetime.datetime.now()
                qs = super(ActiveChangeList, self).get_query_set(*args, **kwargs)
                return qs.filter((Q(start_date=None) | Q(start_date__lte=now))
                                 & (Q(end_date=None) | Q(end_date__gte=now)))

        if not active_pp is None:
            return ActiveChangeList

        return ChangeList


    def lookup_allowed(self, lookup):
        if lookup in self.advanced_search_form.fields.keys():
            return True
        return super(MyModelAdmin, self).lookup_allowed(lookup)


    def changelist_view(self, request, extra_context=None, **kwargs):
        self.other_search_fields = {} 
        asf = self.advanced_search_form
        extra_context = {'asf':asf}

        request.GET._mutable=True

        for key in asf.fields.keys():
            try:
                temp = request.GET.pop(key)
            except KeyError:
                pass 
            else:
                if temp!=['']: 
                    self.other_search_fields[key] = temp 

        request.GET_mutable=False
        return super(FeaduredProductAdmin, self)\
               .changelist_view(request, extra_context=extra_context)

I'm trying to pass in a custom argument to the Django Admin change list view so I can filter the list in a specialized way. I'd like to filter the queryset on 2 fields, start_date and end_date, based on the GET parameter called 'active_pp'. I've gotten the filtering to work correctly, but I'm not able to pass in a GET query parameter that specifies whether I should display the filtered results or the normal results.

I know that, due to security, the Django Admin filters out any query parameters passed to it that aren't related to specified model fields; upon finding bad arguments, the admin redirects the user to the current view but replaces the GET query parameters with e=1. I'd like to whitelist my custom 'active_pp' parameter so the page won't be redirected and I'll be able to use the parameter.

Here is an example of the ModelAdmin in admin.py with the queryset customization.

class FeaduredAdmin(admin.ModelAdmin): 

    ....

    def get_changelist(self, request, **kwargs):
        from django.contrib.admin.views.main import ChangeList

        # Try to get the 'active_pp' query parameter
        active_pp = request.GET.get('active_pp',None)

        # Define a custom ChangeList class with a custom queryset
        class ActiveChangeList(ChangeList):
            def get_query_set(self, *args, **kwargs):
                now = datetime.datetime.now()
                qs = super(ActiveChangeList, self).get_query_set(*args, **kwargs)
                return qs.filter((Q(start_date=None) | Q(start_date__lte=now))
                             & (Q(end_date=None) | Q(end_date__gte=now)))

        # use the custom ChangeList class if the parameter exists
        if active_pp:
             return ActiveChangeList

        return ChangeList

Does anyone know how to whitelist custom GET querystring arguments passed to the change_list?

Thanks for reading and for your consideration,
Joe

UPDATE:

Using Uvasal's provided links, I was able to properly whitelist the GET parameter.

class ActiveFilterAminForm(forms.Form):
    active_pp = forms.CharField()

class FeaduredAdmin(admin.ModelAdmin): 

    ....

    # Based on: http://djangosnippets.org/snippets/2322/
    advanced_search_form = ActiveFilterAminForm()

    def get_changelist(self, request, **kwargs):

        from django.contrib.admin.views.main import ChangeList
        active_pp = self.other_search_fields.get('active_pp',None)
        # now we have the active_pp parameter that was passed in and can use it.

        class ActiveChangeList(ChangeList):

            def get_query_set(self, *args, **kwargs):
                now = datetime.datetime.now()
                qs = super(ActiveChangeList, self).get_query_set(*args, **kwargs)
                return qs.filter((Q(start_date=None) | Q(start_date__lte=now))
                                 & (Q(end_date=None) | Q(end_date__gte=now)))

        if not active_pp is None:
            return ActiveChangeList

        return ChangeList


    def lookup_allowed(self, lookup):
        if lookup in self.advanced_search_form.fields.keys():
            return True
        return super(MyModelAdmin, self).lookup_allowed(lookup)


    def changelist_view(self, request, extra_context=None, **kwargs):
        self.other_search_fields = {} 
        asf = self.advanced_search_form
        extra_context = {'asf':asf}

        request.GET._mutable=True

        for key in asf.fields.keys():
            try:
                temp = request.GET.pop(key)
            except KeyError:
                pass 
            else:
                if temp!=['']: 
                    self.other_search_fields[key] = temp 

        request.GET_mutable=False
        return super(FeaduredProductAdmin, self)\
               .changelist_view(request, extra_context=extra_context)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

独孤求败 2024-12-28 04:10:41

我知道这是一篇旧帖子,但刚刚遇到了这个需要,并发现了一个非常简短的解决方案,我想我会分享。这里的关键是创建一个不影响查询集并接受查找中传递给它的任何内容作为有效选项的过滤器。类似于以下内容:

from django.contrib.admin import SimpleListFilter    

class PassThroughFilter(SimpleListFilter):
    title = ''
    parameter_name = 'pt'
    template = 'admin/hidden_filter.html'

    def lookups(self, request, model_admin):
        return (request.GET.get(self.parameter_name), ''),

    def queryset(self, request, queryset):
        return queryset

hidden_​​filter 模板为空,以防止向过滤区域添加任何内容,并且 lookups 方法将始终返回我为 pt 输入的任何内容 参数作为有效的过滤器条目。这将防止页面加载时弹出 ?e=1 错误。

任何管理员都可以使用 pt 参数重复使用它。如果您需要为单个管理员传递多个参数,则只需将其子类化为单独的过滤器,并使用您需要的任何参数覆盖 parameter_name 即可。这将具有允许查询字符串中的这些参数的效果,而不影响查询集或显示在过滤器列中,然后您可以将它们用于其他地方需要它们的任何目的。

希望这可以帮助某人。

I know this is an old post, but just ran into a need for this and discovered a very short and simple solution that I thought I would share. Key here is to make a filter that doesn't affect the queryset and accepts anything passed to it in the lookups as valid option. Something like the following:

from django.contrib.admin import SimpleListFilter    

class PassThroughFilter(SimpleListFilter):
    title = ''
    parameter_name = 'pt'
    template = 'admin/hidden_filter.html'

    def lookups(self, request, model_admin):
        return (request.GET.get(self.parameter_name), ''),

    def queryset(self, request, queryset):
        return queryset

The hidden_filter template is blank to prevent adding anything to the filter area, and the lookups method will always return whatever I have entered for the pt parameter as a valid filter entry. This will prevent the ?e=1 error from popping up as the page loads.

This can be reused with any admin, using the pt parameter. If you need to pass multiple parameters for a single admin, then just subclass this into separate filters and override parameter_name with whatever parameters you need. This will have the effect of allowing those parameters in the query string without affecting the queryset or showing up in the filter column, and you can then use them for whatever purpose you needed them elsewhere.

Hope this helps someone down the road.

两人的回忆 2024-12-28 04:10:41

我认为您只需将自定义过滤器字段放入 search_fields 类变量中,如高级搜索中所述姜戈片段。

您应该能够修改代码片段以支持日期范围。

I think you just need to put your custom filter fields in the search_fields class variable as outlined in the Advanced Search Django Snippet.

You should be able to modify the snippet to support date ranges as well.

思念满溢 2024-12-28 04:10:41

总之,这里是上面使用的未记录的黑客:

设置 request.GET._mutable = True,然后 request.GET.pop() 关闭自定义 GET 参数你正在使用。

In summary, here is the undocumented hack used above:

set request.GET._mutable = True, then request.GET.pop() off the custom GET argument(s) you are using.

拧巴小姐 2024-12-28 04:10:41

我已经简化了接受的解决方案 这将不起作用,因为 ModelAdmin 实例在请求之间重用,并且 self.active_pp 存储例如 change_view 请求的先前值

class FeaduredAdmin(admin.ModelAdmin):
    ...

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if self.active_pp:
            qs = qs.filter(...)
        return qs

    def changelist_view(self, request, extra_context=None):
        if request.GET:
            request.GET._mutable=True
            try:
                self.active_pp = request.GET.pop('pp')[0]
            except KeyError:
                self.active_pp = None
            request.GET_mutable=False
        return super().changelist_view(request, extra_context=extra_context)

这解决方案故意使 pp 不参与其他管理过滤器。如果您需要 pp 受到其他过滤器的尊重,请使用 @meesterguyperson 解决方案。

I have simplified accepted solution This will not work as ModelAdmin instance is reused between requests and self.active_pp stores previous value for e.g. change_view request:

class FeaduredAdmin(admin.ModelAdmin):
    ...

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if self.active_pp:
            qs = qs.filter(...)
        return qs

    def changelist_view(self, request, extra_context=None):
        if request.GET:
            request.GET._mutable=True
            try:
                self.active_pp = request.GET.pop('pp')[0]
            except KeyError:
                self.active_pp = None
            request.GET_mutable=False
        return super().changelist_view(request, extra_context=extra_context)

This solution intentionally makes pp not participate in other admin filters. If you need pp to be honored by other filters use @meesterguyperson solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文