43

我有一个更新一些权限的数据迁移。我知道迁移中的权限存在一些已知问题,并且我能够通过在迁移中创建权限来避免一些麻烦(而不是使用模型中的元组快捷方式)。

迁移:

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在测试中运行时,此时迁移中没有(不知道为什么)。按照这篇文章中的建议,我尝试在它自己的迁移中手动更新内容类型。添加 :

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'])

我很想知道为什么所有这些都是必要的

4

6 回答 6

13

对于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)

    ...
于 2019-09-30T14:17:33.130 回答
11

答案是:

apps.get_model('contenttypes', 'ContentType') 

:) 今天我自己需要它。

于 2018-07-16T17:49:47.487 回答
7

在编写跨多个应用程序的数据迁移时遇到类似的问题。结果 Django 仅将那些受迁移状态的“依赖项”成员影响的模型加载到应用程序注册表中:https ://code.djangoproject.com/ticket/24303

必须基本上向我使用的迁移依赖项添加一个条目,该条目与当前正在迁移的应用程序没有直接关系,例如 ForeignKey。

于 2015-02-12T13:27:48.437 回答
7

因为,我最终花了 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)
    ]
于 2017-03-14T16:19:30.953 回答
1
update_contenttypes(apps.app_configs['contenttypes'])

将更新 contenttypes 应用的内容类型。

我相信你会想要这样做...

update_contenttypes(apps.app_configs['app_label']) 

其中 app_label 是邀请模型所在的应用程序的应用程序标签。这将更新您的单个应用程序的内容类型,以便可以根据您的原始代码进行查询。

于 2016-06-28T10:01:54.950 回答
0
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),
    ]
于 2020-08-27T19:08:18.800 回答