Das User-Model anpassen

Für unsere App wird es nötig sein, dass wir Events Autoren zuordnen können, die diese eingestellt haben. User müssen sich also auf unserer Website registrieren und einloggen können, um Events zu erstellen.

Django bietet out-of-the-box Zugriff auf das Default-User-Model.

Moment mal. Was ist denn eigentlich ein User-Model?

Das User-Model ist auch ein Django-Model, welches bestimmte Felder besitzt, zum Beispiel das Feld username oder password. Auch für dieses Model exisitiert eine Datenbank-Tabelle mit genau diesen Feldern, wie wir später sehen werden. Dieses Modell bietet zusätzlich Features wie Authentifizierung, Passwort-Hashing usw.

Es wäre kein Problem, das User-Model aus django.contrib.auth.models zu importieren und damit unserem Event-Model einen Autor zu geben: Die Event-Autor Beziehung wäre wieder eine 1:N-Beziehung und wird wie schon bei der Category über ein ForeignKey-Feld gelöst.

Beispiel-Code, wie man es machen könnte, aber nicht machen sollte:

from django.db import models
from django.contrib.auth.models import User

class Event(DateMixin):
    # überflüssiger Code wurde hier weggelassen

    author = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="events"
    )

Das Problem an dieser Stelle ist, dass wir hier auf das Default-User-Model referenzieren. Oft ist es allerdings so, dass wir in einem Django-Projekt auch das User-Model anpassen wollen. Oder die App in einer Umgebung eingesetzt werden soll, wo nicht das Default-User-Model genutzt wird.

Warum sollte man das User-Model ändern wollen?

Das Default-User-Model bietet eine Handvoll an nützlichen und notwendigen Attributen wie first_name, staff_status. Es könnte aber sein, dass man dem User-Model noch ein weiteres Feld hinzufügen möchte, zum Beispiel address oder ähnliches.

Um dies zu realisieren, müssen wir projektweit ein modifiziertes User-Model ermöglichen. Auch wenn dieser Schritt anfangs gar nicht geplant ist, macht es Sinn, sich die Möglichkeit zumindest offen zu lassen.

If you’re starting a new project, it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you.

—Django Dokumentation, Extending the existing User model

Best Practice

In jedem neuen Projekt sollte das Anlegen eines eigenen User-Models einer der ersten Schritte sein. Dies muss allerdings noch vor der ersten Migration geschehen. Sonst könnte es passieren, dass innerhalb eines Projekts auf verschiedene User-Models verwiesen wird. https://docs.djangoproject.com/en/ref/topics/auth/customizing/#substituting-a-custom-user-model

Das angepasst User-Model

Um ein eigenes User-Model zu implementieren, erstellen wir eine App mit dem angepassten User-Model, auf das wir dann projektweit Zugriff haben. Zusätzlich wird dieses User-Model im System registriert und auch vom System, dh. von Django selbst, genutzt.

Bemerkung

Wichtig: Das muss VOR der allerersten Migration geschehen! Andernfalls referenzieren dann manche Tabellen noch auf das alte User-Model, was langfristig natürlich zu Problemen führen kann.

eine user-App erstellen:

Wir erstellen eine neue App namens user.

(eventenv) python manage.py startapp user

models-Datei anpassen:

Wir modifizieren die Datei event_manager/user/models.py wie folgt:

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    """Das ist das neue User-Model. Es hat bisher keine zusätzlichen Attribute."""
    ...

Abstract User VS. AbstractBaseUser

Django bietet zwei Klassen an, von dem unser User erben kann, die AbstractUser- sowie die AbstractBaseUser-Klasse. Wer die volle Kontrolle über das Benutzermodell benötigen, nimmt besser die AbstractBaseUser-Klasse, muss sich dann aber auch darauf gefasst machen, viele Details selber nachzuimplementieren.

Wer nur ein paar Dinge zum vorhandenen Benutzer hinzufügen möchten, z. B. ein zusätzliches Feld Role, ein Adress-oder Sloganfeld, der verwendet besser die AbstractUser-Klasse. In den meisten Fällen reicht die auch aus. Wir werden in diesem Buch ohnehin keine zusätzlichen Felder implementieren.

Mehr zu diesem Thema hier: https://testdriven.io/blog/django-custom-user-model/

App in den Settings registrieren

Damit die App von Django später auch gefunden wird, müssen wir sie in den Settings registrieren. Dazu öffnen wir die Datei event_manager/event_manager/settings.py und modifizieren folgende Zeilen:

INSTALLED_APPS = [
    "user", # <= das hier hinzufügen
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "events",
]

Wir müssen darauf achten, user ganz oben in den INSTALLED_APPS zu platzieren, damit später unsere Templates für die Registrierung und das Change-Password-Formular gefunden werden. Mehr dazu aber später.

Nun müssen wir Django noch sagen, dass wir ein eigenes User-Model nutzen wollen. Dazu setzen wir ebenfalls in der Datei event_manager/event_manager/settings.py die AUTH_USER_MODEL-Konstante auf das User-Model unserer neuen User-App:

AUTH_USER_MODEL = 'user.User'

So sieht unsere Datei event_manager/user/models.py nun aus:

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    pass

App in der Admin

Tragen wir in die Datei event_manager/user/admin.py noch folgendes ein:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

Die admin.py steuert das Aussehen des Models in der Administrationsoberfläche. Dazu aber später mehr.

Zugriff auf das User-Model

Wo auch immer wir das User-Model benötigen, sollten wir nicht direkt drauf zugreifen, sondern über die Helfermethode get_user_model. So bleiben wir flexibel, wenn wir das User-Model ändern wollen. get_user_model gibt uns nämlich immer genau das User-Model zurück, welches wir in den Settings per AUTH_USER_MODEL referenzieren. Praktisch auch, wenn wir die App in einem anderen Projekt einbauen, und nicht wissen, welches User-Model genutzt wird.

Einbinden des aktuellen User-Models in einem Beispielcode:

from django.contrib.auth import get_user_model

User = get_user_model()

Wir haben nun alle nötigen Models erstellt und können diese nun migrieren. Wie das geht, erfahrt ihr im nächsten Kapitel. Später werden wir das User-Model auch nutzen, indem ein Event einem Autor zugeordnet werden kann.

Weiterführende Informationen zum User-Model finden sich in der Django-Doku

https://docs.djangoproject.com/en/ref/topics/auth/customizing/#substituting-a-custom-user-model