在迁移中获取模型 ContentType - Django 1.7
Posted
技术标签:
【中文标题】在迁移中获取模型 ContentType - Django 1.7【英文标题】:Getting model ContentType in migration - Django 1.7 【发布时间】:2014-12-15 09:18:02 【问题描述】:我有一个更新一些权限的数据迁移。我知道迁移中的权限存在一些已知问题,并且我能够通过在迁移中自行创建权限来避免一些麻烦(而不是使用模型中的元组快捷方式)。
迁移:
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
def create_feature_groups(apps, schema_editor):
app = models.get_app('myauth')
Group = apps.get_model("auth", "Group")
pro = Group.objects.create(name='pro')
Permission = apps.get_model("auth", "Permission")
ContentType = apps.get_model("contenttypes", "ContentType")
invitation_contenttype = ContentType.objects.get(name='Invitation')
send_invitation = Permission.objects.create(
codename='send_invitation',
name='Can send Invitation',
content_type=invitation_contenttype)
pro.permissions.add(receive_invitation)
class Migration(migrations.Migration):
dependencies = [
('myauth', '0002_initial_data'),
]
operations = [
migrations.RunPython(create_feature_groups),
]
经过反复试验,我能够使用manage.py migrate
完成这项工作,但我在测试manage.py test
中遇到错误。
__fake__.DoesNotExist: ContentType matching query does not exist.
调试了一下发现在测试中运行时迁移此时没有ContentType
(不知道为什么)。按照post 中的建议,我尝试在它自己的迁移中手动更新内容类型。补充:
from django.contrib.contenttypes.management import update_contenttypes
update_contenttypes(app, models.get_models())
在获取 Invitation
模型的内容类型之前。出现以下错误
File "C:\Python27\lib\site-packages\django-1.7-py2.7.egg\django\contrib\contenttypes\management.py", line 14, in update_contenttypes
if not app_config.models_module:
AttributeError: 'module' object has no attribute 'models_module'
必须有某种方法以可测试的方式在数据迁移中创建/更新权限。
谢谢。
编辑
终于通过添加让它工作了
from django.contrib.contenttypes.management import update_all_contenttypes
update_all_contenttypes()
奇怪的是,这个还不够
update_contenttypes(apps.app_configs['contenttypes'])
我很想知道为什么所有这些都是必要的
【问题讨论】:
对于 Django 1.8 上想要 update_all_contenttypes 的人,请参考这个问题:***.com/questions/29550102/… 我发誓,在与 Django 合作的 3 年中,我从来没有像处理这个问题那样讨厌它(好吧,我在撒谎,还有其他粗糙的补丁在我们过去的关系中也是如此)。无论如何,这些 Q/A 被标记为圣杯,非常感谢! 保持坚强塞巴斯蒂安???? 【参考方案1】:from django.db import migrations
from django.db.migrations import RunPython
from django.apps.registry import Apps, apps as global_apps
from django.contrib.contenttypes.management import create_contenttypes
def add_content_type_records(apps: Apps, schema_editor):
my_app_config = global_apps.get_app_config('my_1_app')
my_app_config.models_module = True
create_contenttypes(my_app_config)
my_app_config.models_module = None
my_app_config = global_apps.get_app_config('my_2_app')
my_app_config.models_module = True
create_contenttypes(my_app_config)
my_app_config.models_module = None
def create_setup_data(apps, schema_editor):
...
def delete_setup_data(apps, schema_editor):
...
class Migration(migrations.Migration):
dependencies = [
('my_1_app', '....'),
('my_2_app', '....'),
('contenttypes', '__latest__'),
]
operations = [
RunPython(add_content_type_records, RunPython.noop),
RunPython(create_setup_data, delete_setup_data),
]
【讨论】:
【参考方案2】:对于 Django 2.1,我必须从全局注册表中导入应用程序,因为传递到迁移中的应用程序是 django.db.migrations.state.AppConfigStub
的实例,而没有填充 models_module
属性。而create_contenttypes
正在检查这个属性。
from django.apps.registry import Apps, apps as global_apps
from django.contrib.contenttypes.management import create_contenttypes
from django.db import migrations
def add_permision(apps: Apps, schema_editor):
my_app_config = global_apps.get_app_config('my_app')
create_contenttypes(my_app_config)
...
【讨论】:
请注意,如果您运行需要存在内容类型的数据迁移,这是必要的,例如在测试数据库中,内容类型表最初是空的。要实际获取数据迁移中的内容类型,您可以使用来自 gabn88 的答案。 这对我来说效果很好!除了测试之外,我的用例是新用户尝试从头开始迁移项目。【参考方案3】:答案是:
apps.get_model('contenttypes', 'ContentType')
:) 我自己今天需要它。
【讨论】:
【参考方案4】:因为,我最终花费了 3-4 个小时来添加我的解决方案。
问题是当我一起运行多个迁移时,没有创建 ContentType 和 Permission 对象。由于我在下一次迁移中引用了这些内容类型和迁移,这导致了问题。)
但是,如果我使用迁移编号一一运行它们,它们就可以正常工作。 (在未来的迁移中被引用)
为了解决这个问题,我在两者之间添加了一个额外的迁移来创建 ContentType 和 Permission 对象。
# -*- coding: utf-8 -*-
# Generated by Django 1.10.6 on 2017-03-11 05:59
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations
def update_all_contenttypes(**kwargs):
from django.apps import apps
from django.contrib.contenttypes.management import update_contenttypes
for app_config in apps.get_app_configs():
update_contenttypes(app_config, **kwargs)
def create_all_permissions(**kwargs):
from django.contrib.auth.management import create_permissions
from django.apps import apps
for app_config in apps.get_app_configs():
create_permissions(app_config, **kwargs)
def forward(apps, schema_editor):
update_all_contenttypes()
create_all_permissions()
def backward(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0002_remove_content_type_name'),
('MY_APP', '0123_LAST_MIGRATION'),
]
operations = [
migrations.RunPython(forward, backward)
]
【讨论】:
使用 Django 1.11 update_contenttypes 不再存在,改用 create_contenttypes【参考方案5】:update_contenttypes(apps.app_configs['contenttypes'])
将更新 contenttypes 应用的内容类型。
我相信你会想要这样做...
update_contenttypes(apps.app_configs['app_label'])
其中 app_label 是邀请模型所在应用的应用标签。这将更新您的单个应用的内容类型,以便可以根据您的原始代码进行查询。
【讨论】:
【参考方案6】:在编写跨多个应用的数据迁移时遇到类似问题。结果 Django 仅将那些受迁移状态的“依赖项”成员影响的模型加载到应用注册表中:https://code.djangoproject.com/ticket/24303
必须基本上向我使用的迁移依赖项添加一个条目,该条目与例如不直接相关。当前正在迁移的应用程序的 ForeignKey。
【讨论】:
我不明白。您如何确定需要添加哪些(不相关的)迁移作为模型可用的依赖项? 基本上我添加了我想在数据迁移过程中使用其模型的所有应用程序。 除了运行 update_all_contenttypes(感谢您的提示)之外,您还应该包含 ('contenttypes', '0001_initial') 作为迁移依赖项。以上是关于在迁移中获取模型 ContentType - Django 1.7的主要内容,如果未能解决你的问题,请参考以下文章
如何在 laravel 中获取所有模型/迁移名称并添加单个新列