Hi everyone, I’m experiencing some pretty strange behaviour when trying to render formsets.
I’ve got a bit of Django experience but I always feel like I’m hacking at Formsets. In this case though the code looks alright to me. I’ve included it below and a screenshot that shows the incorrect output.
What is happening, as in the title, each field in the different forms all have the same IDs. If anyone has any insight here I could use an assist. I got someone on a Django Discord to eyeball the code and they agreed it looks okay so I’m kind of stumped.
As an aside, rendering the form as a table or a paragraph doesn’t work either.
I’m using Django 4.1. I thought I’d check here in case I’m doing something exceedingly obvious before I start digging through the form renderer code to see if anything jumps out at me.
Here’s the rendered HTML:
Template:
{% extends "base.html" %}
{% load static %}
{% block body %}
<form method="post">
{% csrf_token %}
{{ form.management_form }}
<p>Please select each player that {{ rule }}</p>
{% for subform in form %}
{{ subform.as_table }}
{% endfor %}
<input type="submit" value="Save">
</form>
{% endblock %}
Relevant View Code:
class PlayerPointsView(FormView):
template_name = 'add_points.html'
success_url = '/success/'
form_class = PlayerPointsFormSet
def get_context_data(self, **kwargs):
context = super(PlayerPointsView, self).get_context_data(**kwargs)
league = League.objects.get(name__icontains=self.kwargs['league_name'])
context['rule'] = LeagueRule.objects.get(
number=self.kwargs['rule_number'],
league=league
)
context['players'] = Player.objects.filter(league=league)
print(context)
return context
def get_form(self):
league = League.objects.get(name__icontains=self.kwargs['league_name'])
rule = LeagueRule.objects.get(league=league,
number=self.kwargs['rule_number'])
round = Round.objects.get(league=league,
number=self.kwargs['round_number'])
kwargs = self.get_form_kwargs()
if self.request.POST:
formset = self.form_class(data=self.request.POST,
rule=rule, form_kwargs=kwargs)
else:
formset = self.form_class(rule=rule, form_kwargs=kwargs)
for form in formset:
if not form.instance.id:
form.fields['round'].initial = round
form.fields['rule'].initial = rule
form.fields['points'].initial = 0
return formset
def get_form_kwargs(self):
league = League.objects.get(name__icontains=self.kwargs['league_name'])
round = Round.objects.filter(number=self.kwargs['round_number'],
league=league)
rule = LeagueRule.objects.get(league=league,
number=self.kwargs['rule_number'])
kwargs = super(PlayerPointsView, self).get_form_kwargs()
kwargs.update({'rule': rule,
'league': league,
'round': round})
return kwargs
Forms
class PlayerPointsForm(ModelForm):
class Meta:
model = PlayerPoints
fields = ['id', 'player', 'round', 'rule', 'points']
widgets = {
'round': HiddenInput(),
'rule': HiddenInput(),
'points': HiddenInput(),
'id': HiddenInput()
}
def __init__(self, *args, **kwargs):
league = kwargs.pop('league')
round = kwargs.pop('round')
rule = kwargs.pop('rule')
players = Player.objects.filter(league=league)
super(PlayerPointsForm, self).__init__(*args, **kwargs)
self.fields['rule'].label = rule.title
self.fields['round'].queryset = round
self.fields['player'].queryset = players
def clean(self):
cleaned_data = super(PlayerPointsForm, self).clean()
round = cleaned_data.get('round')
player = cleaned_data.get('player')
rule = cleaned_data.get('rule')
if round and player and rule:
position = PlayerRoundPosition.objects.get(
player=player, round=round).position
if position == "Keeper":
cleaned_data['points'] = rule.keeper_points
elif position == "Defender":
cleaned_data['points'] = rule.defender_points
elif position == "Midfield":
cleaned_data['points'] = rule.midfield_points
elif position == "Forward":
cleaned_data['points'] = rule.forward_points
return cleaned_data
class BasePlayerPointsFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
rule = kwargs.pop('rule')
super(BasePlayerPointsFormSet, self).__init__(*args, **kwargs)
self.queryset = PlayerPoints.objects.filter(rule=rule)
for form in self.forms:
form.fields['player'].widget.attrs.update(
{'class': 'form-control'})
form.empty_permitted = True
PlayerPointsFormSet = modelformset_factory(
PlayerPoints,
fields=['player', 'round', 'rule', 'points'],
formset=BasePlayerPointsFormSet,
form=PlayerPointsForm,
extra=1
)
Cheers