Simplify family model and add Member.contact_email

- Replace the Guardianship through-model + self-referential M2M with a simple
  is_guardian flag on Member. Guardians in a family look after the family's
  dependents; guardians/dependents are now derived properties.
- Add Member.contact_email: own contact email, falling back to the linked
  user's login email, so a linked member need not store their email twice.
- Update admin (is_guardian in list/filter/inline; drop guardianship inline).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 00:18:35 +02:00
parent 2ead824c5d
commit aa2c6329b9
4 changed files with 70 additions and 101 deletions

View File

@@ -1,8 +1,7 @@
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.test import TestCase
from .models import Family, Guardianship, Member, User
from .models import Family, Member, User
class UserManagerTests(TestCase):
@@ -58,30 +57,53 @@ class MemberTests(TestCase):
with self.assertRaises(ValidationError):
member.full_clean()
def test_contact_email_prefers_own_then_login(self):
# No own email, no user -> empty.
member = Member.objects.create(
first_name="Kid", last_name="Smith", date_of_birth="2015-05-01"
)
self.assertEqual(member.contact_email, "")
class FamilyAndGuardianshipTests(TestCase):
# Linked user, no own email -> falls back to login email.
member.user = User.objects.create_user(email="login@example.com", password="pw")
member.save()
self.assertEqual(member.contact_email, "login@example.com")
# Own contact email wins over login email.
member.email = "contact@example.com"
self.assertEqual(member.contact_email, "contact@example.com")
class FamilyTests(TestCase):
def setUp(self):
self.family = Family.objects.create(name="The Smiths")
self.parent = Member.objects.create(
first_name="Ann", last_name="Smith", date_of_birth="1980-01-01", family=self.family
first_name="Ann", last_name="Smith", date_of_birth="1980-01-01",
family=self.family, is_guardian=True,
)
self.child = Member.objects.create(
first_name="Kid", last_name="Smith", date_of_birth="2015-05-01", family=self.family
first_name="Kid", last_name="Smith", date_of_birth="2015-05-01",
family=self.family, is_guardian=False,
)
def test_family_groups_members(self):
self.assertEqual(self.family.members.count(), 2)
def test_guardianship_relations_resolve(self):
Guardianship.objects.create(guardian=self.parent, child=self.child)
self.assertIn(self.parent, self.child.guardians.all())
self.assertIn(self.child, self.parent.dependents.all())
def test_child_guardians_are_family_guardians(self):
self.assertIn(self.parent, self.child.guardians)
self.assertNotIn(self.child, self.child.guardians)
def test_no_self_guardianship(self):
with transaction.atomic(), self.assertRaises(IntegrityError):
Guardianship.objects.create(guardian=self.parent, child=self.parent)
def test_guardian_dependents_are_family_non_guardians(self):
self.assertIn(self.child, self.parent.dependents)
self.assertNotIn(self.parent, self.parent.dependents)
def test_guardianship_is_unique(self):
Guardianship.objects.create(guardian=self.parent, child=self.child)
with transaction.atomic(), self.assertRaises(IntegrityError):
Guardianship.objects.create(guardian=self.parent, child=self.child)
def test_guardian_has_no_guardians_and_child_has_no_dependents(self):
self.assertEqual(list(self.parent.guardians), [])
self.assertEqual(list(self.child.dependents), [])
def test_member_without_family_has_no_relations(self):
loner = Member.objects.create(
first_name="Solo", last_name="Jones", date_of_birth="1990-01-01"
)
self.assertEqual(list(loner.guardians), [])
self.assertEqual(list(loner.dependents), [])