feat: Implementierung von Phase 3 und Profilbearbeitung

This commit is contained in:
Matthias Nagel 2025-10-01 08:19:05 +02:00
parent e119b5c914
commit 05da0c94ac
20 changed files with 206 additions and 4 deletions

View File

@ -20,3 +20,9 @@ class CustomUserCreationForm(forms.ModelForm):
class Meta: class Meta:
model = CustomUser model = CustomUser
fields = ('username', 'first_name', 'last_name', 'email', 'birth_date', 'player_number', 'password') fields = ('username', 'first_name', 'last_name', 'email', 'birth_date', 'player_number', 'password')
class CustomUserChangeForm(forms.ModelForm):
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')

View File

@ -0,0 +1,20 @@
# Generated by Django 5.2.6 on 2025-09-30 18:09
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
('clubs', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='customuser',
name='team',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='players', to='clubs.team'),
),
]

View File

@ -6,6 +6,7 @@ import datetime
class CustomUser(AbstractUser): class CustomUser(AbstractUser):
birth_date = models.DateField(null=True, blank=True) birth_date = models.DateField(null=True, blank=True)
player_number = models.IntegerField(default=999) player_number = models.IntegerField(default=999)
team = models.ForeignKey('clubs.Team', on_delete=models.SET_NULL, null=True, blank=True, related_name='players')
@property @property
def age(self): def age(self):

View File

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block content %}
<h2>Edit Profile</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save Changes</button>
</form>
{% endblock %}

View File

@ -7,4 +7,5 @@ urlpatterns = [
path('register/', views.register_view, name='register'), path('register/', views.register_view, name='register'),
path('login/', auth_views.LoginView.as_view(template_name='accounts/login.html'), name='login'), path('login/', auth_views.LoginView.as_view(template_name='accounts/login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('profile/', views.edit_profile, name='edit_profile'),
] ]

View File

@ -1,6 +1,7 @@
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from .forms import InvitationCodeForm, CustomUserCreationForm from .forms import InvitationCodeForm, CustomUserCreationForm, CustomUserChangeForm
from .models import InvitationCode from .models import InvitationCode
from django.contrib.auth.decorators import login_required
def invitation_code_view(request): def invitation_code_view(request):
if request.method == 'POST': if request.method == 'POST':
@ -40,4 +41,15 @@ def register_view(request):
return redirect('login') # Or wherever you want to redirect after registration return redirect('login') # Or wherever you want to redirect after registration
else: else:
form = CustomUserCreationForm() form = CustomUserCreationForm()
return render(request, 'accounts/register.html', {'form': form}) return render(request, 'accounts/register.html', {'form': form})
@login_required
def edit_profile(request):
if request.method == 'POST':
form = CustomUserChangeForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
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})

View File

@ -1,3 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import Club, Team
# Register your models here. admin.site.register(Club)
admin.site.register(Team)

View File

@ -0,0 +1,37 @@
# Generated by Django 5.2.6 on 2025-09-30 18:09
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Club',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('website', models.URLField(blank=True)),
('administrators', models.ManyToManyField(related_name='administered_clubs', to=settings.AUTH_USER_MODEL)),
],
),
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)),
('assistant_coaches', models.ManyToManyField(blank=True, related_name='assisted_teams', to=settings.AUTH_USER_MODEL)),
('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teams', to='clubs.club')),
('head_coach', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='coached_teams', to=settings.AUTH_USER_MODEL)),
('parent_team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='child_teams', to='clubs.team')),
],
),
]

View File

@ -1,3 +1,20 @@
from django.db import models from django.db import models
from django.conf import settings
# Create your models here. class Club(models.Model):
name = models.CharField(max_length=255)
website = models.URLField(blank=True)
administrators = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='administered_clubs')
def __str__(self):
return self.name
class Team(models.Model):
name = models.CharField(max_length=255)
club = models.ForeignKey(Club, on_delete=models.CASCADE, related_name='teams')
head_coach = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='coached_teams')
assistant_coaches = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='assisted_teams', blank=True)
parent_team = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='child_teams')
def __str__(self):
return self.name

Binary file not shown.

27
docs/phase3.md Normal file
View File

@ -0,0 +1,27 @@
## Phase 3: Organisationsstruktur
Diese Phase befasst sich mit den Modellen für Clubs, Mannschaften und Teams.
Schritt 1: Club-Modell erstellen
Navigiere in das clubs-Verzeichnis und öffne models.py.
Erstelle ein Club-Modell mit den Feldern name und website.
Verknüpfe den Club mit dem User-Modell, um Administratoren zuzuweisen.
Schritt 2: Mannschafts- und Team-Modelle erstellen
Erstelle ein Team-Modell, das die Mannschaft repräsentiert, da in der Projektbeschreibung die Mannschaft als Team behandelt werden kann. Ein is_team Feld könnte hier nützlich sein, ist aber nicht notwendig, da die Beziehungen zu parent_team die Hierarchie festlegen.
Ein Team-Modell hat die Felder: name, head_coach (Fremdschlüssel zu User), assistant_coaches (ManyToMany-Feld zu User), und parent_team (Fremdschlüssel zu sich selbst für die Untergliederung).
Ein Team gehört zu einem Club.
Schritt 3: Benutzerbeziehungen implementieren
Stelle sicher, dass die Beziehungen zwischen User und Team korrekt definiert sind:
Ein User kann Player sein und einem Team zugewiesen werden.
Ein User kann HeadCoach oder AssistantCoach eines Team sein.

69
docs/traceback/trace1.log Normal file
View File

@ -0,0 +1,69 @@
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 "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/home/mnagel/Projekte/baseball_organisator/baseball_organisator/urls.py", line 22, in <module>
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 "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/home/mnagel/Projekte/baseball_organisator/accounts/urls.py", line 2, in <module>
from . import views
File "/home/mnagel/Projekte/baseball_organisator/accounts/views.py", line 2, in <module>
@login_required
^^^^^^^^^^^^^^
NameError: name 'login_required' is not defined