feat: Erweiterung der Spielerstellung um Elternsuche mit Autocomplete
This commit is contained in:
parent
956d7a45e9
commit
4203a696d9
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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:
|
||||
|
||||
19
accounts/migrations/0004_customuser_parents.py
Normal file
19
accounts/migrations/0004_customuser_parents.py
Normal file
@ -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),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@ -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):
|
||||
|
||||
@ -17,4 +17,39 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const parent1SearchInput = document.getElementById('id_parent1_search');
|
||||
const parent2SearchInput = document.getElementById('id_parent2_search');
|
||||
|
||||
function setupAutocomplete(inputElement) {
|
||||
const datalist = document.createElement('datalist');
|
||||
datalist.id = inputElement.id + '_list';
|
||||
inputElement.setAttribute('list', datalist.id);
|
||||
document.body.appendChild(datalist); // Append to body instead of parentNode
|
||||
|
||||
inputElement.addEventListener('input', function() {
|
||||
const query = this.value;
|
||||
if (query.length < 2) {
|
||||
datalist.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
fetch(`/accounts/user-search/?q=${query}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
datalist.innerHTML = '';
|
||||
data.forEach(userString => {
|
||||
const option = document.createElement('option');
|
||||
option.value = userString;
|
||||
datalist.appendChild(option);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setupAutocomplete(parent1SearchInput);
|
||||
setupAutocomplete(parent2SearchInput);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -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'),
|
||||
]
|
||||
@ -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)
|
||||
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)
|
||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user