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?
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
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
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.
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()),
],
),
]