I have some Django models that look something like this (this isn't my exact code but is a simpler example that has the same structure):
class Player(models.Model):
# Some fields here.
pass
class Team(models.Model):
players = models.ManyToManyField(Player, through='TeamPlayer')
class TeamPlayer(models.Model):
team = models.ForeignKey(Team)
player = models.ForeignKey(Player)
some_other_field = models.BooleanField()
I'm using the through mechanism because I have extra columns on my link table.
My admin classes look something like this (note that I am using an inline admin to add the players):
class TeamPlayerInline(admin.TabularInline):
model = TeamPlayer
max_num = 11
extra = 11
class TeamAdmin(admin.ModelAdmin):
inlines = [TeamPlayerInline]
admin.site.register(Team, TeamAdmin)
The Question: My problem is that in my admin I would like to validate that a team has exactly 11 players. Any fewer should result in an error. How can I do this?
These are the things that I have tried and the reasons why they didn't work:
Validate the number of players in the clean method of the Team model. This doesn't work because the players haven't been saved yet, so for a new object there are always zero players.
Validate the number in the clean_players method of a ModelForm used by the TeamAdmin. This method never gets called. Similar methods for other non-ManyToMany fields do get called.
Validate the number in the clean method of the aforementioned ModelForm. This method gets called but the self.cleaned_data dictionary does not have an entry for 'players'.
Any ideas how I can achieve this type of validation? I'm far from being a Django expert so don't assume that I've necessarily done everything that should be obvious.
You need to set the formset on the TeamPlayerInline. And override the clean method in that form set. For example:
from django.forms.models import BaseInlineFormSet
class TeamPlayerFormset(BaseInlineFormSet):
def clean(self):
"""Check that exactly 11 players are entered."""
super(TeamPlayerFormset, self).clean()
if any(self.errors):
return
count = 0
for cleaned_data in self.cleaned_data:
if cleaned_data and not cleaned_data.get('DELETE', False):
count += 1
if count != 11:
raise forms.ValidationError('You must enter 11 team players.')
class TeamPlayerInline(admin.TabularInline):
model = TeamPlayer
max_num = 11
extra = 11
formset = TeamPlayerFormset
class TeamAdmin(admin.ModelAdmin):
inlines = [TeamPlayerInline]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With