Extra velden toevoegen aan een aangepaste gebruikersmodel in Django

Het is een goed idee om in Django een aangepast gebruikersmodel te definiëren. In deze tutorial zullen we een aantal velden zoals geboortedatum, adres, telefoonnummer aan het model toevoegen. We zullen ook al deze velden bewerkbaar maken in de Django en Wagtail admin.

30 juni 2020 11:27
Thema's: Aangepast gebruikersmodel

Laten we eerst naar admin kijken. Om dat te doen, hebben we een supergebruiker nodig, dus maak er een aan, als je dat nog niet hebt gedaan:

python3 manage.py createsuperuser

Maak een gebruikersnaam en een wachtwoord en geef een e-mailadres op wanneer daarom wordt gevraagd. In een Wagtail-project is de traditionele Django-admin te vinden op http://127.0.0.1:8000/django-admin (zonder Wagtail op …/admin). Als je je daar aanmeldt, is het aangepaste gebruikersmodel er niet. Om het in admin te identificeren, zet het volgende in het admin.py-bestand van het project

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser

class CustomUserAdmin(UserAdmin):
    add_form = UserCreationForm
    form = UserChangeForm
    model = CustomUser
    list_display = ['pk', 'email', 'username', 'first_name', 'last_name']
    add_fieldsets = UserAdmin.add_fieldsets + (
        (None, {'fields': ('email', 'first_name', 'last_name',)}),
    )
    fieldsets = UserAdmin.fieldsets


admin.site.register(CustomUser, CustomUserAdmin)

Met UserCreationForm en UserChangeForm kunnen we gebruikers aanmaken en bewerken in onze admin. Het attribuut list_display bepaalt welke velden worden weergegeven in het admin-gebruikersoverzicht en fieldsets welke velden kunnen worden aangemaakt of bewerkt. Controleer nu http://127.0.0.1:8000/django-admin: de aangepaste gebruiker is zichtbaar in admin en gebruikers kunnen worden aangemaakt en bewerkt.

Tot nu toe hebben we nog steeds niets meer dan de standaard Django-gebruikersvelden. Het is tijd om wat extra velden toe te voegen in models.py van een van onze apps (genaamd userauth). Ik voeg alle velden tegelijk toe en leg ze hieronder uit.

from django.contrib.auth.models import AbstractUser
from django.core.validators import RegexValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_countries.fields import CountryField

class CustomUser(AbstractUser):
    display_name = models.CharField(verbose_name=_("Display name"), max_length=30, help_text=_("Will be shown e.g. when commenting"))
    date_of_birth = models.DateField(verbose_name=_("Date of birth"), blank=True, null=True)
    address1 = models.CharField(verbose_name=_("Address line 1"), max_length=1024, blank=True, null=True)
    address2 = models.CharField(verbose_name=_("Address line 2"), max_length=1024, blank=True, null=True)
    zip_code = models.CharField(verbose_name=_("Postal Code"), max_length=12, blank=True, null=True)
    city = models.CharField(verbose_name=_("City"), max_length=1024, blank=True, null=True)
    country = CountryField(blank=True, null=True)
    phone_regex = RegexValidator(regex=r"^\+(?:[0-9]●?){6,14}[0-9]$", message=_("Enter a valid international mobile phone number starting with +(country code)"))
    mobile_phone = models.CharField(validators=[phone_regex], verbose_name=_("Mobile phone"), max_length=17, blank=True, null=True)
    additional_information = models.CharField(verbose_name=_("Additional information"), max_length=4096, blank=True, null=True)
    photo = models.ImageField(verbose_name=_("Photo"), upload_to='photos/', default='photos/default-user-avatar.png')

    class Meta:
        ordering = ['last_name']

    def __str__(self):
        return f"{self.username}: {self.first_name} {self.last_name}"

De weergavenaam (display_name) spreekt voor zich. We hebben de mogelijkheid toegevoegd om de uitgebreide naam te vertalen door gettext_lazy te gebruiken. Als je zeker weet dat je maar één taal gebruikt, kun je dit natuurlijk weglaten. In deze tutorial zullen we consequent de vertaalfunctionaliteit toevoegen voor alle 'voor mensen leesbare' strings. Het veld voor de geboortedatum is niet verplicht, zoals wordt aangegeven door de parameters blank = True, null = True. Adresvelden spreken voor zich, behalve het CountryField. Dit is geen standaard Django, het wordt geïmporteerd uit een pakket genaamd django-countries. Hierdoor kunnen we een land kiezen uit een lijst met alle landen ter wereld, in plaats van het land gewoon in een tekstveld te typen. We installeren het pakket met

pip3 install django-countries

Voeg django_countries toe aan de lijst van INSTALLED_APPS in settings/base.py en voeg het ook toe aan requirements.txt. Het veld phone_regex is een RegexValidator met een patroon dat past bij internationale telefoonnummers. Het kan natuurlijk niet garanderen dat het telefoonnummer geldig is, maar het biedt wel enige bescherming tegen foutieve invoer. Het regex-veld wordt gebruikt in het mobiele-telefoonveld.

Het standaard Django ImageField plaatst geüploade afbeeldingen in de directory die wordt aangegeven door het veld upload_to, voorafgegaan door de parameter MEDIA_ROOT in de instellingen. Aangezien onze media root onze basismap is plus media, zullen geüploade afbeeldingen in /media/photos/ worden geplaatst. We voegen ook een standaardafbeelding toe in de directory photos/. Vanwege de manier waarop Django omgaat met bestanden, zal het MEDIA_ROOT voorvoegen aan het pad voor de standaardafbeelding. Dat betekent dat we onze standaardafbeelding in dezelfde map /media/photos/ moeten plaatsen. Het handmatig plaatsen van bestanden in MEDIA_ROOT is een uitzondering, omdat het normaal gesproken alleen wordt gebruikt voor het uploaden van bestanden; er zijn andere manieren om een ​​standaardafbeelding te gebruiken als je dat wilt vermijden.

We hebben ook een volgorde van onze gebruikers (op achternaam) en een weergave gedefinieerd, handig bij het bekijken van gebruikers in de admin. De velden username, last_name en first_name zijn beschikbaar via het model AbstractUser.

Ons gebruikersmodel is compleet, nu moeten we de extra velden toevoegen aan onze admin. Voeg eerst in de CustomUserAdmin in admin.py de extra velden toe aan add_fieldsets en fieldets

add_fieldsets = UserAdmin.add_fieldsets + (
    (None, {'fields': ('email', 'first_name', 'last_name', 'display_name', 'date_of_birth', 'address1', 'address2', 'zip_code', 'city', 'country', 'mobile_phone', 'additional_information', 'photo',)}),
)
fieldsets = UserAdmin.fieldsets + (
    (None, {'fields': ('display_name', 'date_of_birth', 'address1', 'address2', 'zip_code', 'city', 'country', 'mobile_phone', 'additional_information', 'photo',)}),
)

We moeten onze database migreren om alle nieuwe velden op te nemen:

python3 manage.py makemigrations
python3 manage.py migrate

Als we nu de server starten en naar Django admin gaan, zien we dat alle velden er zijn!

Tijd om naar de Wagtail-admin te kijken op http://127.0.0.1:8000/admin. Alleen de standaardvelden zijn er nog, dus we moeten de extra velden toevoegen. Wagtail heeft ook een UserCreationForm en een UserEditForm (iets andere naam hier), dus we voegen het volgende toe aan forms.py:

from .models import CustomUser
from wagtail.users.forms import UserCreationForm, UserEditForm


class WagtailUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        model = CustomUser
        widgets = {'date_of_birth': forms.DateInput(attrs={'type':'date'})}


class WagtailUserEditForm(UserEditForm):
    class Meta(UserEditForm.Meta):
        model = CustomUser
        widgets = {'date_of_birth': forms.DateInput(attrs={'type':'date'})}

Merk op dat we een widget DateInput hebben toegevoegd uit de forms-bibliotheek van Django, waarmee we datums uit een kalender kunnen kiezen.

We zijn nog niet klaar. Wagtail heeft sjablonen create.html en edit.html nodig om deze formulieren te gebruiken en verwacht ze op een bepaalde plaats. We volgen Django's gebruikelijke mapstructuur en plaatsen alle sjablonen voor de app userauth in de directory userauth/templates/userauth. Om Django in staat te stellen deze sjablonen te vinden, voeg het volgende pad toe aan de variabele TEMPLATES in settings/base.py:

os.path.join(BASE_DIR, 'userauth/templates/userauth/'),

Voeg in die map nog twee submappen toe wagtailusers/users en voeg vervolgens de volgende twee templates toe:

{% extends "wagtailusers/users/create.html" %}

{% block extra_fields %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.display_name %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.date_of_birth %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.address1 %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.address2 %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.zip_code %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.city %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.country %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.mobile_phone %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.additional_information %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.photo %}
{% endblock extra_fields %}

Hetzelfde in edit.html, behalve de eerste regel:

{% extends "wagtailusers/users/edit.html" %}

Ten slotte moeten we de volgende parameters toevoegen in onze settings/base.py:

WAGTAIL_USER_CREATION_FORM = 'userauth.forms.WagtailUserCreationForm'
WAGTAIL_USER_EDIT_FORM = 'userauth.forms.WagtailUserEditForm'
WAGTAIL_USER_CUSTOM_FIELDS = ['display_name', 'date_of_birth', 'address1', 'address2', 'zip_code', 'city', 'country', 'mobile_phone', 'additional_information', 'photo',]

Start nu de server op en ga naar http://127.0.0.1:8000/admin om te controleren of alle nieuwe velden er zijn.

Lees meer over inloggen en aanmelden van aangepaste gebruikers en andere authenticatieprocessen.

Reageer op dit artikel (log eerst in of bevestig hieronder met naam en email)