Home > Enterprise >  sorting in django admin list by a custom field alphabetically
sorting in django admin list by a custom field alphabetically

Time:09-20

I have a simple store / product relationship and I want to sort the products depending on the store name alphabetically.

models:

class Store(models.Model):
    name = models.CharField("name", max_length = 128)
    show_name = models.CharField("name", max_length = 128, null = True, blank = True)

class Product(models.Model):
    number = models.PositiveIntegerField()
    name = models.CharField("name", max_length = 128)
    store = models.ForeignKey(Store, on_delete = models.CASCADE)

and in the admin:

class ProductAdmin(admin.ModelAdmin):
    list_display = ["number", "name", "get_store_name",]
    
    def get_store_name(self, obj):
        if obj.store.show_name == None:
            return f"""{obj.store.name}"""
        else:
            return f"""{obj.store.show_name}"""

I know I cannot use order_by on custom fields. So I thought I need to override the get_queryset method probably? I found multiple examples to annotate by counted or calculated numbers, but never for strings.

CodePudding user response:

You can annotate field with condition to queryset and then, set it as ordering in @admin.display decorator for your custom field.

from django.db.models import Case, F, When


class ProductAdmin(admin.ModelAdmin):
    list_display = ["number", "name", "get_store_name"]

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        queryset = queryset.annotate(
            _store_name=Case(
                When(store__show_name__isnull=True, then=F('store__name')),
                default=F('store__show_name')),
            ),
        )
        return queryset

    @admin.display(ordering='_store_name')
    def get_store_name(self, obj):
        if obj.store.show_name:
            return obj.store.show_name
        return obj.store.name

With this implementation, standard django admin interface sorting will work for your column

enter image description here

If you want just default sorting, you can add it to queryset before return in get_queryset()

queryset.order_by('_store_name')
  • Related