ModelChoiceField, How do i add data attribute to the dropdown list of options?

I have a FieldOfficer model, which has region, team_leader and other attributes.

class FieldOfficer(models.Model):
    first_name = models.CharField(max_length=30, blank=False, null=False)
    last_name = models.CharField(max_length=30, blank=False, null=False)
    team_leader = models.ForeignKey('TeamLead', on_delete=models.SET_NULL, null=True, 
                                related_name="agents")
   region = models.CharField(max_length=30, blank=False, null=False)

Additionally, TeamLead and Agent are proxies of the above module.
A TeamLead has many agents, but an agent has one teamlead. An agent should belong to the same region as his TeamLeader.

Upon edit or creation of an agent, i want to enforce the above. ie when i select a teamlead, the agent’s region be the region of the selected team leader. I also want to validate this constraint.

How do i proceed, am using model forms and ModelChoiceField to list all Teamleads available for selection when creating/Updating?

@KenWhitesell already edited, please help

Assuming the “base case” that this is just a two-tier environment, my first reaction is to ask why you’re storing the region at all for the Agent. If this is an absolute constraint (an Agent’s region must always be the same as the TeamLead), then there’s no reason to store it as an attribute of Agent.

Beyond that, I wouldn’t make region a field on the form. I’d set it as a value in the view when it’s being saved. (You can think of it as being analogous to the situation of saving the current user as shown in the example in The save method.) You could also make it more “global” by setting it in the save method for the model.

Side note: Please don’t tag specific individuals requesting assistance. We are all volunteers here contributing information as time, energy, and knowledge allows.

Should region be a table on its own with a one to one relation with the FieldOfficer? also thanks for the reply, the side note is well noted

This depends upon whether or not the only difference in fields between a TeamLead and an Agent is the region. (Well, I guess another difference is that the team_leader field is Null for a TeamLead.)

IF the difference between a TeamLead and Agent are only those two elements, and IF there’s only one TeamLead for a region, then yes, you could simplify this by creating a table for Region having a 1-1 with a TeamLead.

The difference between a teamlead and Agent is just behavioral. I use them as proxy models.

Ken: Building off the title, what if I wanted to add an attribute to model form to do some js-foo on it - like hide or show based on a filter? For example, suppose there’s a four state status on each choice, like “active”, “inactive”, “sleeping”, and “dead”. Following another article, I created a widget that extends widgets.Select. Per the article, you add the custom field to the attrs. The code works just fine for fields that aren’t related to the model, but something wonky happens if I do this for any of the model fields - the attrs is ignored.

Here’s the code that I literally copied from another posting. The print stmts are just to watch what happens.

class CustomSelectWithAttrs(widgets.Select):
    def __init__(self, *args, **kwargs):
        self.extra_data = kwargs.pop("extra_data", [])
        print('in init')
        pprint(self.extra_data)
        super().__init__(*args, **kwargs)

    def create_option(
        self, name, value, label, selected, index, subindex=None, attrs=None
    ):
        option = super().create_option(
            name, value, label, selected, index, subindex=subindex, attrs=attrs
        )
        print(f'before option is {option}')
        if index < len(self.extra_data):
            for key, val in self.extra_data[index].items():
                option["attrs"][key] = val
        print(f'after option is {option}')
        return option

If I set a new field in the form’s init,

self.fields["new_student"] = ChoiceField(choices=field_choices,
                                           widget=CustomSelectWithAttrs(extra_data=field_extra_data),
                                           required=True,
                                           label=f"{matchee.title()} pool",
                                           )

it works.
But if I use the same code for a model field(self.fields["student"] =... where student is a model field), the options only have the value and label, and the additional field is not there. In the field definition, “{matchee}” is the (model) field’s name (e.g. student or tutor in my app).

Same if set only the widget on the field, i.e. self.field["student"].widget = ...

Welcome @m-weintraub !

Please open a new topic for this discussion.

When you do so, please include all the relevant code associated with your example, including the models and templates along with the form.

Also please describe exactly what it is you’re trying to achieve, and what is happening with your attempt. (Don’t just say “it isn’t working” or “it’s not showing ‘x’”.)