Change ModelChoiceField select widget index

I am trying to figure out an issue I am having with the select widget. Using the default rendering

    <div class="form-group">
        <h2>Defense</h2>
        {% for defensemove in form %}
            <label for="{{ defensemove.id_for_label }}" class="label">{{ defensemove.html_name }}</label>
            {{ defensemove }}
            <span id="{{ defensivemove.html_name }}HelpBlock" class="form-text text-muted">{{ defensemove.help_text }}</span>
        {% endfor %}
    </div>
    {% endblock %}

It is producing this:

<div class="form-group">
        <h2>Defense</h2>
        
            <label for="id_defense-move1" class="label">defense-move1</label>
            <select name="defense-move1" required id="id_defense-move1">
  <option value="">---------</option>

  <option value="4">High Block</option>

  <option value="5">Medium Block</option>

  <option value="6">Low Block</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">1st move</span>
</div>

The issue is I want to use the value of the move in the model:

Someone in the Python slack told me Django uses the indexof the ModelChoiceField for the option values by default, but I can’t seem to find any documentation. How do I change this?

Can you please clarify what it is you want? Your image is not legible.

What model? You don’t have one posted here.

It would also help if you posted your form.

Form:

from django.forms import ModelForm

from django import forms
from matches.models import MatchIndex, Move, MatchMoves
from characters.models import Character


class MovesForm(forms.Form):

    def __init__(self, move_type, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['move1'].queryset = Move.objects.filter(move_type=move_type)
        self.fields['move2'].queryset = Move.objects.filter(move_type=move_type)
        self.fields['move3'].queryset = Move.objects.filter(move_type=move_type)
        self.fields['move4'].queryset = Move.objects.filter(move_type=move_type)
        self.fields['move5'].queryset = Move.objects.filter(move_type=move_type)
        self.fields['move6'].queryset = Move.objects.filter(move_type=move_type)

    move1 = forms.ModelChoiceField(
        queryset=None,
        help_text="1st move")
    move2 = forms.ModelChoiceField(
        queryset=None,
        help_text="2nd move")
    move3 = forms.ModelChoiceField(
        queryset=None,
        help_text="3rd move")
    move4 = forms.ModelChoiceField(
        queryset=None,
        help_text="4th move")
    move5 = forms.ModelChoiceField(
        queryset=None,
        help_text="5th move")
    move6 = forms.ModelChoiceField(
        queryset=None,
        help_text="6th move")

    class Meta:
        model = MatchMoves
        fields = ['move1', 'move2', 'move3', 'move4', 'move5', 'move6']


class CreateMatchForm(ModelForm):

    opponent = forms.CharField(
        min_length=4,
        max_length=32,
        help_text="The opponent you want to challenge")

    def clean_opponent(self):
        opponent = self.cleaned_data['opponent']
        try:
            opponent_character = Character.objects.get(name=str.lower(opponent))
        except Character.DoesNotExist:
            raise forms.ValidationError("This opponent does not exist")
        return opponent_character

    class Meta:
        model = MatchIndex
        fields = ['opponent']

Model:

from django.db import models
from characters.models import Character
from django.core.validators import MaxValueValidator, MinValueValidator


# Create your models here.
class MatchIndex(models.Model):
    Open = 0
    Completed = 1
    Cancelled = 2
    Error = 3
    MATCH_STATUS = (
        (0, "Open"),
        (1, "Completed"),
        (2, "Cancelled"),
        (3, "Error"),
    )
    challenger = models.ForeignKey(Character,
                                   help_text="The match's challenger",
                                   related_name="MatchIndex_challenger",
                                   on_delete=models.CASCADE)
    opponent = models.ForeignKey(Character,
                                 help_text="The match's opponent",
                                 related_name="MatchIndex_opponent",
                                 on_delete=models.CASCADE)
    status = models.PositiveSmallIntegerField(
        choices=MATCH_STATUS,
        default=0,
        help_text="The match status")

    class Meta:
        verbose_name_plural = "Match Indexes"


class Move(models.Model):
    Offensive = 0
    Defensive = 1
    MOVE_TYPE = (
        (0, "Offensive"),
        (1, "Defensive"),
    )
    name = models.CharField(
        max_length=16,
        help_text="The move's name")
    value = models.PositiveSmallIntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(99)],
        help_text="Value used to map moves and order by on form")
    move_type = models.PositiveSmallIntegerField(
        choices=MOVE_TYPE,
        default=0,
        help_text="The move type offensive or defensive"
    )

    def __str__(self):
        return self.name


class MatchMoves(models.Model):
    Challenger = 0
    Opponent = 1
    MOVE_CONTROLLER = (
        (0, "Challenger"),
        (1, "Opponent"),
    )
    match = models.ForeignKey(MatchIndex,
                              help_text="match id",
                              on_delete=models.CASCADE)
    move = models.ForeignKey(Move,
                             help_text="move used",
                             on_delete=models.CASCADE)
    player = models.PositiveSmallIntegerField(
        choices=MOVE_CONTROLLER,
        default=0,
        help_text="Which player performed the move")
    order = models.PositiveSmallIntegerField(
                                             help_text="move order")

    class Meta:
        verbose_name_plural = "Match Moves"

The

illegiable

image:

Offense Form:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    
    <link rel="stylesheet" href="/static/css/fontawesome-all.min.css">
    <link rel="stylesheet" href="/static/css/bootswatch-superhero.min.css">
    <title>Choose your offense</title>
</head>
<body>
    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
  <a class="navbar-brand" href="#">Menu</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
    <span class="navbar-toggler-icon"></span>
  </button>
    <div class="collapse navbar-collapse" id="collapsibleNavbar">
    <ul class="navbar-nav">
        
        <li class="nav-item">
        <a class="nav-link" href="/accounts/logout/">Log off</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Profile
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdown">
          <a class="dropdown-item" href="/accounts/profile/">My Profile</a>
          <a class="dropdown-item" href="/accounts/password_change/">Change Password</a>
          <a class="dropdown-item" href="/accounts/profile/update_notifications">Update Notification Preferences</a>
        </div>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Characters
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdown">
          <a class="dropdown-item" href="/characters/">My Characters</a>
          <a class="dropdown-item" href="/characters/create/">Create Character</a>
        </div>
      </li>
      
      <li class="nav-item">
        <a class="nav-link" href="/admin/">Admin</a>
      </li>
      
        
    </ul>
  </div>
</nav>
    <div class="container">
        <div class="row mt-5">
  <div class="col-md-6 m-auto">
    <div class="card card-body">
      <h1 class="text-center mb-3">
          <i class="fas fa-fist-raised" aria-hidden="true"></i>  Choose your offense
      </h1>
    <form method="post">
    <input type="hidden" name="csrfmiddlewaretoken" value="U0eOmi1ILC2lkvO2mlU2W1Kn621aX5WOjMjCKTaCtFeIYJauBX9YpJCnM7OV28gP">
    
    <div class="form-group">
        <h2>Offense</h2>
        
            <label for="id_offense-move1" class="label">offense-move1</label>
            <select name="offense-move1" required id="id_offense-move1">
  <option value="">---------</option>

  <option value="1">High Punch</option>

  <option value="2" selected>Medium Punch</option>

  <option value="3">Low Punch</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">1st move</span>
        
            <label for="id_offense-move2" class="label">offense-move2</label>
            <select name="offense-move2" required id="id_offense-move2">
  <option value="">---------</option>

  <option value="1" selected>High Punch</option>

  <option value="2">Medium Punch</option>

  <option value="3">Low Punch</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">2nd move</span>
        
            <label for="id_offense-move3" class="label">offense-move3</label>
            <select name="offense-move3" required id="id_offense-move3">
  <option value="">---------</option>

  <option value="1">High Punch</option>

  <option value="2">Medium Punch</option>

  <option value="3" selected>Low Punch</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">3rd move</span>
        
            <label for="id_offense-move4" class="label">offense-move4</label>
            <select name="offense-move4" required id="id_offense-move4">
  <option value="">---------</option>

  <option value="1">High Punch</option>

  <option value="2" selected>Medium Punch</option>

  <option value="3">Low Punch</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">4th move</span>
        
            <label for="id_offense-move5" class="label">offense-move5</label>
            <select name="offense-move5" required id="id_offense-move5">
  <option value="">---------</option>

  <option value="1" selected>High Punch</option>

  <option value="2">Medium Punch</option>

  <option value="3">Low Punch</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">5th move</span>
        
            <label for="id_offense-move6" class="label">offense-move6</label>
            <select name="offense-move6" required id="id_offense-move6">
  <option value="">---------</option>

  <option value="1">High Punch</option>

  <option value="2">Medium Punch</option>

  <option value="3" selected>Low Punch</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">6th move</span>
        
    </div>
    <a class="btn btn-link" href="/matchbrowser/create/" role="button">Back</a>
    <button type="submit" class="btn btn-primary">Continue</button>
    </form>
    </div>
  </div>
</div>
    </div>
    <footer>Powered by Django</footer>
    <script type="text/javascript" src="/static/js/jquery-3.4.1.slim.min.js"></script>
    <script type="text/javascript" src="/static/js/popper.min.js"></script>
    <script type="text/javascript" src="/static/js/bootstrap.min.js"></script>
</body>
</html>

Defense Form:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    
    <link rel="stylesheet" href="/static/css/fontawesome-all.min.css">
    <link rel="stylesheet" href="/static/css/bootswatch-superhero.min.css">
    <title>Choose your defense</title>
</head>
<body>
    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
  <a class="navbar-brand" href="#">Menu</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
    <span class="navbar-toggler-icon"></span>
  </button>
    <div class="collapse navbar-collapse" id="collapsibleNavbar">
    <ul class="navbar-nav">
        
        <li class="nav-item">
        <a class="nav-link" href="/accounts/logout/">Log off</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Profile
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdown">
          <a class="dropdown-item" href="/accounts/profile/">My Profile</a>
          <a class="dropdown-item" href="/accounts/password_change/">Change Password</a>
          <a class="dropdown-item" href="/accounts/profile/update_notifications">Update Notification Preferences</a>
        </div>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Characters
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdown">
          <a class="dropdown-item" href="/characters/">My Characters</a>
          <a class="dropdown-item" href="/characters/create/">Create Character</a>
        </div>
      </li>
      
      <li class="nav-item">
        <a class="nav-link" href="/admin/">Admin</a>
      </li>
      
        
    </ul>
  </div>
</nav>
    <div class="container">
        <div class="row mt-5">
  <div class="col-md-6 m-auto">
    <div class="card card-body">
      <h1 class="text-center mb-3">
          <i class="fas fa-shield-alt" aria-hidden="true"></i>  Choose your defense
      </h1>
    <form method="post">
    <input type="hidden" name="csrfmiddlewaretoken" value="tARQ0ZZXKGWjkhOguWaaLncCfupZTaKNSmWEoA8RsJ8GYvaIJyp6e54CVzcKYd4O">
    
    <div class="form-group">
        <h2>Defense</h2>
        
            <label for="id_defense-move1" class="label">defense-move1</label>
            <select name="defense-move1" required id="id_defense-move1">
  <option value="">---------</option>

  <option value="4">High Block</option>

  <option value="5">Medium Block</option>

  <option value="6">Low Block</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">1st move</span>
        
            <label for="id_defense-move2" class="label">defense-move2</label>
            <select name="defense-move2" required id="id_defense-move2">
  <option value="">---------</option>

  <option value="4">High Block</option>

  <option value="5">Medium Block</option>

  <option value="6">Low Block</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">2nd move</span>
        
            <label for="id_defense-move3" class="label">defense-move3</label>
            <select name="defense-move3" required id="id_defense-move3">
  <option value="">---------</option>

  <option value="4">High Block</option>

  <option value="5">Medium Block</option>

  <option value="6">Low Block</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">3rd move</span>
        
            <label for="id_defense-move4" class="label">defense-move4</label>
            <select name="defense-move4" required id="id_defense-move4">
  <option value="">---------</option>

  <option value="4">High Block</option>

  <option value="5">Medium Block</option>

  <option value="6">Low Block</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">4th move</span>
        
            <label for="id_defense-move5" class="label">defense-move5</label>
            <select name="defense-move5" required id="id_defense-move5">
  <option value="">---------</option>

  <option value="4">High Block</option>

  <option value="5">Medium Block</option>

  <option value="6">Low Block</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">5th move</span>
        
            <label for="id_defense-move6" class="label">defense-move6</label>
            <select name="defense-move6" required id="id_defense-move6">
  <option value="">---------</option>

  <option value="4">High Block</option>

  <option value="5">Medium Block</option>

  <option value="6">Low Block</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">6th move</span>
        
    </div>
    <a class="btn btn-link" href="/matchbrowser/offense/" role="button">Back</a>
    <button type="submit" class="btn btn-primary">Issue Challenge!</button>
    </form>
    </div>
  </div>
</div>
    </div>
    <footer>Powered by Django</footer>
    <script type="text/javascript" src="/static/js/jquery-3.4.1.slim.min.js"></script>
    <script type="text/javascript" src="/static/js/popper.min.js"></script>
    <script type="text/javascript" src="/static/js/bootstrap.min.js"></script>
</body>
</html>

Note this:

<label for="id_offense-move1" class="label">offense-move1</label>
            <select name="offense-move1" required id="id_offense-move1">
  <option value="">---------</option>

  <option value="1">High Punch</option>

  <option value="2" selected>Medium Punch</option>

  <option value="3">Low Punch</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">1st move</span>

compare to:

<label for="id_defense-move1" class="label">defense-move1</label>
            <select name="defense-move1" required id="id_defense-move1">
  <option value="">---------</option>

  <option value="4">High Block</option>

  <option value="5">Medium Block</option>

  <option value="6">Low Block</option>

</select>
            <span id="HelpBlock" class="form-text text-muted">1st move</span>

and compare it to the database values in the screenshot.

To confirm, you’re saying that you’re getting a set of options that look like this:

When what you really want is this:

<label for="id_defense-move1" class="label">defense-move1</label>
            <select name="defense-move1" required id="id_defense-move1">
  <option value="">---------</option>
  <option value="1">High Block</option>
  <option value="2">Medium Block</option>
  <option value="3">Low Block</option>
</select>

Is that correct?

And that these option values are to come from the value field in the Move model?

What you’re asking for doesn’t seem to make sense to me in the context of this data.

The move field in the MatchMoves object is a ForeignKey reference to Move object.
If you return a “2” for Medium Block, then the MatchMoves object is going to refer to the Medium Punch object.

Generally speaking, you want the ForeignKey field to refer to the ID of the referenced object. You then access the desired fields from that object.

The idea here is that the moves are stored as 1 for high, 2 for medium, and 3 for low. This is how the original defunct game worked: BattleMail Cheat/Crack & BattleMail Kung Fu - YouTube (this is the v2 version, the original used cartoon graphics).

Sorry, but am I correct in understanding that this is a database relation error on my part? On my previous attempt, the moves were stored in a single field.

312223232121

Where each number was split off and translated to a single move. I remember someone in the python slack telling me that also made no sense from a database relation standpoint.

But does it make a difference as to whether it’s a punch or block?

What you want to avoid is confusing an “internal value”, some data element used within the processing itself such as a “primary or foreign key”, with “external value” - some externally visible attribute of the data being displayed or otherwise processed.

Your table keys should have no intrinsic meaning. Whether the primary key of a particular row within a table is 1, 42, or 321, shouldn’t make a difference at all. It’s just an arbitrary value that exists allowing you to uniquely identify and refer to that row.

So the mistake you’re making is in expressing any concern or interest in what that value is in the select field. What you’re really interested in - at whatever point in the process where it becomes necessary, is the value column from the row identified by that key.

Yes but, the move type field that gives it that distinction.

So how would you go about rewriting the code? Would you remove the value column and use the primary key of the move instead?

No, it’s the exact opposite of that. You keep your code and tables just how they are.

When you need the value for a move, then you refer to the value column of the row identified by the foreign key.

The point here is that you should not care about what that key is - you only care about the value column.

Actually:

move = models.ForeignKey(Move,
                             help_text="move used",
                             on_delete=models.CASCADE)

Using the forign key seems like the way to go:

for moves in request.session:
    matchmove = MatchMoves(matchindex.id, move, 0, ???);

Or am i completely off? I am hoping to unite the values in the database and the form so I didn’t have to do so much work in querying the database per move.

I’m sorry, you’ve lost me here.

The original topic was asking about using something other than the primary key of a related table as the option value in a select widget.

My previous responses were all addressing the reasons why doing that is both undesirable and unnecessary.

This appears to be something completely different.

What are you asking about / looking for here?

As a general principle, this isn’t something you should be worrying about - at least not at this point in time.
But since I’m not understanding what you’re trying to accomplish with this - how this fits into your overall objective, I really can’t answer you.

Actually I’m sorry it’s my fault I’m new to Python/Django. I think my issue is that I have an idea of what I want, but it might not be the best idea in principle and or know how to go about it in principle. So basically we are both lost in thought. :crazy_face: The reason I stated what I stated above is because, I realized the model uses foreign keys for matching the moves. The idea was originally, as I stated, each move is an integer and a type which distinguishes it from the others. Since Django is forcing me to use foreign keys in the form with no way to override it, I figured to go with the flow and not against it.