Search code examples
pythondjangodjango-tables2

Force F() expression to return a model instance rather than a primary key


enter image description here

In the documentation, is there a way to override this behavior?

i.e. I want car.built_by to return a model instance i.e. <Manufacturer: Toyota> rather than a primary key since I need to maintain the foreign key relationship while annotating.

Update:

I'm currently developing a basketball stats website. I have the following models:

# models.py

class Game(models.Model):
    league = models.ForeignKey('League', ...)
    tournament = models.ForeignKey('Tournament', ...)
    home_team = models.ForeignKey('Team',...)
    away_team = models.ForeignKey('Team',...)
    home_pts = models.IntegerField()
    away_pts = models.IntegerField()

class Team(models.Model):
    team_name = models.CharField()
    team_code = models.CharField()
    team_nickname = models.CharField()

    def __str__(self):
        return f'{self.team_name} {self.team_nickname}'

I'm using django-tables2 to populate the table of a Team's schedule for a particular tournament. So I do the following query:

Game.objects.filter(tournament_id=/insert_tournament_id_here/).filter(Q(home_team_id=self.id) | Q(away_team_id=self.id))

that gives me the Games of the Team. My problem is that when I'm populating the views.py of the team schedule, I want it to be "Opponent" rather than "Home Team" and "Away Team". So I decided to do this:

Team_Game_Queryset.annotate(opponent=Case(When(home_team=/team_id/, then=F('away_team')), default=F('home_team'), output_field=ForeignKey('myapp.Team'))

my problem is that, the annotated column "opponent" isn't a ForeignKey reference for the Team model, so I can't use the str representation of the object. I was able to do this by converting the QuerySet into a pandas DataFrame and then returning a dictionary (which is an easy read into django-tables2). But I always prefer to return a QuerySet to django-tables rather than a dict, and I was wondering if there was any way to overwrite the F() behavior, or if there's any other way to do this using QuerySets.


Solution

  • Since you've said you're using django-tables2, I'm assuming you have a table class

    # tables.py
    import django_tables2 as tables
    
    class Game Table(tables.Table):
        opponent = tables.Column()
    
        class Meta:
            model = Game
    
        def __init__(self, team_id, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
        def render_opponent(self, record):
            return record.home_team if record.away_team_id == self.team_id else record.away_team
    
    # views.py
    def list_team_games(request):
        query_set = Game.objects.filter(tournament_id=/insert_tournament_id_here/).filter(Q(home_team_id=self.id) | Q(away_team_id=self.id))
        table = GameTable(self.id, queryset)
    
        return render(request, 'game_list.html', {
            'table': table
        })
    

    Note that I have added a opponent column and written a render_opponent method on the table. (Refer to the docs)

    I personally have never used django-tables2, but this is what I put together by reading the tutorial in the documentation