Как расширить модель Django Group?
Есть ли способ расширить встроенный объект Django Group для добавления дополнительных атрибутов, похожих на способ расширения пользовательского объекта? С помощью объекта пользователя вы можете сделать следующее:
class UserProfile(models.Model):
user = models.OneToOneField(User)
и добавьте следующее в файл settings.py
AUTH_PROFILE_MODULE = 'app.UserProfile'
который получает вас:
profile = User.objects.get(id=1).get_profile()
Существует ли какой-либо эквивалент этого подхода для расширения группы? Если нет, есть ли альтернативный подход, который я могу взять?
Ответы
Ответ 1
Вы можете создать модель, которая подклассифицирует группу, добавить свои собственные поля и использовать диспетчер моделей для возврата любых настраиваемых запросов. Здесь показан усеченный пример, показывающий, как я расширил группу для представления семейств, связанных со школой:
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
Ответ 2
Если вы просто подклассифицируете объект Group, тогда по умолчанию он создаст новую таблицу базы данных, а сайт администратора не получит никаких новых полей.
Вам нужно ввести новые поля в существующую группу:
if not hasattr(Group, 'parent'):
field = models.ForeignKey(Group, blank=True, null=True, related_name='children')
field.contribute_to_class(Group, 'parent')
Чтобы добавить методы в группу, подкласс, но пометить модель как прокси:
class MyGroup(Group):
class Meta:
proxy = True
def myFunction(self):
return True
Ответ 3
Для меня сработало решение на основе:
https://docs.djangoproject.com/pl/1.11/topics/auth/customizing/#extending-user
Позвольте мне объяснить, что я сделал с группами, расширяющими модель по умолчанию с помощью псевдонима электронной почты:
Прежде всего, я создал собственное приложение django, назовите его
python manage.py startapp auth_custom
Раздел кода:
В auth_custom/models.py я создал объект 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="")
В 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)
После выполнения миграций у меня появляется такой результат в представлении администратора Django.
Пользовательская группа в Django Admin
Чтобы получить доступ к этому настраиваемому полю, вы должны ввести:
from django.contrib.auth.models import Group
group = Group.objects.get(name="Admins") # example name
email_alias = group.customgroup.email_alias
Если есть ошибки, пожалуйста, сообщите мне, я исправлю этот ответ.
Ответ 4
Мне удалось использовать миграции с @Semprini aswer.
Поэтому мне нужно было создать поле, связанное с компанией, в поле, связанном с моими группами, поэтому в моих моделях я сделал это:
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
Затем я запускаю make.migrations. Это создало 2 файла. Один с зависимостями от другого, но первый, принадлежащий приложению auth
, был создан внутри моей виртуальной среды. Файлы выглядят так:
# 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'),
),
]
Второй файл, созданный в папке миграций myapp, выглядит следующим образом:
# 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()),
],
),
]
Таким образом, решение состояло в том, чтобы переместить файл, созданный в моем virtualenv, в папку миграций myapp, прежде чем другой файл, сгенерированный с помощью makemigrations, но поскольку миграция применяется к приложению auth
вместо myapp
, мне нужно реализовать Обходной путь в файле. Итак, окончательный файл теперь:
# 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
Методы mutate и apply позволяют выполнять миграцию в приложение auth
из миграций myapp
.
Во втором файле я просто изменяю зависимости, чтобы зависеть от вновь созданного файла:
# 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()),
],
),
]