feat: Implementierung von Phase 2 (Benutzer- und Rollenmodell) und Anpassung des Geburtsdatumsformats
This commit is contained in:
parent
bf27894513
commit
e119b5c914
BIN
accounts/__pycache__/forms.cpython-313.pyc
Normal file
BIN
accounts/__pycache__/forms.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
22
accounts/forms.py
Normal file
22
accounts/forms.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from django import forms
|
||||||
|
from .models import InvitationCode, CustomUser
|
||||||
|
|
||||||
|
class InvitationCodeForm(forms.Form):
|
||||||
|
code = forms.CharField(max_length=255, label="Einladungscode")
|
||||||
|
|
||||||
|
def clean_code(self):
|
||||||
|
code = self.cleaned_data.get('code')
|
||||||
|
try:
|
||||||
|
invitation_code = InvitationCode.objects.get(code=code)
|
||||||
|
if not invitation_code.is_valid():
|
||||||
|
raise forms.ValidationError("Dieser Einladungscode ist nicht mehr gültig.")
|
||||||
|
except InvitationCode.DoesNotExist:
|
||||||
|
raise forms.ValidationError("Ungültiger Einladungscode.")
|
||||||
|
return code
|
||||||
|
|
||||||
|
class CustomUserCreationForm(forms.ModelForm):
|
||||||
|
password = forms.CharField(widget=forms.PasswordInput)
|
||||||
|
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', 'password')
|
||||||
58
accounts/migrations/0001_initial.py
Normal file
58
accounts/migrations/0001_initial.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Generated by Django 5.2.6 on 2025-09-30 17:59
|
||||||
|
|
||||||
|
import django.contrib.auth.models
|
||||||
|
import django.contrib.auth.validators
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomUser',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('birth_date', models.DateField(blank=True, null=True)),
|
||||||
|
('player_number', models.IntegerField(default=999)),
|
||||||
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'user',
|
||||||
|
'verbose_name_plural': 'users',
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='InvitationCode',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('code', models.CharField(max_length=255, unique=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('is_active', models.BooleanField(default=True)),
|
||||||
|
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
BIN
accounts/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
BIN
accounts/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
Binary file not shown.
@ -1,3 +1,32 @@
|
|||||||
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
import datetime
|
||||||
|
|
||||||
# Create your models here.
|
class CustomUser(AbstractUser):
|
||||||
|
birth_date = models.DateField(null=True, blank=True)
|
||||||
|
player_number = models.IntegerField(default=999)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def age(self):
|
||||||
|
if not self.birth_date:
|
||||||
|
return None
|
||||||
|
today = datetime.date.today()
|
||||||
|
return today.year - self.birth_date.year - ((today.month, today.day) < (self.birth_date.month, self.birth_date.day))
|
||||||
|
|
||||||
|
class InvitationCode(models.Model):
|
||||||
|
code = models.CharField(max_length=255, unique=True)
|
||||||
|
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
is_active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
if not self.is_active:
|
||||||
|
return False
|
||||||
|
two_weeks_ago = timezone.now() - datetime.timedelta(weeks=2)
|
||||||
|
if self.created_at < two_weeks_ago:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.code
|
||||||
10
accounts/templates/accounts/invitation_code.html
Normal file
10
accounts/templates/accounts/invitation_code.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Enter Invitation Code</h2>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
10
accounts/templates/accounts/login.html
Normal file
10
accounts/templates/accounts/login.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Login</h2>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
10
accounts/templates/accounts/register.html
Normal file
10
accounts/templates/accounts/register.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Register</h2>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<button type="submit">Register</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
10
accounts/templates/base.html
Normal file
10
accounts/templates/base.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Baseball Organisator</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,6 +1,10 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from . import views
|
from . import views
|
||||||
|
from django.contrib.auth import views as auth_views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Add your URLs here
|
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('logout/', auth_views.LogoutView.as_view(), name='logout'),
|
||||||
|
]
|
||||||
@ -1,3 +1,43 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render, redirect
|
||||||
|
from .forms import InvitationCodeForm, CustomUserCreationForm
|
||||||
|
from .models import InvitationCode
|
||||||
|
|
||||||
# Create your views here.
|
def invitation_code_view(request):
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = InvitationCodeForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
code = form.cleaned_data['code']
|
||||||
|
request.session['invitation_code'] = code
|
||||||
|
return redirect('register')
|
||||||
|
else:
|
||||||
|
form = InvitationCodeForm()
|
||||||
|
return render(request, 'accounts/invitation_code.html', {'form': form})
|
||||||
|
|
||||||
|
def register_view(request):
|
||||||
|
invitation_code_str = request.session.get('invitation_code')
|
||||||
|
if not invitation_code_str:
|
||||||
|
return redirect('invitation_code')
|
||||||
|
|
||||||
|
try:
|
||||||
|
invitation_code = InvitationCode.objects.get(code=invitation_code_str)
|
||||||
|
if not invitation_code.is_valid():
|
||||||
|
# Handle invalid code, maybe redirect with a message
|
||||||
|
return redirect('invitation_code')
|
||||||
|
except InvitationCode.DoesNotExist:
|
||||||
|
return redirect('invitation_code')
|
||||||
|
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = CustomUserCreationForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
user = form.save(commit=False)
|
||||||
|
user.set_password(form.cleaned_data['password'])
|
||||||
|
user.save()
|
||||||
|
invitation_code.is_active = False
|
||||||
|
invitation_code.user = user
|
||||||
|
invitation_code.save()
|
||||||
|
# Log the user in and redirect to the dashboard
|
||||||
|
return redirect('login') # Or wherever you want to redirect after registration
|
||||||
|
else:
|
||||||
|
form = CustomUserCreationForm()
|
||||||
|
return render(request, 'accounts/register.html', {'form': form})
|
||||||
Binary file not shown.
BIN
baseball_organisator/__pycache__/wsgi.cpython-313.pyc
Normal file
BIN
baseball_organisator/__pycache__/wsgi.cpython-313.pyc
Normal file
Binary file not shown.
@ -124,3 +124,5 @@ STATIC_URL = 'static/'
|
|||||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
AUTH_USER_MODEL = 'accounts.CustomUser'
|
||||||
|
|||||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
27
docs/phase2.md
Normal file
27
docs/phase2.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
## Phase 2: Benutzer- und Rollenmodell
|
||||||
|
|
||||||
|
In dieser Phase liegt der Fokus auf der Erstellung der Benutzerprofile und der Definition der verschiedenen Rollen.
|
||||||
|
|
||||||
|
Schritt 1: Benutzer- und Profilmodelle erstellen
|
||||||
|
|
||||||
|
Navigiere in das accounts-Verzeichnis und öffne models.py.
|
||||||
|
|
||||||
|
Erstelle ein benutzerdefiniertes User-Modell, das von AbstractUser erbt, um es an die spezifischen Anforderungen anzupassen (z. B. für birthdate).
|
||||||
|
|
||||||
|
Füge die folgenden Felder hinzu: first_name, last_name, birth_date (Geburtsdatum), und player_number mit einem Standardwert von 999.
|
||||||
|
|
||||||
|
Das Alter soll dynamisch aus dem Geburtsdatum berechnet werden. Die Rollen Player, Parent, Child, Coach, AssistantCoach, HeadCoach werden nicht als boolesche Felder gespeichert, sondern durch die Beziehungen zu anderen Modellen abgeleitet.
|
||||||
|
|
||||||
|
Schritt 2: Einladungscode-Modell erstellen
|
||||||
|
|
||||||
|
Erstelle in accounts/models.py ein Modell namens InvitationCode mit Feldern für den Code selbst, das Erstellungsdatum, die Gültigkeit (2 Wochen) und einen Status (aktiv/deaktiviert).
|
||||||
|
|
||||||
|
Verknüpfe dieses Modell mit dem User-Modell.
|
||||||
|
|
||||||
|
Schritt 3: Benutzerauthentifizierung und Logik implementieren
|
||||||
|
|
||||||
|
Erstelle in der accounts-App Logik für die Registrierung mittels Einladungscode.
|
||||||
|
|
||||||
|
Erstelle Views und URLs für die Code-Eingabe, Benutzerdatenerstellung und den Login.
|
||||||
|
|
||||||
|
Sorge dafür, dass der Einladungscode nach der Verwendung erlischt.
|
||||||
Loading…
x
Reference in New Issue
Block a user