Datamigration and Django 1.7 user groups

I am trying to implement datamigration using the native django 1.7 migration system. Here is what I did.

# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations def create_basic_user_group(apps, schema_editor): """Forward data migration that create the basic_user group """ Group = apps.get_model('auth', 'Group') Permission = apps.get_model('auth', 'Permission') group = Group(name='basic_user') group.save() perm_codenames = ( 'add_stuff', '...', ) # we prefere looping over all these in order to be sure to fetch them all perms = [Permission.objects.get(codename=codename) for codename in perm_codenames] group.permissions.add(*perms) group.save() def remove_basic_user_group(apps, schema_editor): """Backward data migration that remove the basic_user group """ group = Group.objects.get(name='basic_user') group.delete() class Migration(migrations.Migration): """This migrations automatically create the basic_user group. """ dependencies = [ ] operations = [ migrations.RunPython(create_basic_user_group, remove_basic_user_group), ] 

But when I try to migrate, I got a LookupError exception telling me that no applications with the tag 'auth' were found.

How can I create my own groups in pure form, which can also be used in unit tests?

+7
python django migration
source share
3 answers

I did what you are trying to do. Problems:

  • The documentation for 1.7 and 1.8 is very clear: if you want to access the model from another application, you must specify this application as a dependency:

    When writing a RunPython function that uses models from applications other than those in which the migration is located, the migration dependency attribute must include the most recent migration of each application that is involved, otherwise you may receive an error similar to: LookupError: No installed app with label 'myappname' when you try to get the model in the RunPython function using apps.get_model() .

    Thus, you should have a dependency on the latest migration to auth .

  • As you mentioned in comment , you will have a problem where the permissions you want to use are not yet created. The problem is that permissions are created by the signal handler attached to the post_migrate signal. Therefore, the permissions associated with any new model created during the migration are not available until the migration is complete.

    You can fix this by doing this at the beginning of create_basic_user_group :

     from django.contrib.contenttypes.management import update_contenttypes from django.apps import apps as configured_apps from django.contrib.auth.management import create_permissions for app in configured_apps.get_app_configs(): update_contenttypes(app, interactive=True, verbosity=0) for app in configured_apps.get_app_configs(): create_permissions(app, verbosity=0) 

    This will also create content types for each model (which are also created after migration), see below why you should take care of this.

    Perhaps you can be more selective than me in the code above: update only some key applications, and not update all applications. I did not try to be selective. In addition, it is possible that both cycles can be combined into one. I have not tried this with a single loop.

  • You get Permission objects by searching on codename , but codename not guaranteed to be unique. Two applications may have models named Stuff , so you may have add_stuff permission associated with two different applications. If this happens, your code will fail. You should search for codename and content_type , which are guaranteed to be unique together. A unique content_type is associated with each model of the project: two models with the same name, but in different applications will receive two different types of content.

    This means adding a contenttypes application contenttypes and using the ContentType model: ContentType = apps.get_model("contenttypes", "ContentType") .

+5
source share

So, I figure out how to solve this problem, and I get the following output: get_model will only retrieve your model applications. I'm not sure it will be a good grade, but it worked for me.

I just called the model directly and made changes.

 # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations from django.contrib.auth.models import Group def create_groups(apps, schema_editor): g = Group(name='My New Group') g.save() class Migration(migrations.Migration): operations = [ migrations.RunPython(create_groups) ] 

And then just apply the /manage.py migration to complete. Hope this helps.

+1
source share

As stated in https://code.djangoproject.com/ticket/23422 , you must send the post_migrate signal before sending Permission objects.

But on Django there is an auxiliary function for sending the necessary signal: django.core.management.sql.emit_post_migrate_signal

Here he worked as follows:

 # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations from django.core.management.sql import emit_post_migrate_signal PERMISSIONS_TO_ADD = [ 'view_my_stuff', ... ] def create_group(apps, schema_editor): # Workarounds a Django bug: https://code.djangoproject.com/ticket/23422 db_alias = schema_editor.connection.alias try: emit_post_migrate_signal(2, False, 'default', db_alias) except TypeError: # Django < 1.8 emit_post_migrate_signal([], 2, False, 'default', db_alias) Group = apps.get_model('auth', 'Group') Permission = apps.get_model('auth', 'Permission') group, created = Group.objects.get_or_create(name='MyGroup') permissions = [Permission.objects.get(codename=i) for i in PERMISSIONS_TO_ADD] group.permissions.add(*permissions) class Migration(migrations.Migration): dependencies = [ ('auth', '0001_initial'), ('myapp', '0002_mymigration'), ] operations = [ migrations.RunPython(create_group), ] 
+1
source share

All Articles