Retrieve field values to Django Admin using many to many relationships

Viewed 86

I have a simple task: I need to expose player name related to Game in game list (Django Admin). Game object has ManyToMany relationship with Player object via 'players' attribute. The problem is that now I have empty 'players_list' field (empty list means I don't know what to do and just leave it here[enter image description here][1]), though I tried Player.objects.all() and obviously got all players even those who are not bound to particular game. I feel it has simple solution but my brain refuses to work after 55 opened tabs. Thanks in advance! This is my models.py

from django.db import model 
class Player(models.Model):
    name = models.CharField(max_length=54, default="")
    email = models.EmailField(max_length=54)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    def __str__(self):
        return self.name

class Game(models.Model):
    name = models.CharField(max_length=254, default="")
    players = models.ManyToManyField(Player, blank=True, related_name='player_games')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    players_list = []
    def __str__(self):
        return self.name

and admin.py

from django.contrib import admin
from .models import Game, Player


class PlayerInline(admin.TabularInline):
    model = Game.players.through

@admin.register(Player)
class Admin(admin.ModelAdmin):

    search_fields = ['name', 'email']
    list_display = ('name', 'email', 'created_at', 'updated_at')
    inlines = [
    PlayerInline,
]

@admin.register(Game)
class AdminAdmin(admin.ModelAdmin):
    list_display = ('name',  'created_at', 'updated_at', 'players_list')
    inlines = [
    PlayerInline,
]
    exclude = ('players',)

Pic as it looks now [1]: https://i.stack.imgur.com/KVJ5y.png

1 Answers

The best approach here will be creating custom method in your model instead of single variable. You will not even have to change your list_display:

class Game(models.Model):
    ...
    def players_list(self):
        return self.players.all()

Alternatively, if you want only names of players or anything else from, you can change it (or add another method) to something like that:

class Game(models.Model):
    ...
    def players_list(self):
        return [player for player in self.players.all()]
        # or
        return [player.name for player in self.players.all()]

This is called list comprehension, if you are not familiar with it.

Related