Add Team/TeamRole/TeamMembership/TeamPicture models and fix two test bugs
- Fix Member.create(): post_save signal creates Member immediately on User creation,
so new users always hit the hasattr branch; check `created` flag to set initial
password and notes for genuinely new users
- Fix Season.for_date(): replace get() with filter().order_by("-start_date").first()
to handle overlapping seasons gracefully and raise DoesNotExist when none match
- Add TeamRole, Team, TeamMembership, TeamPicture models with rules permissions
- Add migration 0002 for new models
- Add test suites for members and teams covering all new model behaviour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# Generated by Django 6.0.3 on 2026-06-01 20:21
|
||||
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import django_extensions.db.fields
|
||||
import rules.contrib.models
|
||||
import teams.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0006_member_notes'),
|
||||
('teams', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Team',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, unique=True, verbose_name='name')),
|
||||
('short_name', models.CharField(blank=True, help_text='An optional short name for the team', max_length=255, null=True, unique=True, verbose_name='short name')),
|
||||
('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, max_length=255, populate_from='name', unique=True, verbose_name='slug')),
|
||||
('logo', models.ImageField(blank=True, null=True, upload_to='teams/logo/', verbose_name='logo')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'team',
|
||||
'verbose_name_plural': 'teams',
|
||||
'ordering': ['name'],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TeamRole',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, unique=True, verbose_name='name')),
|
||||
('abbreviation', models.CharField(help_text='An abbreviated version of the role name', max_length=255, unique=True, verbose_name='abbreviation')),
|
||||
('staff_role', models.BooleanField(default=False, help_text='A staff role is any supporting function for a given team (e.g., coach, team manager, ...)', verbose_name='staff role')),
|
||||
('admin_role', models.BooleanField(default=False, help_text='An admin role is a role that has administrative privileges and can change team information and settings', verbose_name='admin role')),
|
||||
('sort_order', models.PositiveIntegerField(default=10, help_text='The order in which the role should be displayed (low to high)', verbose_name='sort order')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'team role',
|
||||
'verbose_name_plural': 'team roles',
|
||||
'ordering': ['sort_order'],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TeamMembership',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('number', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(99)], verbose_name='number')),
|
||||
('captain', models.BooleanField(default=False, verbose_name='captain')),
|
||||
('alternate_captain', models.BooleanField(default=False, verbose_name='alternate captain')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('modified', models.DateTimeField(auto_now=True)),
|
||||
('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_memberships', to='members.member', verbose_name='member')),
|
||||
('season', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_memberships', to='teams.season', verbose_name='season')),
|
||||
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teams.team', verbose_name='team')),
|
||||
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_memberships', to='teams.teamrole', verbose_name='role')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'team membership',
|
||||
'verbose_name_plural': 'team memberships',
|
||||
'ordering': ['team__name', 'role__sort_order', 'number', 'member__user__last_name', 'member__user__first_name'],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='team',
|
||||
name='members',
|
||||
field=models.ManyToManyField(through='teams.TeamMembership', to='members.member', verbose_name='members'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TeamPicture',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('picture', models.ImageField(upload_to=teams.models.team_season_file_path, verbose_name='picture')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('modified', models.DateTimeField(auto_now=True)),
|
||||
('season', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teams.season', verbose_name='season')),
|
||||
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teams.team', verbose_name='team')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'team picture',
|
||||
'verbose_name_plural': 'team pictures',
|
||||
'ordering': ['team__name', 'season__start_date'],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='teammembership',
|
||||
constraint=models.UniqueConstraint(fields=('team', 'season', 'number'), name='unique_team_membership_number', violation_error_message='A team can only have one member for a given season and number.'),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user