Anular el conjunto de consultas predeterminado en el administrador de Django

79

Uno de mis modelos tiene una bandera eliminada, que se usa para ocultar objetos globalmente:

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

¿Cómo puedo anular el conjunto de consultas predeterminado utilizado por el módulo de administración de Django para incluir conversaciones eliminadas?

Natan Yellin
fuente
¿Realmente necesita administradores personalizados para esas consultas simples?
César
3
Sí, los objetos eliminados deben ignorarse universalmente (excepto en las páginas de administración), por lo que tiene sentido establecer un valor predeterminado.
Natan Yellin

Respuestas:

144

Puede anular el get_queryset método en su clase de administrador modelo.

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)

Tenga en cuenta que en Django <= 1.5 el método se nombró simplemente queryset.

Konrad Hałas
fuente
3
¿Cómo funcionaría eso en este caso? ¿Puedo modificar el conjunto de consultas creado por ModelAdmin.querysetpara incluir objetos eliminados? No quiero construir el conjunto de consultas yo mismo en lugar de llamar a la superclase.
Natan Yellin
Mira mi respuesta para ver a qué me refiero. ¿Existe una alternativa para volver a implementar completamente la función?
Natan Yellin
4
Es útil poner la respuesta en tu respuesta, en lugar de solo vincular. Ese enlace está muerto ahora, así que lo actualizaré para dar una explicación.
Dan
16
En Django 1.6, se cambió el nombre de este método a get_queryset.
Fernando Macedo
1
No pude hacer que esto funcionara. Recibo un error que dice que .filter no está en el objeto qs. En segundo lugar, tampoco puedo usar queryset para otra cosa que no sea una solicitud. stackoverflow.com/questions/54472649/… ¿ Alguna ayuda?
Gary
9

Konrad tiene razón, pero esto es más difícil que el ejemplo que se da en la documentación.

Las conversaciones eliminadas no se pueden incluir en un conjunto de consultas que ya las excluye. Así que no veo otra opción que no sea volver a implementar admin.ModelAdmin.queryset por completo.

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
Natan Yellin
fuente
1
No creo que haya nada de malo en eso. Usar dos gerentes es el camino a seguir. Sin embargo, es cierto que el administrador de Django podría proporcionar un gancho para que no tenga que volver a implementar la parte de pedidos.
Thomas Orozco
3

¿Qué estaría tan mal con lo siguiente?

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

Entonces, en sus propias aplicaciones / proyectos, usa Conversation.nondeleted_conversations()y deja que la aplicación de administración incorporada haga su trabajo.

Evan Porter
fuente
1
Estoy ignorando los objetos eliminados en todas partes menos en las páginas de administración, por lo que creo que debería ser el predeterminado. Además, de esta manera no necesito actualizar el código heredado agregando la capacidad de eliminar conversaciones.
Natan Yellin
2

La solución aceptada funciona muy bien para mí, pero necesitaba un poco más de flexibilidad, así que terminé extendiendo la vista de la lista de cambios para agregar un parámetro de conjunto de consultas personalizado. Ahora puedo configurar mi conjunto de consultas / filtro predeterminado como tal y aún se puede modificar usando un filtro diferente (obtener parámetros):

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)
radtek
fuente
2

Puede hacer esto con un modelo de proxy 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
    ...

O, si tiene una clase ModelAdmin existente que desea reutilizar:

admin.site.register(UnfilteredConversation, ConversationAdmin)

Este enfoque evita problemas que pueden surgir al anular el administrador predeterminado en el modelo de conversación original, porque el administrador predeterminado también se usa en relaciones ManyToMany y relaciones ForeignKey inversas.

zlovelady
fuente
1

Natan Yellin tiene razón, pero puede cambiar el orden de los gerentes y el primero será el predeterminado, luego es el que usa el administrador:

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

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

La implementación de administración de get_queryset()uso ._default_manageren su lugar .objects, como se muestra a continuación

qs = self.model._default_manager.get_queryset()

ref Django github Implementación BaseModelAdmin

Esto solo garantiza que cada vez que use YourModel.objects, no incluirá objetos eliminados, sino también las vistas genéricas y otros usos ._default_manager. Entonces, si no anula get_queryset, no es una solución. Acabo de verificar ListView y admin.

AramírezMiori
fuente