Ajouter des champs supplémentaires au modèle utilisateur personnalisé en Django

C'est une bonne idée de définir un modèle utilisateur personnalisé dans Django. Dans ce tutoriel, on ajoutera au modèle un certain nombre de champs tels que la date de naissance, l'adresse, le numéro de téléphone. On rendra également tous ces champs modifiables dans l'admin Django et Wagtail.

30 Juin 2020 11:27
Thèmes: Modèle d'utilisateur personnalisé

Jetons d'abord un œil à l'admin. Pour ce faire, on a besoin d'un superutilisateur, alors créez-en un, si vous ne l'avez pas déjà fait:

python3 manage.py createsuperuser

Créez un nom d'utilisateur et un mot de passe et fournissez une adresse e-mail lorsque vous y êtes invité. Dans un projet Wagtail, l'admin Django traditionnel se trouve à http://127.0.0.1:8000/django-admin (sans Wagtail, c'est à … / admin). Si vous vous y connectez, notre modèle d'utilisateur personnalisé n'est pas là. Pour l'identifier dans votre admin, mettez ce qui suit dans le fichier admin.py de votre projet:

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)

UserCreationForm et UserChangeForm nous permettent de créer et de modifier des utilisateurs dans notre admin. L'attribut list_display contrôle les champs affichés dans la vue d'ensemble de l'admin et fieldsets lesquels qui peuvent être créés ou édités. Vérifiez maintenant http://127.0.0.1:8000/django-admin: l'utilisateur personnalisé est visible dans admin et les utilisateurs peuvent être créés et modifiés.

Jusqu'à présent, on n'a rien de plus que les champs utilisateur standard de Django. Il est temps d'ajouter quelques champs supplémentaires dans models.py de l'une de nos applications (appelée userauth). Je vais ajouter tous les champs à la fois et les expliquer ci-dessous.

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}"

Le nom d'affichage (display_name) parle de lui-même. On a ajouté la possibilité de traduire le nom détaillé, en utilisant gettext_lazy. Lorsque vous êtes sûr que vous n'utiliserez qu'une seule langue, vous pouvez bien sûr l'omettre. Dans ce didacticiel, on ajoutera systématiquement la fonctionnalité de traduction pour toutes les phrases 'lisibles par l'homme'. Le champ date de naissance n'est pas obligatoire, comme l'indiquent les paramètres blank = True, null = True. Les champs d'adresse parlent d'eux-mêmes, à l'exception de CountryField. Ce n'est pas Django standard, il est importé d'un paquet appelé django-countries. Cela nous permet de choisir un pays dans une liste de tous les pays du monde, au lieu de simplement taper le pays dans un champ de texte. On installe le package avec

pip3 install django-countries

Ajoutez django_countries à la liste des INSTALLED_APPS dans settings/base.py et ajoutez-le également à requirements.txt. Le champ phone_regex est un RegexValidator dont le modèle correspond aux numéros de téléphone internationaux. Bien sûr, cela ne garantit pas que le numéro de téléphone est valide, mais il offre une certaine protection contre la saisie erronée. Le champ d'expression régulière est introduit dans le champ de téléphone portable .

Le Django ImageFieldstandard place les images téléchargées dans le répertoire indiqué par le champ upload_to, préfixé par le paramètre MEDIA_ROOT dans les paramètres. Puisque notre racine média est notre répertoire de base plus media, les images téléchargées seront placées dans /media/photos/. On a également ajouté une image par défaut, dans le répertoire photos/. En raison de la façon dont Django gère les fichiers , il préfixera MEDIA_ROOT sur le chemin de l'image par défaut. Cela signifie qu'on doit placer notre image par défaut dans le même répertoire /media/photos/. Le placement manuel de fichiers dans MEDIA_ROOT est une exception, car il n'est normalement utilisé que pour le téléchargement de fichiers; il existe d'autres façons de fournir une image par défaut si vous voulez éviter cela.

On a également défini un ordre de nos utilisateurs (par nom de famille) et une représentation, utile lors de la visualisation des utilisateurs en admin. Les champs username, last_name et first_name sont mis à notre disposition via le modèle AbstractUser.

Notre modèle d'utilisateur est terminé, on doit maintenant ajouter les champs supplémentaires à notre admin. Tout d'abord, dans CustomUserAdmin dans admin.py, ajoutez les champs supplémentaires à add_fieldsets et fieldsets:

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',)}),
)

On doit migrer notre base de données pour intégrer tous les nouveaux champs:

python3 manage.py makemigrations
python3 manage.py migrate

Lorsqu'on exécute maintenant le serveur et qu'on va à l'admin Django, on voit que tous les champs sont là!

Il est temps de regarder l'admin Wagtail à http://127.0.0.1:8000/admin. Seuls les champs standard sont là maintenant, on doit donc ajouter les champs supplémentaires. Wagtail a également un UserCreationForm et un UserEditForm (nom légèrement différent ici), on ajoute donc ce qui suit à 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'})}

Notez qu'on a ajouté un widget DateInput de la bibliothèque forms de Django, qui nous permet de choisir des dates dans un calendrier.

On n'a pas encore fini. Wagtail a besoin de modèles create.html et edit.html pour utiliser ces formulaires et les attend à un certain endroit. On suivra la structure de répertoire de modèles de Django et placerons tous les modèles de l'application userauth dans le répertoire userauth/templates/userauth. Pour permettre à Django de trouver ces modèles, ajoutez le chemin suivant à la variable TEMPLATES dans settings/base.py:

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

Dans ce répertoire, ajoutez deux autres sous-répertoires wagtailusers/users, puis ajoutez les deux modèles suivants:

{% 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 %}

Idem dans edit.html, sauf pour la première ligne:

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

Enfin, on doit ajouter les paramètres suivants dans notre 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',]

Maintenant, exécutez le serveur et accédez à http://127.0.0.1:8000/admin pour vérifier que tous les nouveaux champs sont là!

Lisez plus sur la connexion et l'inscription d'utilisateurs personnalisés et d'autres processus d'authentification.

Commentez cet article (connectez-vous d'abord ou confirmez par nom et email ci-dessous)