web-dev-qa-db-fra.com

Remplacer le jeu de requêtes par défaut dans Django admin

Un de mes modèles a un drapeau supprimé, qui est utilisé pour masquer des objets globalement:

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

Comment puis-je remplacer le jeu de requêtes par défaut utilisé par le module d'administration Django pour inclure les conversations supprimées?

56
Natan Yellin

Vous pouvez remplacer la méthodeget_queryset dans votre classe admin de modèle.

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)

Remarque Dans Django <= 1.5, la méthode s'appelait simplement queryset

102
Konrad Hałas

Konrad a raison, mais c’est plus difficile que l’exemple donné dans la documentation.

Les conversations supprimées ne peuvent pas être incluses dans un ensemble de requêtes qui les exclut déjà. Je ne vois donc pas d'autre option que de réimplémenter entièrement 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
7
Natan Yellin

Quel serait si mal avec ce qui suit:

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

Ainsi, dans vos propres applications/projets, vous utilisez Conversation.nondeleted_conversations() et laissez l’application d’administration intégrée faire son travail.

2
Evan Porter

La solution acceptée fonctionne très bien pour moi, mais il me fallait un peu plus de flexibilité. J'ai donc fini par étendre la vue des listes de modifications pour ajouter un paramètre de requête personnalisé. Je peux maintenant configurer mon filtre de requête/filtre par défaut en tant que tel et il peut toujours être modifié en utilisant un filtre différent (obtenir les paramètres):

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)
1
radtek

Vous pouvez le faire avec un modèle Django proxy .

# 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
    ...

Ou, si vous souhaitez réutiliser une classe ModelAdmin existante:

admin.site.register(UnfilteredConversation, ConversationAdmin)

Cette approche évite les problèmes pouvant survenir lors du remplacement du gestionnaire par défaut sur le modèle de conversation d'origine, car le gestionnaire par défaut est également utilisé dans les relations ManyToMany et les relations inversées ForeignKey.

0
zlovelady

Natan Yellin a raison, mais vous pouvez changer l'ordre des gestionnaires et le premier sera celui par défaut, puis il sera utilisé par l'administrateur:

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

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

L'implémentation administrative de get_queryset() utilise ._default_manager à la place de .objects, comme le montre next

qs = self.model._default_manager.get_queryset()

ref implémentation de Django github BaseModelAdmin

Cela garantit uniquement qu'à chaque fois que vous utilisez YourModel.objects, vous n'incluez pas les objets supprimés, mais les vues génériques et autres utilisent également ._default_manager. Ensuite, si vous ne substituez pas get_queryset, ce n'est pas une solution. Je viens de vérifier sur un ListView et admin.

0
AramirezMiori