django管理员操作没有选择对象

时间:2010-12-21 15:32:59

标签: django django-admin

是否可以为django管理员创建一个自定义管理操作,不需要选择一些对象来运行它?

如果您尝试在不选择对象的情况下运行操作,则会收到消息:

Items must be selected in order to perform actions on them. No items have been changed.

是否有办法覆盖此行为并让操作仍然运行?

9 个答案:

答案 0 :(得分:11)

接受的答案在django 1.6中对我不起作用,所以我最终得到了这个:

from django.contrib import admin

class AdvWebUserAdmin(admin.ModelAdmin):

    ....

    def changelist_view(self, request, extra_context=None):
        if 'action' in request.POST and request.POST['action'] == 'your_action_here':
            if not request.POST.getlist(admin.ACTION_CHECKBOX_NAME):
                post = request.POST.copy()
                for u in MyModel.objects.all():
                    post.update({admin.ACTION_CHECKBOX_NAME: str(u.id)})
                request._set_post(post)
        return super(AdvWebUserAdmin, self).changelist_view(request, extra_context)

当调用my_action并且未选择任何内容时,请选择db中的所有MyModel个实例。

答案 1 :(得分:6)

我想要这个但最终决定不使用它。在此发布以供将来参考。


在操作中添加额外的属性(如acts_on_all):

def my_action(modeladmin, request, queryset):
    pass
my_action.short_description = "Act on all %(verbose_name_plural)s"
my_action.acts_on_all = True

ModelAdmin中,覆盖changelist_view以检查您的财产。

如果请求方法是POST,并且指定了操作,并且操作callable将您的属性设置为True,则修改表示所选对象的列表。

def changelist_view(self, request, extra_context=None):
    try:
        action = self.get_actions(request)[request.POST['action']][0]
        action_acts_on_all = action.acts_on_all
    except (KeyError, AttributeError):
        action_acts_on_all = False

    if action_acts_on_all:
        post = request.POST.copy()
        post.setlist(admin.helpers.ACTION_CHECKBOX_NAME,
                     self.model.objects.values_list('id', flat=True))
        request.POST = post

    return admin.ModelAdmin.changelist_view(self, request, extra_context)

答案 2 :(得分:5)

Yuji走在正确的轨道上,但我使用了一个可能对你有用的更简单的解决方案。如果您按照下面的步骤覆盖response_action,则可以在检查发生之前将空查询集替换为包含所有对象的查询集。此代码还会检查您正在运行的操作,以确保在更改查询集之前已批准在所有对象上运行,因此您可以将其限制为仅在某些情况下发生。

    def response_action(self, request, queryset):
    # override to allow for exporting of ALL records to CSV if no chkbox selected
    selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
    if request.META['QUERY_STRING']:
        qd = dictify_querystring(request.META['QUERY_STRING'])
    else:
        qd = None
    data = request.POST.copy()
    if len(selected) == 0 and data['action'] in ('export_to_csv', 'extended_export_to_csv'):
        ct = ContentType.objects.get_for_model(queryset.model)
        klass = ct.model_class()
        if qd:
            queryset = klass.objects.filter(**qd)[:65535] # cap at classic Excel maximum minus 1 row for headers
        else:
            queryset = klass.objects.all()[:65535] # cap at classic Excel maximum minus 1 row for headers
        return getattr(self, data['action'])(request, queryset)
    else:
        return super(ModelAdminCSV, self).response_action(request, queryset)

答案 3 :(得分:2)

  

有没有办法覆盖它   行为,让行动运行   呢?

我会说不,没有简单的方法。

如果你grep你的错误信息,你会看到代码在django.contrib.admin.options.py,问题代码在changelist_view的深处。

action_failed = False
selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)

# Actions with no confirmation
if (actions and request.method == 'POST' and
        'index' in request.POST and '_save' not in request.POST):
    if selected:
        response = self.response_action(request, queryset=cl.get_query_set())
        if response:
            return response
        else:
            action_failed = True
    else:
        msg = _("Items must be selected in order to perform "
                "actions on them. No items have been changed.")
        self.message_user(request, msg)
        action_failed = True

它也在response_action函数中使用,所以你不能只是覆盖changelist_template并使用它 - 最简单的方法是定义你自己的动作有效性检查器和跑步者。


如果你真的想使用那个下拉列表,这是一个没有保证的想法。

如何为无选择的管理操作定义新属性:myaction.selectionless = True

在被覆盖的response_action中复制changelist_view功能,该功能仅适用于指定了特定标记的操作,然后返回“真实”changelist_view

    # There can be multiple action forms on the page (at the top
    # and bottom of the change list, for example). Get the action
    # whose button was pushed.
    try:
        action_index = int(request.POST.get('index', 0))
    except ValueError:
        action_index = 0

    # Construct the action form.
    data = request.POST.copy()
    data.pop(helpers.ACTION_CHECKBOX_NAME, None)
    data.pop("index", None)

    # Use the action whose button was pushed
    try:
        data.update({'action': data.getlist('action')[action_index]})
    except IndexError:
        # If we didn't get an action from the chosen form that's invalid
        # POST data, so by deleting action it'll fail the validation check
        # below. So no need to do anything here
        pass

    action_form = self.action_form(data, auto_id=None)
    action_form.fields['action'].choices = self.get_action_choices(request)

    # If the form's valid we can handle the action.
    if action_form.is_valid():
        action = action_form.cleaned_data['action']
        select_across = action_form.cleaned_data['select_across']
        func, name, description = self.get_actions(request)[action]

        if func.selectionless:
             func(self, request, {})

调用'真实'动作时仍然会出错。如果调用重写的操作,您可以修改request.POST以删除操作。

其他方式涉及黑客攻击太多的东西。我想至少。

答案 4 :(得分:0)

由于对象选择不是您需要的一部分,因此您可以通过创建自己的管理视图来获得最佳服务。

制作自己的管理视图非常简单:

  1. 编写视图功能
  2. 在其上放置一个@staff_member_required装饰器
  3. 向您的URLconf添加指向该视图的模式
  4. 通过overriding the relevant admin template(s)
  5. 添加指向它的链接

    您也可以使用a new 1.1 feature related to this,但您可能会发现我刚才所说的更简单。

答案 5 :(得分:0)

好吧,对于那些顽固到想要这个工作的人来说,这是一个丑陋的黑客(对于django 1.3),即使你没有选择任何东西也会允许任何动作运行。

你必须欺骗原来的changelist_view,以为你选择了一些东西。

class UsersAdmin(admin.ModelAdmin):

    def changelist_view(self, request, extra_context=None):
        post = request.POST.copy()
        if helpers.ACTION_CHECKBOX_NAME not in post:
            post.update({helpers.ACTION_CHECKBOX_NAME:None})
            request._set_post(post)
        return super(ContributionAdmin, self).changelist_view(request, extra_context)

因此,在你的modeladmin中,你覆盖changelist_view,添加到request.POST一个django用来存储所选对象的id的键。

在您的操作中,您可以检查是否没有选定的项目:

if queryset == None:
    do_your_stuff()

不言而喻,你不应该这样做。

答案 6 :(得分:0)

我使用以下mixin来创建不需要用户选择至少一个对象的动作。它还允许您获取用户刚刚过滤的查询集:https://gist.github.com/rafen/eff7adae38903eee76600cff40b8b659

这里有一个如何使用它的例子(有关如何在链接上使用它的更多信息):

@admin.register(Contact)
class ContactAdmin(ExtendedActionsMixin, admin.ModelAdmin):
    list_display = ('name', 'country', 'state')
    actions = ('export',)
    extended_actions = ('export',)

    def export(self, request, queryset):
        if not queryset:
            # if not queryset use the queryset filtered by the URL parameters
            queryset = self.get_filtered_queryset(request)

        # As usual do something with the queryset

答案 7 :(得分:0)

我对@AndyTheEntity响应进行了更改,以避免每行调用一次。

        def changelist_view(self, request, extra_context=None):
                actions = self.get_actions(request)
                if (actions and request.method == 'POST' and 'index' in request.POST and
                        request.POST['action'].startswith('generate_report')):
                    data = request.POST.copy()
                    data['select_across'] = '1'
                    request.POST = data
                    response = self.response_action(request, queryset=self.get_queryset(request))
                    if response:
                        return response
                return super(BaseReportAdmin, self).changelist_view(request, extra_context)

答案 8 :(得分:0)

我找到的最简单的解决方案是按照Django docs创建django admin函数,然后在您的网站admin中随机选择任何对象并运行该函数。这会将该项传递给您的函数,但您根本不会在任何地方使用它,因此它是多余的。为我工作。