2

How can sort on a non-existent field in django admin. Basically I have server and app model with a third relationship model linking them. I saw your responses but not sure where to place the code you have mentioned. Here are the model and admin.py

# Model.py File
class Server(models.Model):
name = models.CharField(max_length=100,  unique=True)
operating_system = models.CharField(max_length=20, choices=constants.OPERATING_SYSTEMS.items())

@property
def number_of_apps(self):
    return ServerApplicationRelationship.objects.filter(server=self).count()

class Application(models.Model):
    name = models.CharField(max_length=100,  unique=True)
hosted_on = models.ManyToManyField(Server, through='ServerApplicationRelationship', blank=True,)

@property
def number_of_servers(self):
    return ServerApplicationRelationship.objects.filter(app=self).count()
# number_of_servers.server_field = 'server__count'

class ServerApplicationRelationship(models.Model):
    server = models.ForeignKey(Server, blank=True, )
    # server_tag = models.ForeignKey(Server, through_fields= 'tags')
    app = models.ForeignKey(Application, blank=True)

# Admin.py file
@admin.register(Application)
class ApplicationAdmin(admin.ModelAdmin):


    inlines = [ApplicationInLine]
    list_display = ['name', 'number_of_servers']
    list_display_links = ['name', 'number_of_servers']

    ordering = ('number_of_servers', 'name')

@property
def number_of_apps(self):
    queryset = ServerAppRelation.objects.filter(server=self).count()
    return queryset

If I include the number_of_servers in the ordering. I get an error

    ERRORS:
<class 'S5.admin.ApplicationAdmin'>: (admin.E033) The value of 'ordering[0]' refers to 'number_of_server', which is not an attribute of 'S5.Appl
ication'

number_of_server is displayed in as column in the table but is not sortable. How can I make it sortable?

Many thanks

Stryker
  • 5,732
  • 1
  • 57
  • 70

1 Answers1

8

First, you need to override the get_queryset method, and annotate it with the number of servers.

Unfortunately, you can't include the annotated field in the list_display, because it fails the Django system check. So you define a method that returns the annotated field, and include that method in list_display.

Finally, to make the column orderable, you need to set the admin_order_field attribute on the method.

@admin.register(Application)
class ApplicationAdmin(admin.ModelAdmin):
    list_display = ['name', '_num_servers']

    def _num_servers(self, obj):
        return obj.num_servers
    _num_servers.admin_order_field = 'num_servers'

    def get_queryset(self, request):
        return super(AppAdmin, self).get_queryset(
            request,
        ).annotate(num_servers=Count('servers'))
Alasdair
  • 298,606
  • 55
  • 578
  • 516