Search code examples
djangodjango-modelsdjango-querysetdjango-admin-actions

How to set a custom queryset class for Django admin actions?


In an application I'm building, I've created a series of custom model managers and querysets to have a higher level api.

The problem comes when I execute an admin action. The queryset passed to it seems to be a generic one, and I would like to have access to my custom queryset to be able to use the filtering functions I created in it.

This is the action:

def mark_payment_as_sent_action():
    ''' Admin action to mark payment as sent '''

    def mark_payment_as_sent(modeladmin, request, queryset):

        # #####################################################################
        # This is what I currently do
        payments = queryset.filter(status=models.Payment.S_PENDING)

        # This is what I want to do
        payments = queryset.pending()
        # #####################################################################

        # Do stuff with filtered payments

        return HttpResponseRedirect("...")

    mark_payment_as_sent.short_description = "Mark as sent"
    return mark_payment_as_sent

These are the custom model manager an query set:

class PaymentQuerySet(models.query.QuerySet):

    def pending(self):
        return self.filter(status=self.model.S_PENDING)

class PaymentManager(models.Manager):
    use_for_related_fields = True

    def get_query_set(self):
        return PaymentQuerySet(self.model)

    def pending(self, *args, **kwargs):
        return self.get_query_set().pending(*args, **kwargs)

And finally the model and admin classes:

class Payment(models.Model):

    status = models.CharField(
        max_length=25,
        choices=((S_PENDING, 'Pending'), ...)
    )

    objects = managers.PaymentManager()

@admin.register(models.Payment)
class PaymentsAdmin(admin.ModelAdmin):

    actions = (
        admin_actions.mark_payment_as_sent_action(),
    )

Any hint on how can I tell Django to use my queryset when calling an admin action?

Thanks a lot.


Solution

  • As noamk noted, the problem was the method name. Django renamed the get_query_set method to get_queryset.

    Now it's working petectly.

    class PaymentQuerySet(models.query.QuerySet):
    
        def pending(self):
            return self.filter(status=self.model.S_PENDING)
    
    class PaymentManager(models.Manager):
        use_for_related_fields = True
    
        def get_queryset(self):
            return PaymentQuerySet(self.model)
    
        def pending(self):
            return self.get_queryset().pending()