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
If you want just default sorting, you can add it to queryset before return in get_queryset()
queryset.order_by('_store_name')