Home > Mobile >  Django : Reduce number of possible choices based on previous model choice inputs
Django : Reduce number of possible choices based on previous model choice inputs

Time:01-09

I have multiple models :

class Game(models.Model):
    name = models.CharField('Nom du jeu', max_length=100)
    logo = models.ImageField('Logo', blank=True, upload_to='game_logos')

class League(models.Model):
    game = models.ForeignKey(Game, verbose_name='Jeu', null=True, on_delete=models.SET_NULL)
    league = models.CharField('Ligue', max_length=20)
    hexcolor = models.CharField('Code couleur Hex', max_length=7, blank=True, default='#000000')

class Event(models.Model):
    game = models.ForeignKey(Game, verbose_name='Nom du jeu', null=True, on_delete=models.SET_NULL)
    event_type = models.CharField(verbose_name="Type d'événement", max_length=50, choices=type_course)
    league = models.ForeignKey(League, verbose_name='Ligue', null=True, on_delete=models.SET_NULL)
    circuit = models.ForeignKey(Circuit, verbose_name='Circuit', null=True, on_delete=models.SET_NULL)
    event_date = models.DateTimeField(verbose_name="Date de la course")

The logic here is, that we have races, that take place place in a certain game, and in a certain league. The thing is, I can have different games that have their own league, independent from the league of the other games (Formula 1 will have League A, B and C, and another game will have League 1, 2 and 3).

So when I set up a new event, as soon as I choose the game, I want to get the leagues associated with this game, not all of them. How can I filter the choices of the leagues based on the game input ?

CodePudding user response:

Option 1

The user choose the game and then gets redirected to a new page where he can select the league ant the event.

def select_league_and_event(request, game_pk):
    # game_pk = primary key of the selected game passed via url param
    game = Game.objects.get(pk=game_pk)
    leagues = game.league_set.all()  # you can filter them also
    events = game.event_set.all()

    context = {
        "leagues": leagues,
        "events": events,
    }
    return render(request, 'YOUR_HTML.html', context)

You can give the ForeignKey a related_name instead of using e.g. league_set you can name it game_leagues. So calling leagues = game.game_leagues.all() will give you the same results.

Option 2

If the user selects the game, league and event in a single page you must a) pass alle leagues and events to the view and use javascript to filter the leagues and events depending on the selected game or b) you can use django rest framework to fetch all the leagues and events from the REST API after a game is selected.

Option a) is quite simple and required less code but if you have a lot of leagues and events it's performance might lack. Then option b) is preferred.

CodePudding user response:

So.. that depends if you are using Django Templates or if you're using the Django Rest Framework with an independent frontend. I'm going to assume that you are using Django Templates.

Intro

So, your Django app runs on the server, which means that none of your python code is executed in the browser. This means that whenever you want to display new data to the user, the browser has to send a request to the server, the server fetches the data from the db and sends it back to the user's browser. This requires refreshing the page, and we know that when you refresh the page any state (i.e. input fields values) is lost. So how to circumvent this problem?

URL Params

When you need to refresh the page while persisting state, the easiest thing to do is to store the information in your URL. Unless the information is reserved obviously, because everyone can see the URL.

Here below i'll type you an example of a form with two select inputs, with the second one depending on the first one.

<form method="get">
    <select name="game" id="id_game" onChange="this.form.submit()">
        <option>Select a game</option>
        {% for game in games %}
            <option value="{{ game.id }}" {% if game.id == selected_game_id %} selected {% endif %}>{{ game.name|title }}</option>
        {% endfor %}
    </select>
    <select name="league" id="id_league">
        <option>Select a league</option>
        {% for league in game_specific_leagues %}
            <option value="{{ league.id }}" {% if league.id == selected_league_id %} selected {% endif %}>{{ league.name|title }}</option>
        {% endfor %}
    </select>
</form>

In order for this to work, you have to capture the URL params in the backend and pass them to the frontend via the context variable.

def get(self, request):
    game = request.GET.get('name', None)
    league = request.GET.get('league', None)

    context = {
        'games': Game.objects.all(),
        'game_specific_leagues': Game.league_set.all(),
        'selected_game_id': game,
        'selected_league_id': league
    }

    return render(request, 'your_template.html', context)
  • Related