django: data migrate permissions
Asked Answered
R

3

8

I have a bunch of new permissions which I need to migrate. I tried doing it through data migration but complains about ContentType not being available.

Doing quick research I found out that ContentType table is populated after all the migrations applied.

I even tried using update_all_contenttypes() from from django.contrib.contenttypes.management import update_all_contenttypes which causes migration to load data which is not consistent to the fixture.

What is the best way to migrate permission data in Django?

Rudderpost answered 27/3, 2015 at 9:1 Comment(4)
Can you paste your models and migration?Chime
I have the same problem. Migration looks like this: pastebin.com/avmfXHmY ; INSTALLED_APPS = ('svsite', ..., 'django.contrib.contenttypes', '...', 'member') and the user just extends AbstractUser.Lection
It seems it would fail for anything though. All Permissions need ContentType instances, which don't exists during migration...Lection
Some interesting issues: code.djangoproject.com/ticket/23422, code.djangoproject.com/ticket/29843Battlement
V
12

There are 2 ways to solve this:

1) The ugly way:

Run manage.py migrate auth before your wanted migration

2) Recommended way:

from django.contrib.auth.management import create_permissions

def add_permissions(apps, schema_editor):
    apps.models_module = True

    create_permissions(apps, verbosity=0)
    apps.models_module = None

    # rest of code here....
Viridi answered 14/12, 2015 at 17:3 Comment(1)
Also see source for django.contrib.auth.management.create_permissionsBattlement
A
11

Here is a quick and dirty way to ensure all permissions for all apps have been created:

def add_all_permissions(apps=None, schema_editor=None):
    from django.contrib.auth.management import create_permissions

    if apps is None:
        from django.apps import apps

    for app_config in apps.get_app_configs():
        app_config.models_module = True
        create_permissions(app_config, verbosity=0)
        app_config.models_module = None

class Migration(migrations.Migration):
    dependencies = [('myapp', '0123_do_the_thing')]
    operations = [
        migrations.RunPython(add_all_permissions, 
                             reverse_code=migrations.RunPython.noop)
        # ...
    ]

NOTE: edited to include ruohola's excellent suggestion

Andro answered 4/5, 2017 at 18:32 Comment(3)
Where would you add this?Durstin
This would go in a data migration (anywhere in the file) and get called at the beginning of a RunPython methodAndro
Better to make it so def add_all_permissions(apps, schema_editor):, and then there is no need to import apps. Then one can just use migrations.RunPython(code=add_all_permission).Behre
P
5

Here are steps for adding custom permissions to the User model:

First create a migration file, for example under your authentication application,

Here i named it 0002_permission_fixtures.py:

account (your authentication application)
 |_migrations
   |__ 0001_initial.py
   |__ 0002_permission_fixtures.py
   |__ __init__.py

Then adding your permission objects, as follow:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations


def forwards_func(apps, schema_editor):
    # Get models that we needs them
    user = apps.get_model("auth", "User")
    permission = apps.get_model("auth", "Permission")
    content_type = apps.get_model("contenttypes", "ContentType")
    # Get user content type object
    uct = content_type.objects.get_for_model(user)
    db_alias = schema_editor.connection.alias
    # Adding your custom permissions to User model: 
    permission.objects.using(db_alias).bulk_create([
        permission(codename='add_sample', name='Can add sample', content_type=uct),
        permission(codename='change_sample', name='Can change sample', content_type=uct),
        permission(codename='delete_sample', name='Can delete sample', content_type=uct),
    ])


class Migration(migrations.Migration):
    dependencies = [
    ('contenttypes', '__latest__'),
            ]

    operations = [
        migrations.RunPython(
            forwards_func,
        ),
    ]

To run this migration, first migrate contenttype model, and then migrate your application (here is account).

$ python manage.py migrate contenttypes
$ python manage.py migrate account
Ploy answered 21/9, 2015 at 23:20 Comment(1)
Wouldn't it be better to use "Sample" content type as opposed to User content type?Grass

© 2022 - 2024 — McMap. All rights reserved.