.. _authentification: .. index:: single: User single: auth single: Registrierung single: Authentifizierung 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 .. admonition:: 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. .. code-block:: python 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: .. code-block:: python 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: .. literalinclude:: ../../../src/events/create_user_form_1.py 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: .. code-block:: python 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. .. code-block:: bash {% extends 'base.html' %} {% load crispy_forms_tags %} {% block head%} Restriere dich jetzt! {% endblock %} {% block content %}
{% csrf_token %} {{ form|crispy }}
{% 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: .. code-block:: python 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.