User Registrierung
Bisher mussten wir als Administrator die User anlegen. Das ist natürlich für den Produktiv-Betrieb wie einem Online-Shop ungünstig. Es ist seit vielen Jahren Gang und Gebe, dass man sich auf einer Website selber registrieren kann. https://python.plainenglish.io/introduction-to-login-and-logout-in-django-85e6b71cbfca
DSGVO-konformes Registrierungsformular
An User, die sich auf der Website registrieren, sollte das System auf alle
Fälle eine Bestätigungs-Email gesendet werden, um zu überprüfen, ob sich auch
wirklich der User mit dieser Email angemeldet hat. Das geht technisch gesehen
mit einem Token, der bei Erstellung des Users gespeichert wird und dann mit
der Bestätigungsemail abgeglichen wird. Stimmen die beiden Token überein,
wird der User „aktiviert“, dh. seit is_active
-Status wird auf True
gesetzt.
Da diese Überprüfung den Rahmen dieses Buches sprengen würde, haben wir an dieser Stelle darauf verzichtet, diese aufwändige Überprüfung zu implementieren.
User Registrierungs URL
für die Registrierung eines Users am System, also dem signup
gibt es von
Django keine Default Möglichkeiten, wie das noch bei Login und Passwort ändern
der Fall war. Wir müssen hier jetzt alles selber implementieren.
Zuerst einmal passen wir die URLs unter event_manager/event_manager/urls.py
an. Da wir unsere Registieruns-URL auch unter accounts
laufen lassen
wollen, dafür aber später unsere eigene User-App
ansprechen müssen, legen
wir einen zweiten Eintrag mit path('accounts/')
an.
urlpatterns = [
# [..]
path("accounts/", include("user.urls")),
path("accounts/", include("django.contrib.auth.urls")),
# [..]
]
Auf den ersten Blick sieht das ungewöhnlich aus, ist aber legales Vorgehen im
URL-Design. Während also http://127.0.0.1/accounts/login
in der Auth-App
von Django zu finden ist, werden wir http://127.0.0.1/accounts/signup
in
der User-App implementieren.
Die URLs für die User-App
in den Projekt-URLs unter event_manager/event_manager/urls.py
haben wir
schon auf unsere User-App referenziert. Aus diesem Grund legen wir die Datei
jetzt mal unter event_manager/user/urls.py
an und fügen folgenden Inhalt
ein:
from django.urls import path
from .views import SignUpView
app_name = "user"
urlpatterns = [
path("signup/", SignUpView.as_view(), name="signup"),
]
Unser Registierungsformular soll also unter http://127.0.0.1/accounts/signup
aufrufbar sein.
die SignUpView
gibt es bisher noch nicht, die müssen wir im folgenden anlegen. Bevor wir das tun, müssen wir aber noch das Formular selbst definieren.Bisher hatten wir ja forms.ModelForm
gentutz, da unsere Formulare immer auf dem Model selbst aufgebaut haben.
Das Registrierungs-Formular
Das Anlegen eines Users über ein Formular ist so eine Standard-Aufgabe, dass es
dafür sogar ein Built-In Formular gibt, das UserCreationForm
. Wir bräuchten
also kein eigenes Formular für diese Aufgabe anlegen.
Wir wollen das Standard Formular aber um ein Feld erweitern. Und zwar wollen
wir eine Checkbox einbauen, die angegklickt werden muss, um die
Datenschutzbestimmungen zu bestätigen. Wird diese Checkbox vom User nicht angeglickt,
wird ein Validation-Fehler erhoben und der Registrierungsprozess nicht
abgeschossen. Dazu legen wir eine neue Forms-Datei in der User-App an: event_manager/user/forms.py
.
So sieht das fertige Formular unter event_manager/user/forms.py
aus:
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from django.core.exceptions import ValidationError
User = get_user_model()
class SignUpForm(UserCreationForm):
class Meta:
model = User
fields = (
"username",
"email",
"password1",
"password2",
)
labels = {"privacy": "Datenschutzerklärung"}
privacy = forms.BooleanField(required=False)
def clean_privacy(self):
"""Die Datenschutzerklärung muss angeglickt werden, ansonsten wird ein
ValidationError ausgelöst"""
privacy = self.cleaned_data["privacy"]
if not privacy:
raise ValidationError(
"""Bitte bestätigen Sie, dass Sie die
Datenschutzerklärung gelesen haben!"""
)
return privacy
Die Views für die User-App
Nun fehlt neben dem Template noch die View, in der wir das Formular an den User durchreichen.
Legen wir jetzt unter event_manager/user/views.py
die Views für die
User-App an und fügen folgenden Inhalt ein:
from django.urls import reverse_lazy
from django.views import generic
from .forms import SignUpForm
class SignUpView(generic.CreateView):
form_class = SignUpForm
success_url = reverse_lazy("login")
template_name = "registration/signup.html"
Was passiert hier? Wir importieren neben den generischen Views auch noch das
User-Registierungsformular aus der eben erstellten forms.py
. Dann erstellen
wir eine class-based SignUpView
, die von generic.CreateView
erbt. Bei
der CreateView
handelt es sich um eine generische View, die für das Anlegen von
Usern genutzt werden kann. Wir passen sie nur unseren Ansprüchen etwas an. Das
heisst, nach erfolgreicher Registierungen werden wir an das Login-Formular
weitergeleteitet (success_url
) und unser Template soll das signup.html
sein, dass wir noch anlegen müssen.
Fast fertig, es fehlt nur noch das Template, dann kann sich der erste User bei uns registrieren!
Das Template für das Registrierungs-Formular
Das Registrierungs-Template lebe wie die anderen Templates der
Authentifizierungsaktionen ebenfalls unter event_manager/templates
. Leben
wir dort eine neue Datei namens signup.html
an und füllen sie mit diesem
Inhalt.
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block head%}
Restriere dich jetzt!
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Registrieren!</button>
</form>
{% endblock %}
Die Registrierung testen
Falls Du eingeloggt bist, logge dich jetzt aus und klicke dann auf den
Registrierungslink. Du solltest an http://127.0.0.1/accounts/signup
weitergeleitet werden. Führe den Registrierungsvorgang aus und logge dich mit
dem neu erstellten User ein.
Der Schönheitsfehler von vorhin
Wir hatten ja festgestellt, dass wir uns, obwohl wir eingeloggt sind, auf die
Login-URL unter http://127.0.0.1/accounts/login
begeben können. Das heisst,
wir können uns einloggen, obwohl wir schon eingeloggt sind. Das ist verwirrend,
und das wollen wir ändern.
Dazu müssen wir die event_manager/user/urls.py
Datei anpassen. Die
Default-Login-Route der Auth-App verhindert nicht, dass wir sie aufrufen
können, obwohl wir eingeloggt sind. Deshalb überschreiben wir die Login-Route
wie folgt:
from django.contrib.auth import views as auth_views
from django.urls import path
from .views import SignUpView
app_name = "user"
urlpatterns = [
# diese Route überschreiben wir
path(
"login/",
auth_views.LoginView.as_view(redirect_authenticated_user=True),
name="login",
),
path("signup/", SignUpView.as_view(), name="signup"),
]
Die auth_views.LoginView
ist die Standard-Login-View von Django. Per
default ist hier das Attribut redirect_authenticated_user
auf False
gesetzt, d.h. auch eingeloggte User werden auf das Login-Formular weitergeleitet. Das
verhindern wir, indem wir den Wert auf True setzen.