Django管理应用:构建动态的管理员操作列表

7
我正在尝试使用ModelAdmin上的get_actions()方法动态构建管理员操作列表。每个操作都与另一个模型的特定实例相关联,由于可能会添加或删除新实例,因此我希望确保操作列表反映这一点。
以下是ModelAdmin
class PackageAdmin(admin.ModelAdmin):
    list_display = ('name', 'quality')

    def _actions(self, request):
        for q in models.Quality.objects.all():
            action = lambda modeladmin, req, qset: qset.update(quality=q)
            name = "mark_%s" % (q,)
            yield (name, (action, name, "Mark selected as %s quality" % (q,)))

    def get_actions(self, request):
        return dict(action for action in self._actions(request))

这个奇怪的重复元组字典返回值是由Django文档中get_actions()解释的。

正如预期的那样,这将导致适当命名的管理操作列表,用于将Quality外键批量分配给Package对象。

问题在于,无论我选择哪个操作,都会将同一个Quality对象分配给所选的Package

我认为使用lambda关键字创建的闭包都包含对同一q对象的引用,因此每次迭代都会为每个函数更改q的值。

我能打破这个引用,使我仍然可以使用包含不同q值的闭包列表吗?


编辑:我意识到在这个例子中使用lambda是不必要的。可以用以下代码替代:

action = lambda modeladmin, req, qset: qset.update(quality=q)

我可以简单地使用def

def action(modeladmin, req, qset):
    return qset.update(quality=q)
3个回答

8
尝试
   def make_action(quality):
        return lambda modeladmin, req, qset: qset.update(quality=quality)

   for q in models.Quality.objects.all():
       action = make_action(q)
       name = "mark_%s" % (q,)
       yield (name, (action, name, "Mark selected as %s quality" % (q,)))

如果那不起作用,我怀疑bug与你使用的yield有关。也许尝试:

def make_action(quality):
    name = 'mark_%s' % quality
    action = lambda modeladmin, req, qset: qset.update(quality=quality)
    return (name, (action, name, "Mark selected as %s quality" % quality))

def get_actions(self, request):
    return dict([make_action for q in models.Quality.objects.all()])

1
谢谢,我刚刚想到了一个与你的第二个建议类似的解决方案,正要发布时看到了这个。感谢你的时间,点赞。 - Ben James
我正在努力让它工作。你能否发布一个完整的可运行示例? - Andy Baker
啊,我明白了: (编辑 - 评论中没有代码格式!糟糕...)def get_actions(self, request): def make_action(quality): name = 'mark_%s' % quality action = lambda modeladmin, req, qset: qset.update(quality=quality) return (name, (action, name, "Mark selected as %s quality" % quality)) return dict(make_action(q) for q in models.Quality.objects.all()) - Andy Baker

5

如我在对andylei的回答中所提到的,我刚找到了一个解决方案;使用另一个函数来创建闭包似乎会破坏引用,这意味着现在每个操作都会引用正确的 Quality 实例。

def create_action(quality):
    fun = lambda modeladmin, request, queryset: queryset.update(quality=quality)
    name = "mark_%s" % (quality,)
    return (name, (fun, name, "Mark selected as %s quality" % (quality,)))

class PackageAdmin(admin.ModelAdmin):
    list_display = ('name', 'quality')

    def get_actions(self, request):
        return dict(create_action(q) for q in models.Quality.objects.all())

0

我很惊讶在循环中 q 保持相同的对象。

你的 lambda 表达式中使用 quality=q.id,它能正常工作吗?


谢谢您的时间,但这并没有解决问题。我猜测lambda中的q.id仍然是按引用传递的。 - Ben James

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接