2

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.

xtlc
  • 1,070
  • 1
  • 15
  • 41
  • try `queryset.order_by('store__name')` – Sina Farahani Sep 19 '22 at 08:48
  • The keyword `store` cannot be resolved. I know this example is pretty basic, but I intentionally asked for string-sorting, as my logic sometimes changes the valur returned. I updated my example to show that. – xtlc Sep 19 '22 at 09:06
  • 1
    In model that you show `Store` model has no `show_name` field, so this line `f"""{obj.store.show_name}"""` wouldn't work – weAreStarsDust Sep 19 '22 at 09:52
  • 1
    yes, this was a very poor edit of mine. Thanks for pointing that out. – xtlc Sep 19 '22 at 10:02
  • Thanks for edit, does my answer satisfy your question? @xtlc – weAreStarsDust Sep 20 '22 at 08:40
  • It actually does. Can you have a look at my more detailed question here: https://stackoverflow.com/questions/73773465/annotation-in-admin-list-with-many-to-many-relation – xtlc Sep 20 '22 at 09:56
  • 1
    Glad to hear, consider accepting the answer if it was helpful. Okay, I'll take a look. – weAreStarsDust Sep 20 '22 at 10:17

1 Answers1

3

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')
weAreStarsDust
  • 2,634
  • 3
  • 10
  • 22