2014-10-01 12 views
7

Próbuję zaimplementować datamigration przy użyciu natywnego systemu migracji django 1.7. Oto, co zrobiłem.Django 1.7 datamigracja i grupy użytkowników

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

Ale gdy próbuję uruchomić migrację, mam wyjątek LookupError mówi mi, że żadna aplikacja z etykietą „auth” można znaleźć.

Jak mogę utworzyć moje grupy w czysty sposób, który może być również użyty w testach jednostkowych?

+1

Spróbuj 'app.get_registered_model' i/lub zależeć od' ("auth", "group") ". To jest rodzaj losowej porady, ponieważ sam jestem w trakcie rozumienia rejestru. Pomogło mi to rozwiązać podobny problem. –

+1

W django 1.8 mananagers obiektów mogą być dostępne podczas migracji. W szczególności teraz Twój kod powinien działać tak jak jest –

Odpowiedz

1

Więc, wymyślę, jak rozwiązać ten problem i otrzymuję następujące wyjście: get_model pobierze tylko Twoje aplikacje modelu. Nie mam pewności, czy to byłaby dobra robota, ale zadziałało to dla mnie.

Po prostu wywołałem model Bezpośrednio i wprowadziłem zmiany.

# -*- 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) 
    ] 

A następnie, po prostu zastosuj migrację /manage.py, aby zakończyć. Mam nadzieję, że to pomaga.

+1

Działa to, ale nie możesz dodawać do niego uprawnień, ponieważ są one tworzone na podstawie sygnałów po migracji. W rzeczywistości będziesz myślał, że działa, jeśli zmigrujesz istniejącą bazę danych, ponieważ obiekty uprawnień zostały utworzone podczas poprzedniej migracji. Ale zakończy się niepowodzeniem w przypadku migracji kierowanej na nową i pustą bazę danych. – Egg

5

Zrobiłem to, co próbujesz zrobić. Problemy są:

  1. Dokumentacja 1.7 i 1.8 jest dość jasne: Jeśli chcesz uzyskać dostęp do modelu z innej aplikacji, należy wymienić tę aplikację jako zależność:

    podczas pisania RunPython funkcja korzystająca z modeli z aplikacji innych niż ta, w której zlokalizowana jest migracja, atrybut zależności migracyjnych powinien uwzględniać najnowszą migrację każdej aplikacji, w przeciwnym razie może wystąpić błąd podobny do: LookupError: No installed app with label 'myappname' przy próbie pobrania model w funkcji RunPython za pomocą apps.get_model().

    Powinieneś mieć zależność od ostatniej migracji w auth.

  2. Jak wspomniano w artykule comment, pojawi się problem, w wyniku którego uprawnienia, których chcesz użyć, nie zostaną jeszcze utworzone. Problem polega na tym, że uprawnienia są tworzone przez moduł obsługi sygnału dołączony do sygnału post_migrate. Tak więc uprawnienia związane z dowolnym modelem utworzonym podczas migracji są niedostępne do czasu zakończenia migracji .

    Można rozwiązać ten problem w ten sposób na początku 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) 
    

    Będzie to również tworzyć typy treści dla każdego modelu (które są także stworzone po migracji), patrz poniżej, aby dlaczego warto się tym przejmować.

    Być może możesz być bardziej selektywny niż ja w powyższym kodzie: zaktualizuj tylko niektóre kluczowe aplikacje, zamiast aktualizować wszystkie aplikacje. Nie próbowałem być selektywny. Możliwe też, że obie pętle można połączyć w jedną. Nie próbowałem tego z jedną pętlą.

  3. Otrzymujesz swoje obiekty Permission przez wyszukiwanie przez codename, ale codename nie może być unikatowe.Dwie aplikacje mogą mieć modele o nazwie Stuff, więc możesz mieć uprawnienie add_stuff powiązane z dwiema różnymi aplikacjami. Jeśli tak się stanie, Twój kod się nie powiedzie. Co należy zrobić, to przeszukać przez codename i content_type, które gwarantują unikalne połączenie. Unikalny model content_type jest powiązany z każdym modelem w projekcie: dwa modele o tej samej nazwie, ale w różnych aplikacjach, otrzymają dwa różne typy zawartości.

    Oznacza to dodanie zależności od aplikacji contenttypes i korzystanie z modelu ContentType: ContentType = apps.get_model("contenttypes", "ContentType").

1

Jak powiedział w https://code.djangoproject.com/ticket/23422 sygnał post_migrate należy przesłać przed czynienia z obiektami zgody.

Ale jest funkcja pomocnik już na Django aby wysłał potrzebne sygnału: django.core.management.sql.emit_post_migrate_signal

Tutaj to działało w ten sposób:

# -*- 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), 
    ] 
Powiązane problemy