From 21d3deb97301a34979dffc6e840db6f2fff25a11 Mon Sep 17 00:00:00 2001
From: Paul Marillonnet <pmarillonnet@entrouvert.com>
Date: Fri, 24 Dec 2021 10:28:36 +0100
Subject: [PATCH 1/3] custom_user: add base models for subprofile management
 (#58554)

---
 .../migrations/0028_profile_profiletype.py    | 85 +++++++++++++++++++
 src/authentic2/custom_user/models.py          | 42 +++++++++
 2 files changed, 127 insertions(+)
 create mode 100644 src/authentic2/custom_user/migrations/0028_profile_profiletype.py

diff --git a/src/authentic2/custom_user/migrations/0028_profile_profiletype.py b/src/authentic2/custom_user/migrations/0028_profile_profiletype.py
new file mode 100644
index 00000000..89aa21e0
--- /dev/null
+++ b/src/authentic2/custom_user/migrations/0028_profile_profiletype.py
@@ -0,0 +1,85 @@
+# Generated by Django 2.2.19 on 2022-01-06 10:17
+
+import django.contrib.postgres.fields.jsonb
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+import authentic2.utils.misc
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('custom_user', '0027_user_deactivation_reason'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ProfileType',
+            fields=[
+                (
+                    'id',
+                    models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+                ),
+                (
+                    'uuid',
+                    models.CharField(
+                        default=authentic2.utils.misc.get_hex_uuid,
+                        editable=False,
+                        max_length=32,
+                        unique=True,
+                        verbose_name='UUID',
+                    ),
+                ),
+                ('name', models.CharField(max_length=128, verbose_name='name')),
+                ('slug', models.SlugField(max_length=256, unique=True, verbose_name='slug')),
+            ],
+            options={
+                'verbose_name': 'profile type',
+                'verbose_name_plural': 'profile types',
+                'ordering': ('name', 'slug'),
+            },
+        ),
+        migrations.CreateModel(
+            name='Profile',
+            fields=[
+                (
+                    'id',
+                    models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+                ),
+                (
+                    'data',
+                    django.contrib.postgres.fields.jsonb.JSONField(
+                        blank=True, null=True, verbose_name='profile data'
+                    ),
+                ),
+                (
+                    'profile_type',
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name='profiles',
+                        to='custom_user.ProfileType',
+                        verbose_name='profile type',
+                    ),
+                ),
+                (
+                    'user',
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name='subprofiles',
+                        to=settings.AUTH_USER_MODEL,
+                        verbose_name='user',
+                    ),
+                ),
+            ],
+            options={
+                'verbose_name': 'profile',
+                'verbose_name_plural': 'profiles',
+                'ordering': ('user', 'profile_type'),
+                'unique_together': {('user', 'profile_type')},
+            },
+        ),
+    ]
diff --git a/src/authentic2/custom_user/models.py b/src/authentic2/custom_user/models.py
index 366e42af..5419f383 100644
--- a/src/authentic2/custom_user/models.py
+++ b/src/authentic2/custom_user/models.py
@@ -41,6 +41,7 @@ from authentic2.models import Attribute, AttributeValue, Service
 from authentic2.utils import misc as utils_misc
 from authentic2.validators import email_validator
 from django_rbac.models import PermissionMixin
+from django_rbac.utils import generate_slug
 
 from .managers import UserManager, UserQuerySet
 
@@ -460,3 +461,44 @@ class DeletedUser(models.Model):
         verbose_name = _('deleted user')
         verbose_name_plural = _('deleted users')
         ordering = ('deleted', 'id')
+
+
+class ProfileType(models.Model):
+    uuid = models.CharField(
+        verbose_name=_('UUID'), max_length=32, default=utils_misc.get_hex_uuid, editable=False, unique=True
+    )
+    name = models.CharField(max_length=128, verbose_name=_('name'))
+    slug = models.SlugField(verbose_name=_('slug'), max_length=256, unique=True)
+
+    def save(self, *args, **kwargs):
+        if not self.slug:
+            self.slug = generate_slug(self.name)
+        if len(self.slug) > 256:
+            self.slug = self.slug[:256]
+        return super().save(*args, **kwargs)
+
+    class Meta:
+        verbose_name = _('profile type')
+        verbose_name_plural = _('profile types')
+        ordering = ('name', 'slug')
+
+
+class Profile(models.Model):
+    profile_type = models.ForeignKey(
+        verbose_name=_('profile type'),
+        to=ProfileType,
+        related_name='profiles',
+        blank=True,
+        null=True,
+        on_delete=models.CASCADE,
+    )
+    user = models.ForeignKey(
+        to=User, verbose_name=_('user'), related_name='subprofiles', on_delete=models.CASCADE
+    )
+    data = JSONField(verbose_name=_('profile data'), null=True, blank=True)
+
+    class Meta:
+        unique_together = (('user', 'profile_type'),)
+        verbose_name = _('profile')
+        verbose_name_plural = _('profiles')
+        ordering = ('user', 'profile_type')
-- 
2.34.1

