覆盖Django管理中的默认查询集


79

我的一个模型有一个delete标志,该标志用于全局隐藏对象:

class NondeletedManager(models.Manager):
    """Returns only objects which haven't been deleted"""

    def get_query_set(self):
        return super(NondeletedManager, self).get_query_set().exclude(deleted=True)

class Conversation(BaseModel):
    ...
    deleted = models.BooleanField(default=False)
    objects = NondeletedManager()
    all_conversations = models.Manager() # includes deleted conversations

如何覆盖Django管理模块使用的默认查询集以包括已删除的对话?


您真的需要针对这些简单查询的自定义管理器吗?
塞萨尔

3
是的,删除的对象应该被普遍忽略(管理页面上除外),因此设置默认值是有意义的。
Natan Yellin 2012年

Answers:


142

您可以在模型管理类中重写 get_queryset方法。

class MyModelAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(MyModelAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)

请注意,在Django <= 1.5中,该方法被命名为just queryset


3
在这种情况下如何运作?我可以修改创建的查询集ModelAdmin.queryset以包括已删除的对象吗?我不想自己构建查询集,而不是调用超类。
Natan Yellin 2012年

看我的回答,明白我的意思。是否可以完全重新实现该功能?
Natan Yellin 2012年

4
将答案实际放入答案中,而不只是链接,将很有帮助。该链接现在已失效,因此我将进行更新以给出解释。
2014年

16
在Django 1.6上,此方法已重命名get_queryset
Fernando Macedo 2014年

1
我无法使它正常工作。我收到一个错误消息,说.filter在qs对象中不存在。其次,除了请求以外,我也无法使用queryset。stackoverflow.com/questions/54472649/…任何帮助?
加里

8

Konrad是正确的,但这比文档中给出的示例更加困难。

已删除的对话不能包含在已经排除对话的查询集中。因此,除了完全重新实现admin.ModelAdmin.queryset之外,我没有看到其他选项。

class ConversationAdmin (admin.ModelAdmin):

    def queryset (self, request):
        qs = Conversation.all_conversations
        ordering = self.get_ordering(request)
        if ordering:
            qs = qs.order_by(*ordering)
        return qs

我不认为这有什么问题。使用两个管理器是必经之路。但是,Django管理员可以提供一个挂钩,因此您不必重新实现订购部分。
Thomas Orozco 2012年

3

以下内容有什么问题:

class Conversation(BaseModel):
    ...
    deleted = models.BooleanField(default=False)
    objects = models.Manager() # includes deleted conversations
    nondeleted_conversations = NondeletedManager()

因此,在您自己的应用程序/项目中,您可以使用Conversation.nondeleted_conversations()内置的管理应用程序来完成该任务。


1
我到处忽略删除的对象,但后台管理页面,所以我认为应该是默认的。此外,通过这种方式,我不需要通过添加删除对话的功能来更新旧版代码。
Natan Yellin 2012年

2

接受的解决方案对我来说非常有效,但是我需要更多的灵活性,因此最终扩展了变更列表视图以添加自定义queryset参数。现在,我可以像这样配置默认查询集/过滤器,并且仍可以使用其他过滤器(获取参数)对其进行修改:

def changelist_view(self, request, extra_context=None):
    if len(request.GET) == 0 :
        q = request.GET.copy()
        q['status__gt'] = 4
        request.GET = q
        request.META['QUERY_STRING'] = request.GET.urlencode()

    return super(WorksheetAdmin,self).changelist_view(request, extra_context=extra_context)

2

您可以使用Django代理模型执行此操作。

# models.py
class UnfilteredConversation(Conversation):
    class Meta:
        proxy = True

    # this will be the 'default manager' used in the Admin, and elsewhere
    objects = models.Manager() 

# admin.py
@admin.register(UnfilteredConversation)
class UnfilteredConversationAdmin(Conversation):
    # regular ModelAdmin stuff here
    ...

或者,如果您有现有的ModelAdmin类,则要重用:

admin.site.register(UnfilteredConversation, ConversationAdmin)

此方法避免了在原始会话模型上覆盖默认管理器时可能出现的问题-因为默认管理器还用于ManyToMany关系和反向ForeignKey关系中。


1

Natan Yellin是正确的,但是您可以更改经理的顺序,第一个将是默认值,然后由管理员使用:

class Conversation(BaseModel):
    ...
    deleted = models.BooleanField(default=False)

    all_conversations = models.Manager() # includes deleted conversations
    objects = NondeletedManager()

管理员执行的get_queryset()使用._default_manager,而不是.objects作为展示未来

qs = self.model._default_manager.get_queryset()

ref Django github BaseModelAdmin实现

这仅确保每次使用YourModel.objects时,都不会包括已删除的对象,但是通用视图和其他视图也都使用._default_manager。然后,如果不重写,则get_queryset不是解决方案。我只是检查了ListView和管理员。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.