web-dev-qa-db-fra.com

Comment étendre le modèle de groupe Django?

Existe-t-il un moyen d'étendre l'objet intégré Django Group pour ajouter des attributs supplémentaires similaires à la façon dont vous pouvez étendre un objet utilisateur? Avec un objet utilisateur, vous pouvez effectuer les opérations suivantes:

class UserProfile(models.Model):
    user = models.OneToOneField(User)

et ajoutez ce qui suit au fichier settings.py

AUTH_PROFILE_MODULE = 'app.UserProfile'

ce qui vous donne:

profile = User.objects.get(id=1).get_profile()

Existe-t-il un équivalent à cette approche pour l'extension d'un groupe? Sinon, existe-t-il une autre approche que je peux adopter?

41
Steve Platz

Vous pouvez créer un modèle qui sous-classe le groupe, ajouter vos propres champs et utiliser un gestionnaire de modèles pour renvoyer tous les ensembles de requêtes personnalisés dont vous avez besoin. Voici un exemple tronqué montrant comment j'ai étendu le groupe pour représenter les familles associées à une école:

from Django.contrib.auth.models import Group, User

class FamilyManager(models.Manager):
    """
    Lets us do querysets limited to families that have 
    currently enrolled students, e.g.:
        Family.has_students.all() 
    """
    def get_query_set(self):
        return super(FamilyManager, self).get_query_set().filter(student__enrolled=True).distinct()


class Family(Group):
    notes = models.TextField(blank=True)

    # Two managers for this model - the first is default 
    # (so all families appear in the admin).
    # The second is only invoked when we call 
    # Family.has_students.all()  
    objects = models.Manager()
    has_students = FamilyManager()

    class Meta:
        verbose_name_plural = "Families"
        ordering = ['name']

    def __unicode__(self):
        return self.name
25
shacker

Si vous sous-classe simplement l'objet Groupe, par défaut, il créera une nouvelle table de base de données et le site d'administration ne récupérera aucun nouveau champ.

Vous devez injecter de nouveaux champs dans le groupe existant:

if not hasattr(Group, 'parent'):
    field = models.ForeignKey(Group, blank=True, null=True, related_name='children')
    field.contribute_to_class(Group, 'parent')

Pour ajouter des méthodes au groupe, sous-classe mais marquez le modèle comme proxy:

class MyGroup(Group):

    class Meta:
        proxy = True

    def myFunction(self):
        return True
52
Semprini

Pour moi une solution travaillée basée sur:

https://docs.djangoproject.com/pl/1.11/topics/auth/customizing/#extending-user

Permettez-moi d'expliquer ce que j'ai fait avec les groupes étendant le modèle par défaut avec l'alias de messagerie:

Tout d'abord, j'ai créé ma propre application Django let name it

python manage.py startapp auth_custom

Section de code:

Dans auth_custom/models.py J'ai créé un objet CustomGroup

from Django.contrib.auth.models import Group
from Django.db import models

class CustomGroup(models.Model):
        """
        Overwrites original Django Group.
        """
        def __str__(self):
            return "{}".format(self.group.name)

        group = models.OneToOneField('auth.Group', unique=True)
        email_alias = models.EmailField(max_length=70, blank=True, default="")

Dans auth_custom/admin.py:

from Django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
from Django.contrib.auth.models import Group


class GroupInline(admin.StackedInline):
    model = CustomGroup
    can_delete = False
    verbose_name_plural = 'custom groups'


class GroupAdmin(BaseGroupAdmin):
    inlines = (GroupInline, )


# Re-register GroupAdmin
admin.site.unregister(Group)
admin.site.register(Group, GroupAdmin)

Après avoir effectué des migrations, j'ai un tel résultat dans Django Admin view.

Groupe personnalisé dans Django Admin

Pour accéder à ce champ personnalisé, vous devez taper:

from Django.contrib.auth.models import Group


    group = Group.objects.get(name="Admins")  # example name

    email_alias = group.customgroup.email_alias

En cas d'erreur, veuillez m'en informer, je corrigerai cette réponse.

7
rozacek

J'ai réussi à utiliser les migrations avec @Semprini aswer.

Je devais donc créer un domaine lié à l'entreprise dans le domaine lié à mes groupes, donc dans mes modèles, j'ai fait ceci:

if not hasattr(Group, 'company'):
    field = models.ForeignKey(Company, on_delete=models.DO_NOTHING, null=True)
    field.contribute_to_class(Group, 'company')


class Group(Group):

    class Meta:
        proxy = True

Ensuite, je lance des makemigrations manage.py. Cela a créé 2 fichiers. L'une avec des dépendances sur l'autre, mais la première appartenant à l'application auth a été créée dans mon environnement virtuel. Les fichiers ressemblent à ceci:

# Generated by Django 2.2.5 on 2019-10-08 16:00

from Django.db import migrations, models
import Django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0013_guestuser_permissions_20190919_1715'),
        ('auth', '0011_update_proxy_permissions'),
    ]

    operations = [
        migrations.AddField(
            model_name='group',
            name='company',
            field=models.ForeignKey(
                null=True, on_delete=Django.db.models.deletion.DO_NOTHING, to='myapp.Company'),
        ),
    ]

Le second créé dans le dossier de migrations myapp ressemble à ceci:

# Generated by Django 2.2.5 on 2019-10-08 16:00

import Django.contrib.auth.models
from Django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('auth', '0012_group_company_20191008'),
        ('myapp', '0013_guestuser_permissions_20190919_1715'),
    ]

    operations = [
        migrations.CreateModel(
            name='Group',
            fields=[
            ],
            options={
                'proxy': True,
                'indexes': [],
                'constraints': [],
            },
            bases=('auth.group',),
            managers=[
                ('objects', Django.contrib.auth.models.GroupManager()),
            ],
        ),
    ]

La solution a donc été de déplacer le fichier créé dans mon virtualenv vers le dossier de migrations myapp, avant celui généré avec makemigrations, mais comme la migration est appliquée à l'application auth au lieu de myapp j'ai pour implémenter une solution de contournement dans le fichier. Le fichier final est donc maintenant:

# Generated by Django 2.2.5 on 2019-10-08 16:00

from Django.db import migrations, models
import Django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0013_guestuser_permissions_20190919_1715'),
        ('auth', '0011_update_proxy_permissions'),
    ]

    operations = [
        migrations.AddField(
            model_name='group',
            name='company',
            field=models.ForeignKey(
                null=True, on_delete=Django.db.models.deletion.DO_NOTHING, to='myapp.Company'),
        ),
    ]

    def mutate_state(self, project_state, preserve=True):
        """
        This is a workaround that allows to store ``auth``
        migration outside the directory it should be stored.
        """
        app_label = self.app_label
        self.app_label = 'auth'
        state = super(Migration, self).mutate_state(project_state, preserve)
        self.app_label = app_label
        return state

    def apply(self, project_state, schema_editor, collect_sql=False):
        """
        Same workaround as described in ``mutate_state`` method.
        """
        app_label = self.app_label
        self.app_label = 'auth'
        state = super(Migration, self).apply(project_state, schema_editor, collect_sql)
        self.app_label = app_label
        return state

Les méthodes mutate an apply vous permettent de migrer vers l'application auth à partir des migrations myapp.

Dans le deuxième fichier, je change simplement la dépendance pour dépendre du nouveau fichier créé:

# Generated by Django 2.2.5 on 2019-10-08 16:00

import Django.contrib.auth.models
from Django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0014_group_company_20191008'),
        ('myapp', '0013_guestuser_permissions_20190919_1715'),
    ]

    operations = [
        migrations.CreateModel(
            name='Group',
            fields=[
            ],
            options={
                'proxy': True,
                'indexes': [],
                'constraints': [],
            },
            bases=('auth.group',),
            managers=[
                ('objects', Django.contrib.auth.models.GroupManager()),
            ],
        ),
    ]

0
CardCaptor