diff --git a/accounts/__pycache__/forms.cpython-313.pyc b/accounts/__pycache__/forms.cpython-313.pyc index 36bb4d7..0165fa4 100644 Binary files a/accounts/__pycache__/forms.cpython-313.pyc and b/accounts/__pycache__/forms.cpython-313.pyc differ diff --git a/accounts/__pycache__/models.cpython-313.pyc b/accounts/__pycache__/models.cpython-313.pyc index 22c5d9f..e5d7513 100644 Binary files a/accounts/__pycache__/models.cpython-313.pyc and b/accounts/__pycache__/models.cpython-313.pyc differ diff --git a/accounts/__pycache__/urls.cpython-313.pyc b/accounts/__pycache__/urls.cpython-313.pyc index be27102..f018a9f 100644 Binary files a/accounts/__pycache__/urls.cpython-313.pyc and b/accounts/__pycache__/urls.cpython-313.pyc differ diff --git a/accounts/__pycache__/views.cpython-313.pyc b/accounts/__pycache__/views.cpython-313.pyc index ee2fd6a..bb6cd9e 100644 Binary files a/accounts/__pycache__/views.cpython-313.pyc and b/accounts/__pycache__/views.cpython-313.pyc differ diff --git a/accounts/forms.py b/accounts/forms.py index 8e02c24..48d7111 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -28,8 +28,10 @@ class CustomUserChangeForm(forms.ModelForm): fields = ('username', 'first_name', 'last_name', 'email', 'birth_date', 'player_number', 'team') class PlayerCreationForm(forms.ModelForm): - parent1_email = forms.EmailField(required=False) - parent2_email = forms.EmailField(required=False) + parent1_search = forms.CharField(label="Search Parent 1 by Username/Email", required=False) + parent1_new = forms.EmailField(label="Or create new Parent 1 with Email", required=False) + parent2_search = forms.CharField(label="Search Parent 2 by Username/Email", required=False) + parent2_new = forms.EmailField(label="Or create new Parent 2 with Email", required=False) birth_date = forms.DateField(input_formats=['%d.%m.%Y', '%Y-%m-%d'], widget=forms.DateInput(format='%d.%m.%Y', attrs={'type': 'date'})) class Meta: diff --git a/accounts/migrations/0004_customuser_parents.py b/accounts/migrations/0004_customuser_parents.py new file mode 100644 index 0000000..f31f6c0 --- /dev/null +++ b/accounts/migrations/0004_customuser_parents.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.6 on 2025-10-01 10:00 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0003_absenceperiod'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='parents', + field=models.ManyToManyField(blank=True, related_name='children', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/accounts/migrations/__pycache__/0004_customuser_parents.cpython-313.pyc b/accounts/migrations/__pycache__/0004_customuser_parents.cpython-313.pyc new file mode 100644 index 0000000..66a738d Binary files /dev/null and b/accounts/migrations/__pycache__/0004_customuser_parents.cpython-313.pyc differ diff --git a/accounts/models.py b/accounts/models.py index fad17c1..1d12871 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -7,6 +7,7 @@ class CustomUser(AbstractUser): birth_date = models.DateField(null=True, blank=True) player_number = models.IntegerField(default=999) team = models.ForeignKey('clubs.Team', on_delete=models.SET_NULL, null=True, blank=True, related_name='players') + parents = models.ManyToManyField('self', symmetrical=False, blank=True, related_name='children') @property def age(self): diff --git a/accounts/templates/accounts/player_form.html b/accounts/templates/accounts/player_form.html index c6fa7b1..a014ced 100644 --- a/accounts/templates/accounts/player_form.html +++ b/accounts/templates/accounts/player_form.html @@ -17,4 +17,39 @@ -{% endblock %} \ No newline at end of file + + +{% endblock %} diff --git a/accounts/urls.py b/accounts/urls.py index 2d0f7c6..f1dcba7 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -9,4 +9,5 @@ urlpatterns = [ path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('profile/', views.edit_profile, name='edit_profile'), path('player/add/', views.PlayerCreateView.as_view(), name='player-add'), + path('user-search/', views.user_search, name='user-search'), ] \ No newline at end of file diff --git a/accounts/views.py b/accounts/views.py index d407eaf..b886f48 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -4,6 +4,8 @@ from django.views.generic.edit import CreateView from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.contrib.auth import views as auth_views +from django.db.models import Q +from django.http import JsonResponse from .forms import InvitationCodeForm, CustomUserCreationForm, CustomUserChangeForm, PlayerCreationForm from .models import CustomUser, InvitationCode import uuid @@ -78,14 +80,38 @@ class PlayerCreateView(LoginRequiredMixin, HeadCoachCheckMixin, CreateView): # Create invitation code for player InvitationCode.objects.create(code=str(uuid.uuid4()), user=player) - # Create parent users and invitation codes - for email_field in ['parent1_email', 'parent2_email']: - email = form.cleaned_data.get(email_field) - if email: - parent_user = CustomUser.objects.filter(email=email).first() - if not parent_user: - parent_user = CustomUser.objects.create(email=email, username=email, is_active=False) + # Handle parents + for i in ['1', '2']: + search_identifier = form.cleaned_data.get(f'parent{i}_search') + new_email = form.cleaned_data.get(f'parent{i}_new') + + if search_identifier: + import re + match = re.search(r'\((\w+)\)', search_identifier) + if match: + username = match.group(1) + try: + parent_user = CustomUser.objects.get(username=username) + player.parents.add(parent_user) + except CustomUser.DoesNotExist: + form.add_error(f'parent{i}_search', 'User not found.') + else: + # if no user is selected from the list, maybe the user typed an email/username directly + try: + parent_user = CustomUser.objects.get(Q(username=search_identifier) | Q(email=search_identifier)) + player.parents.add(parent_user) + except CustomUser.DoesNotExist: + form.add_error(f'parent{i}_search', 'User not found.') + except CustomUser.MultipleObjectsReturned: + form.add_error(f'parent{i}_search', 'Multiple users found. Please be more specific.') + + elif new_email: + parent_user, created = CustomUser.objects.get_or_create(email=new_email, defaults={'username': new_email, 'is_active': False}) InvitationCode.objects.create(code=str(uuid.uuid4()), user=parent_user) + player.parents.add(parent_user) + + if form.errors: + return self.form_invalid(form) return redirect(self.success_url) @@ -93,4 +119,12 @@ class MyLoginView(auth_views.LoginView): def get(self, request, *args, **kwargs): if request.user.is_authenticated: return redirect('dashboard') - return super().get(request, *args, **kwargs) \ No newline at end of file + return super().get(request, *args, **kwargs) + +def user_search(request): + q = request.GET.get('q', '') + users = CustomUser.objects.filter(last_name__istartswith=q).values('username', 'first_name', 'last_name') + results = [] + for user in users: + results.append(f"{user['last_name']}, {user['first_name']} ({user['username']})") + return JsonResponse(results, safe=False) \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 7fee6b0..dcd41b2 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ