如何扩展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()
那么,有没有类似的方法可以用来扩展组(Group)呢?如果没有,还有其他的办法吗?
5 个回答
12
我成功地使用了@Semprini的回答来进行迁移。
我需要在我的组相关字段中创建一个与公司相关的字段,所以在我的模型中我这样做了:
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
然后我运行了manage.py makemigrations。这创建了两个文件。一个文件依赖于另一个,但第一个文件属于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'),
),
]
第二个文件是在我的应用的迁移文件夹中创建的,样子是这样的:
# 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()),
],
),
]
所以解决办法是把在我的虚拟环境中创建的文件移动到我的应用的迁移文件夹中,放在用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方法允许你从myapp
的迁移迁移到auth
应用。
在第二个文件中,我只是把依赖关系改成依赖于新创建的文件:
# 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()),
],
),
]
58
如果你只是简单地继承了Group对象,那么默认情况下,它会创建一个新的数据库表,而管理后台不会识别到任何新字段。
你需要把新字段添加到现有的Group中:
if not hasattr(Group, 'parent'):
field = models.ForeignKey(Group, blank=True, null=True, related_name='children')
field.contribute_to_class(Group, 'parent')
要给Group添加方法,可以继承它,但要把模型标记为代理:
class MyGroup(Group):
class Meta:
proxy = True
def myFunction(self):
return True
31
你可以创建一个模型,继承自Group,然后添加你自己的字段,还可以使用模型管理器来返回你需要的任何自定义查询集。下面是一个简化的例子,展示了我是如何扩展Group来表示与学校相关的家庭的:
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