diff --git a/accounts/__pycache__/admin.cpython-313.pyc b/accounts/__pycache__/admin.cpython-313.pyc index 3699747..2cf9cb3 100644 Binary files a/accounts/__pycache__/admin.cpython-313.pyc and b/accounts/__pycache__/admin.cpython-313.pyc differ diff --git a/accounts/__pycache__/apps.cpython-313.pyc b/accounts/__pycache__/apps.cpython-313.pyc index 52d3823..16e99c1 100644 Binary files a/accounts/__pycache__/apps.cpython-313.pyc and b/accounts/__pycache__/apps.cpython-313.pyc differ diff --git a/accounts/__pycache__/forms.cpython-313.pyc b/accounts/__pycache__/forms.cpython-313.pyc index 90fb9f6..28b5292 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 41adae9..22c5d9f 100644 Binary files a/accounts/__pycache__/models.cpython-313.pyc and b/accounts/__pycache__/models.cpython-313.pyc differ diff --git a/accounts/__pycache__/signals.cpython-313.pyc b/accounts/__pycache__/signals.cpython-313.pyc new file mode 100644 index 0000000..7f8e323 Binary files /dev/null and b/accounts/__pycache__/signals.cpython-313.pyc differ diff --git a/accounts/__pycache__/urls.cpython-313.pyc b/accounts/__pycache__/urls.cpython-313.pyc index c024d90..be27102 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 fc7da10..ee2fd6a 100644 Binary files a/accounts/__pycache__/views.cpython-313.pyc and b/accounts/__pycache__/views.cpython-313.pyc differ diff --git a/accounts/admin.py b/accounts/admin.py index 8c38f3f..9d87a67 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -1,3 +1,13 @@ from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from .models import CustomUser, AbsencePeriod -# Register your models here. +class CustomUserAdmin(UserAdmin): + model = CustomUser + list_display = ['email', 'username', 'team', 'is_staff'] + fieldsets = UserAdmin.fieldsets + ( + (None, {'fields': ('team',)}), + ) + +admin.site.register(CustomUser, CustomUserAdmin) +admin.site.register(AbsencePeriod) \ No newline at end of file diff --git a/accounts/apps.py b/accounts/apps.py index 3e3c765..4f3f51c 100644 --- a/accounts/apps.py +++ b/accounts/apps.py @@ -1,6 +1,8 @@ from django.apps import AppConfig - class AccountsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'accounts' + + def ready(self): + import accounts.signals \ No newline at end of file diff --git a/accounts/forms.py b/accounts/forms.py index d7ab0e9..6fe6bb7 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -26,3 +26,12 @@ class CustomUserChangeForm(forms.ModelForm): class Meta: model = CustomUser 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) + birth_date = forms.DateField(input_formats=['%d.%m.%Y'], widget=forms.DateInput(format='%d.%m.%Y')) + + class Meta: + model = CustomUser + fields = ('username', 'first_name', 'last_name', 'email', 'birth_date', 'player_number', 'team') diff --git a/accounts/migrations/0003_absenceperiod.py b/accounts/migrations/0003_absenceperiod.py new file mode 100644 index 0000000..350dabe --- /dev/null +++ b/accounts/migrations/0003_absenceperiod.py @@ -0,0 +1,24 @@ +# Generated by Django 5.2.6 on 2025-10-01 07:05 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0002_customuser_team'), + ] + + operations = [ + migrations.CreateModel( + name='AbsencePeriod', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_date', models.DateField()), + ('end_date', models.DateField()), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='absence_periods', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/accounts/migrations/__pycache__/0003_absenceperiod.cpython-313.pyc b/accounts/migrations/__pycache__/0003_absenceperiod.cpython-313.pyc new file mode 100644 index 0000000..be680c5 Binary files /dev/null and b/accounts/migrations/__pycache__/0003_absenceperiod.cpython-313.pyc differ diff --git a/accounts/models.py b/accounts/models.py index 4d87b3a..fad17c1 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -30,4 +30,9 @@ class InvitationCode(models.Model): return True def __str__(self): - return self.code \ No newline at end of file + return self.code + +class AbsencePeriod(models.Model): + user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='absence_periods') + start_date = models.DateField() + end_date = models.DateField() \ No newline at end of file diff --git a/accounts/signals.py b/accounts/signals.py new file mode 100644 index 0000000..5e9dcc6 --- /dev/null +++ b/accounts/signals.py @@ -0,0 +1,19 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver +from .models import AbsencePeriod +from calendars.models import Event, EventParticipation + +@receiver(post_save, sender=AbsencePeriod) +def handle_absence_period(sender, instance, **kwargs): + user = instance.user + events_in_period = Event.objects.filter( + team=user.team, + start_time__date__gte=instance.start_date, + start_time__date__lte=instance.end_date + ) + for event in events_in_period: + EventParticipation.objects.update_or_create( + user=user, + event=event, + defaults={'status': 'rejected'} + ) diff --git a/accounts/templates/accounts/logout.html b/accounts/templates/accounts/logout.html new file mode 100644 index 0000000..099733f --- /dev/null +++ b/accounts/templates/accounts/logout.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block content %} +

Logout

+

Are you sure you want to log out?

+
+ {% csrf_token %} + +
+{% endblock %} diff --git a/accounts/templates/accounts/player_form.html b/accounts/templates/accounts/player_form.html new file mode 100644 index 0000000..51c8100 --- /dev/null +++ b/accounts/templates/accounts/player_form.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block content %} +

Create Player

+
+ {% csrf_token %} + {{ form.as_p }} + +
+{% endblock %} diff --git a/accounts/templates/base.html b/accounts/templates/base.html index 1e9ad1c..8d355b4 100644 --- a/accounts/templates/base.html +++ b/accounts/templates/base.html @@ -4,7 +4,14 @@ Baseball Organisator + {% if user.is_authenticated %} +
+ {% csrf_token %} + +
+ {% endif %} + {% block content %} {% endblock %} - + \ No newline at end of file diff --git a/accounts/urls.py b/accounts/urls.py index ab96f4f..2d0f7c6 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -5,7 +5,8 @@ from django.contrib.auth import views as auth_views urlpatterns = [ path('invitation/', views.invitation_code_view, name='invitation_code'), path('register/', views.register_view, name='register'), - path('login/', auth_views.LoginView.as_view(template_name='accounts/login.html'), name='login'), + path('login/', views.MyLoginView.as_view(template_name='accounts/login.html'), name='login'), 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'), ] \ No newline at end of file diff --git a/accounts/views.py b/accounts/views.py index b15b898..d407eaf 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -1,7 +1,12 @@ from django.shortcuts import render, redirect -from .forms import InvitationCodeForm, CustomUserCreationForm, CustomUserChangeForm -from .models import InvitationCode +from django.urls import reverse_lazy +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 .forms import InvitationCodeForm, CustomUserCreationForm, CustomUserChangeForm, PlayerCreationForm +from .models import CustomUser, InvitationCode +import uuid def invitation_code_view(request): if request.method == 'POST': @@ -52,4 +57,40 @@ def edit_profile(request): return redirect('edit_profile') # Or wherever you want to redirect else: form = CustomUserChangeForm(instance=request.user) - return render(request, 'accounts/edit_profile.html', {'form': form}) \ No newline at end of file + return render(request, 'accounts/edit_profile.html', {'form': form}) + +class HeadCoachCheckMixin(UserPassesTestMixin): + def test_func(self): + return self.request.user.is_superuser or self.request.user.coached_teams.exists() + +class PlayerCreateView(LoginRequiredMixin, HeadCoachCheckMixin, CreateView): + model = CustomUser + form_class = PlayerCreationForm + template_name = 'accounts/player_form.html' + success_url = reverse_lazy('dashboard') + + def form_valid(self, form): + # Create player user + player = form.save(commit=False) + player.is_active = False # Player can only login after using invitation code + player.save() + + # 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) + InvitationCode.objects.create(code=str(uuid.uuid4()), user=parent_user) + + return redirect(self.success_url) + +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 diff --git a/baseball_organisator/__pycache__/admin.cpython-313.pyc b/baseball_organisator/__pycache__/admin.cpython-313.pyc new file mode 100644 index 0000000..7ec8702 Binary files /dev/null and b/baseball_organisator/__pycache__/admin.cpython-313.pyc differ diff --git a/baseball_organisator/__pycache__/urls.cpython-313.pyc b/baseball_organisator/__pycache__/urls.cpython-313.pyc index 6603856..8149b53 100644 Binary files a/baseball_organisator/__pycache__/urls.cpython-313.pyc and b/baseball_organisator/__pycache__/urls.cpython-313.pyc differ diff --git a/baseball_organisator/admin.py b/baseball_organisator/admin.py new file mode 100644 index 0000000..d962bb2 --- /dev/null +++ b/baseball_organisator/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +def has_permission(self, request): + if request.user.is_active and request.user.is_staff: + if request.user.is_superuser: + return True + if hasattr(request.user, 'administered_clubs') and request.user.administered_clubs.exists(): + return True + return False + +admin.site.has_permission = has_permission.__get__(admin.site, admin.AdminSite) diff --git a/baseball_organisator/urls.py b/baseball_organisator/urls.py index 0119cc1..5a06906 100644 --- a/baseball_organisator/urls.py +++ b/baseball_organisator/urls.py @@ -16,6 +16,7 @@ Including another URLconf """ from django.contrib import admin from django.urls import path, include +import baseball_organisator.admin urlpatterns = [ path('admin/', admin.site.urls), diff --git a/calendars/__pycache__/admin.cpython-313.pyc b/calendars/__pycache__/admin.cpython-313.pyc index 5169540..04fda17 100644 Binary files a/calendars/__pycache__/admin.cpython-313.pyc and b/calendars/__pycache__/admin.cpython-313.pyc differ diff --git a/calendars/__pycache__/models.cpython-313.pyc b/calendars/__pycache__/models.cpython-313.pyc index 895e3a3..0e5eafd 100644 Binary files a/calendars/__pycache__/models.cpython-313.pyc and b/calendars/__pycache__/models.cpython-313.pyc differ diff --git a/calendars/admin.py b/calendars/admin.py index b997dd9..8764abb 100644 --- a/calendars/admin.py +++ b/calendars/admin.py @@ -1,7 +1,8 @@ from django.contrib import admin -from .models import Event, Training, Game, GameResult +from .models import Event, Training, Game, GameResult, EventParticipation admin.site.register(Event) admin.site.register(Training) admin.site.register(Game) -admin.site.register(GameResult) \ No newline at end of file +admin.site.register(GameResult) +admin.site.register(EventParticipation) diff --git a/calendars/migrations/0002_eventparticipation.py b/calendars/migrations/0002_eventparticipation.py new file mode 100644 index 0000000..01a53e3 --- /dev/null +++ b/calendars/migrations/0002_eventparticipation.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.6 on 2025-10-01 07:05 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('calendars', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='EventParticipation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('attending', 'Attending'), ('rejected', 'Rejected'), ('maybe', 'Maybe')], default='maybe', max_length=20)), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='calendars.event')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'event')}, + }, + ), + ] diff --git a/calendars/migrations/__pycache__/0002_eventparticipation.cpython-313.pyc b/calendars/migrations/__pycache__/0002_eventparticipation.cpython-313.pyc new file mode 100644 index 0000000..13be1ef Binary files /dev/null and b/calendars/migrations/__pycache__/0002_eventparticipation.cpython-313.pyc differ diff --git a/calendars/models.py b/calendars/models.py index 29228dd..5ec7b39 100644 --- a/calendars/models.py +++ b/calendars/models.py @@ -34,4 +34,12 @@ class GameResult(models.Model): inning_results = models.CharField(max_length=255, help_text="Comma-separated scores per inning, e.g., '1-0,0-2,3-1'") def __str__(self): - return f"Result for {self.game}" \ No newline at end of file + return f"Result for {self.game}" + +class EventParticipation(models.Model): + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + event = models.ForeignKey(Event, on_delete=models.CASCADE) + status = models.CharField(max_length=20, choices=[('attending', 'Attending'), ('rejected', 'Rejected'), ('maybe', 'Maybe')], default='maybe') + + class Meta: + unique_together = ('user', 'event') \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 57f3111..f70bebe 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/docs/phase5.md b/docs/phase5.md new file mode 100644 index 0000000..5b4c8c2 --- /dev/null +++ b/docs/phase5.md @@ -0,0 +1,29 @@ +## Phase 5: Fortgeschrittene Funktionen und Backend + +Diese Phase umfasst komplexere Features wie die Spieleranlage, die Abwesenheitsfunktion und das Backend-Management. + + Schritt 1: Spieleranlage-Workflow + + Implementiere in den accounts-Views einen Workflow für Headcoaches, um neue Spieler anzulegen. + + Die Logik muss die automatische Generierung von Einladungscodes für den Spieler und optional für bis zu zwei Elternteile umfassen. + + Die Überprüfung des Alters und die automatische Zuweisung eines Standard-Geburtsdatums muss implementiert werden. + + Schritt 2: Abwesenheitsfunktion implementieren + + Erstelle in accounts/models.py ein Modell AbsencePeriod mit Feldern start_date, end_date und einer Verknüpfung zum User. + + Implementiere in den Views die Logik, die alle Termine in diesem Zeitraum automatisch auf "abgelehnt" setzt. + + Schritt 3: Django-Admin-Bereich anpassen + + Konfiguriere den Django-Admin-Bereich, sodass nur Club-Admins und der Superuser darauf zugreifen können. + + Registriere die erstellten Modelle im Admin-Bereich, um die Verwaltung zu erleichtern. + + Schritt 4: User-Migration und Logik für Rollen-Änderungen + + Schreibe die Migrationsdateien für alle erstellten Modelle. + + Implementiere die Logik für den Wechsel der Rollen, z. B. wenn ein Spieler über 18 Jahre alt wird. diff --git a/docs/traceback/trace2.log b/docs/traceback/trace2.log new file mode 100644 index 0000000..1d56c3e --- /dev/null +++ b/docs/traceback/trace2.log @@ -0,0 +1,70 @@ +Exception in thread django-main-thread: +Traceback (most recent call last): + File "/usr/lib64/python3.13/threading.py", line 1043, in _bootstrap_inner + self.run() + ~~~~~~~~^^ + File "/usr/lib64/python3.13/threading.py", line 994, in run + self._target(*self._args, **self._kwargs) + ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/utils/autoreload.py", line 64, in wrapper + fn(*args, **kwargs) + ~~^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/management/commands/runserver.py", line 134, in inner_run + self.check(**check_kwargs) + ~~~~~~~~~~^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/management/base.py", line 492, in check + all_issues = checks.run_checks( + app_configs=app_configs, + ...<2 lines>... + databases=databases, + ) + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/checks/registry.py", line 89, in run_checks + new_errors = check(app_configs=app_configs, databases=databases) + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/checks/urls.py", line 16, in check_url_config + return check_resolver(resolver) + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/checks/urls.py", line 26, in check_resolver + return check_method() + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/urls/resolvers.py", line 531, in check + for pattern in self.url_patterns: + ^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/utils/functional.py", line 47, in __get__ + res = instance.__dict__[self.name] = self.func(instance) + ~~~~~~~~~^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/urls/resolvers.py", line 718, in url_patterns + patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module) + ^^^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/utils/functional.py", line 47, in __get__ + res = instance.__dict__[self.name] = self.func(instance) + ~~~~~~~~~^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/urls/resolvers.py", line 711, in urlconf_module + return import_module(self.urlconf_name) + File "/usr/lib64/python3.13/importlib/__init__.py", line 88, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1387, in _gcd_import + File "", line 1360, in _find_and_load + File "", line 1331, in _find_and_load_unlocked + File "", line 935, in _load_unlocked + File "", line 1026, in exec_module + File "", line 488, in _call_with_frames_removed + File "/home/mnagel/Projekte/baseball_organisator/baseball_organisator/urls.py", line 23, in + path('accounts/', include('accounts.urls')), + ~~~~~~~^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/urls/conf.py", line 39, in include + urlconf_module = import_module(urlconf_module) + File "/usr/lib64/python3.13/importlib/__init__.py", line 88, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1387, in _gcd_import + File "", line 1360, in _find_and_load + File "", line 1331, in _find_and_load_unlocked + File "", line 935, in _load_unlocked + File "", line 1026, in exec_module + File "", line 488, in _call_with_frames_removed + File "/home/mnagel/Projekte/baseball_organisator/accounts/urls.py", line 2, in + from . import views + File "/home/mnagel/Projekte/baseball_organisator/accounts/views.py", line 2, in + class MyLoginView(auth_views.LoginView): + ^^^^^^^^^^ +NameError: name 'auth_views' is not defined + diff --git a/docs/traceback/trace3.log b/docs/traceback/trace3.log new file mode 100644 index 0000000..25b9c6a --- /dev/null +++ b/docs/traceback/trace3.log @@ -0,0 +1,47 @@ +[01/Oct/2025 07:16:54] "GET / HTTP/1.1" 302 0 +[01/Oct/2025 07:17:22] "GET / HTTP/1.1" 200 540 +[01/Oct/2025 07:17:27] "POST /accounts/logout/ HTTP/1.1" 200 3420 +[01/Oct/2025 07:17:31] "GET / HTTP/1.1" 302 0 +[01/Oct/2025 07:17:31] "GET /accounts/login/?next=/ HTTP/1.1" 200 727 +[01/Oct/2025 07:17:37] "POST /accounts/login/?next=/ HTTP/1.1" 302 0 +[01/Oct/2025 07:17:37] "GET / HTTP/1.1" 200 540 +[01/Oct/2025 07:17:49] "GET /accounts/player/add/ HTTP/1.1" 200 2330 +Internal Server Error: /accounts/player/add/ +Traceback (most recent call last): + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/handlers/exception.py", line 55, in inner + response = get_response(request) + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/handlers/base.py", line 197, in _get_response + response = wrapped_callback(request, *callback_args, **callback_kwargs) + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/views/generic/base.py", line 105, in view + return self.dispatch(request, *args, **kwargs) + ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/contrib/auth/mixins.py", line 73, in dispatch + return super().dispatch(request, *args, **kwargs) + ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/contrib/auth/mixins.py", line 135, in dispatch + return super().dispatch(request, *args, **kwargs) + ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/views/generic/base.py", line 144, in dispatch + return handler(request, *args, **kwargs) + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/views/generic/edit.py", line 182, in post + return super().post(request, *args, **kwargs) + ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/views/generic/edit.py", line 151, in post + return self.form_valid(form) + ~~~~~~~~~~~~~~~^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/accounts/views.py", line 85, in form_valid + parent_user, created = CustomUser.objects.get_or_create(email=email, defaults={'username': email, 'is_active': False}) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/db/models/manager.py", line 87, in manager_method + return getattr(self.get_queryset(), name)(*args, **kwargs) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/db/models/query.py", line 946, in get_or_create + return self.get(**kwargs), False + ~~~~~~~~^^^^^^^^^^ + File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/db/models/query.py", line 636, in get + raise self.model.MultipleObjectsReturned( + ...<5 lines>... + ) +accounts.models.CustomUser.MultipleObjectsReturned: get() returned more than one CustomUser -- it returned 2! +[01/Oct/2025 07:18:48] "POST /accounts/player/add/ HTTP/1.1" 500 112859 +