web-dev-qa-db-fra.com

Obtenir le modèle ContentType dans la migration - Django 1.7

J'ai une migration de données qui met à jour certaines autorisations. Je sais qu'il y a des problèmes connus avec les autorisations dans les migrations et j'ai pu éviter certains problèmes en créant les autorisations dans la migration elle-même (plutôt qu'en utilisant le raccourci Tuple dans le modèle).

La migration:

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

Après quelques essais et erreurs, j'ai pu faire ce travail en utilisant manage.py migrate mais je reçois des erreurs dans le test manage.py test.

__fake__.DoesNotExist: ContentType matching query does not exist.

Le débogage a découvert qu'il n'y avait pas de ContentType à ce stade de la migration lors de l'exécution dans test (je ne sais pas pourquoi). En suivant les conseils de ce post j'ai essayé de mettre à jour les types de contenu manuellement dans la migration elle-même. Ajoutée :

from Django.contrib.contenttypes.management import update_contenttypes
update_contenttypes(app, models.get_models())

avant de récupérer le type de contenu pour le modèle Invitation. Vous avez l'erreur suivante

  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'

Il doit y avoir un moyen de créer/mettre à jour les autorisations dans les migrations de données de manière testable.

Merci.

[~ # ~] modifier [~ # ~]

Enfin, cela a fonctionné en ajoutant

from Django.contrib.contenttypes.management import update_all_contenttypes
update_all_contenttypes() 

assez curieusement celui-ci n'était pas suffisant

update_contenttypes(apps.app_configs['contenttypes'])

J'aimerais savoir pourquoi tout cela est nécessaire

40
haki

La réponse est:

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

:) J'en avais besoin moi-même aujourd'hui.

9
gabn88

Avoir un problème similaire lors de l'écriture d'une migration de données qui s'étend sur plusieurs applications. Il s'avère que Django charge uniquement les modèles dans le registre d'application qui sont affectés par ce que le membre "dépendances" de la migration déclare: https://code.djangoproject.com/ticket/243

J'ai dû ajouter une entrée aux dépendances de migration que j'utilise qui n'est pas directement liée par exemple une clé étrangère vers l'application en cours de migration.

7
Sakuraba

Depuis, j'ai fini par y consacrer 3 à 4 heures, j'ajoute ma solution.

Le problème était ContentType et les objets d'autorisation n'étaient pas créés lorsque j'ai exécuté plusieurs migrations ensemble. Étant donné que je faisais référence à ce type de contenu et à la migration lors de la prochaine migration, cela posait problème.)

Cependant, ils fonctionnent bien si je les exécute un par un à l'aide du numéro de migration. (qui ont été référencés lors de futures migrations)

Pour le résoudre, j'ai ajouté une migration supplémentaire entre les deux pour créer des objets ContentType et 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)
    ]
6
mithuntnt

Pour Django 2.1 J'ai dû importer des applications du registre mondial car les applications passées dans la migration étaient des instances de Django.db.migrations.state.AppConfigStub sans remplissage models_module attribut. Et create_contenttypes vérifie cet attribut.

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):
    gls_app_config = global_apps.get_app_config('my_app')
    create_contenttypes(gls_app_config)

    ...
1
Petr Přikryl
update_contenttypes(apps.app_configs['contenttypes'])

mettra à jour les types de contenu de l'application contenttypes.

Je pense que vous voudriez faire ça ...

update_contenttypes(apps.app_configs['app_label']) 

où app_label est le libellé de l'application dans laquelle se trouve le modèle d'invitation. Cela mettra à jour les types de contenu de votre application afin qu'elle soit disponible pour interroger selon votre code d'origine.

1
simondo92