Wie man ein Modell zwischen zwei Django-Apps (Django 1.7)

Also vor etwa einem Jahr habe ich ein Projekt gestartet und wie alle neuen Entwickler habe ich mich nicht ganz auf die Struktur konzentriert, aber jetzt bin ich weiter zusammen mit Django hat es angefangen zu erscheinen, dass mein Projektlayout vor allem meine Modelle in der Struktur schrecklich sind .

Ich habe Modelle hauptsächlich in einer einzigen App gehalten und wirklich die meisten dieser Modelle sollten in ihren eigenen individuellen Apps sein, ich habe versucht, dies zu lösen und verschieben sie mit Süden aber ich fand es schwierig und wirklich schwierig wegen Fremdschlüssel ect.

Doch wegen Django 1.7 und gebaut in Unterstützung für Migrationen gibt es einen besseren Weg, dies zu tun jetzt?

Vielen Dank im Voraus für jede Hilfe.

    9 Solutions collect form web for “Wie man ein Modell zwischen zwei Django-Apps (Django 1.7)”

    Anmerkung: Ozan Antwort ist besser als meine. Bitte verwenden Sie diese Methode

    SCHAFFEN APPS & MODELLE

    1) Erstellen Sie zwei Apps, app1 & app2 .

     $ python manage.py startapp app1 $ python manage.py startapp app2 

    2) Erstellen Sie zwei Modelle model1 & model2 .

    App1 / model1.py

     from django.db import models class model1(models.Model): name = models.CharField(max_length=100) 

    App2 / model2.py

     from django.db import models class model1(models.Model): name = models.CharField(max_length=100) 

    3) Migrationen anwenden

     $ python manage.py makemigrations app1 && python migrate app1 $ python manage.py makemigrations app2 && python migrate app2 

    Django erstellt 2 Tabellen app1_model & app2_model2 . Sie können Daten zu Modellen hinzufügen.

    Lasst uns kleine Daten in unsere Modelle hinzufügen, um sicherzustellen, dass wir Daten beim Bewegen des Modells bewahren.

     In [1]: from apps.app1.models import model1 In [2]: model1.objects.create() Out[2]: <model1: model1 object> In [3]: model1.objects.create() Out[3]: <model1: model1 object> 

    MIGRIERENDES MODELL

    Lets schauen, wie man die tatsächliche Migration durchführt.

    4) Umbenennen von Table & Move Source Code Gehen Sie zu Ihrer Datenbank und benennen Sie app1_model in app2_model1 . Verschieben Sie den Quellcode für model1 von app1 auf app2 und fügen Sie Meta-Optionen hinzu.

    App1 / model1.py

     from django.db import models 

    App2 / model2.py

     from django.db import models class model2(models.Model): name = models.CharField(max_length=100) class model1(models.Model): name = models.CharField(max_length=100) class Meta: managed = False db_table = 'app2_model1' 

    5) Migrationen anwenden Wenden Sie Migrationen für beide Apps an.

     $ python manage.py makemigrations app2 && python manage.py migrate app2 $ python manage.py makemigrations app1 && python manage.py migrate --fake app1 

    Sobald die Migrationen abgeschlossen sind, können Sie die Daten im Modell überprüfen.

     In [1]: from apps.app2.models import model1 In [2]: model1.objects.all() Out[2]: [<model1: model1 object>, <model1: model1 object>] 

    Dies kann ganz einfach mit migrations.SeparateDatabaseAndState . Grundsätzlich verwenden wir eine Datenbankoperation, um die Tabelle gleichzeitig mit zwei Zustandsoperationen umzubenennen, um das Modell aus dem Verlauf einer App zu entfernen und es in einem anderen zu erstellen.

    Aus alter App entfernen

     python manage.py makemigrations old_app --empty 

    In der Migration:

     class Migration(migrations.Migration): dependencies = [] database_operations = [ migrations.AlterModelTable('TheModel', 'newapp_themodel') ] state_operations = [ migrations.DeleteModel('TheModel') ] operations = [ migrations.SeparateDatabaseAndState( database_operations=database_operations, state_operations=state_operations) ] 

    Zu neuer App hinzufügen

    Zuerst kopiere das Modell in die neue App Model.py, dann:

     python manage.py makemigrations new_app 

    Dies wird eine Migration mit einer naiven CreateModel Operation als einzige Operation generieren. Wickeln Sie das in einer SeparateDatabaseAndState Operation so ein, dass wir nicht versuchen, die Tabelle neu zu erstellen. Dazu gehören auch die vorherige Migration als Abhängigkeit:

     class Migration(migrations.Migration): dependencies = [ ('old_app', 'above_migration') ] state_operations = [ migrations.CreateModel( name='TheModel', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ], options={ 'db_table': 'newapp_themodel', }, bases=(models.Model,), ) ] operations = [ migrations.SeparateDatabaseAndState(state_operations=state_operations) ] 

    Ich stieß auf das gleiche Problem. Ozans Antwort hat mir sehr geholfen, aber leider war es nicht genug. In der Tat hatte ich mehrere ForeignKey Verknüpfung mit dem Modell, das ich bewegen wollte. Nach einigen Kopfschmerzen fand ich die Lösung so beschlossen, es zu posten, um Menschen Zeit zu lösen.

    Du brauchst noch 2 Schritte:

    1. Bevor Sie etwas tun, ändern Sie alle Ihre ForeignKey Verknüpfung mit TheModel in Integerfield . Dann laufen python manage.py makemigrations
    2. Nachdem du Ozans Schritte gemacht hast, kannst du deine Fremdschlüssel wieder ForeignKey(TheModel) anstelle von IntegerField() . Dann mach die Migrationen wieder ( python manage.py makemigrations ). Sie können dann migrieren und es sollte funktionieren ( python manage.py migrate )

    Ich hoffe es hilft. Natürlich testen Sie es in lokal, bevor Sie in der Produktion versuchen, um schlechte Überraschungen zu vermeiden 🙂

    Wie ich es getan habe (getestet auf Django == 1.8, mit postgres, also wohl auch 1.7)

    Lage

    App1.YourModel

    Aber du willst es zu: app2.YourModel

    1. Kopiere deinModel (den Code) von app1 auf app2.
    2. Füge dies zu app2.YourModel hinzu:

       Class Meta: db_table = 'app1_yourmodel' 
    3. $ Python manage.py makemigrations app2

    4. Eine neue Migration (zB 0009_auto_something.py) wird in app2 mit einer migrations.CreateModel () -Anweisung gemacht, diese Anweisung auf die anfängliche Migration von app2 (zB 0001_initial.py) verschieben (es wird genau so sein wie es immer dort war). Und nun die erzeugte Migration = 0009_auto_something.py entfernen

    5. So wie Sie handeln, wie app2.YourModel war immer da, jetzt entfernen Sie die Existenz von app1.YourModel aus Ihren Migrationen. Bedeutung: Kommentieren Sie die CreateModel-Anweisungen und jede Anpassung oder datamigration, die Sie danach verwendet haben.

    6. Und natürlich muss jeder Verweis auf app1.YourModel zu app2.YourModel durch dein Projekt geändert werden. Vergessen Sie auch nicht, dass alle möglichen Fremdschlüssel zu app1.YourModel in Migrationen in app2.YourModel geändert werden müssen

    7. Nun, wenn Sie $ python verwalten.py migrieren, hat sich nichts geändert, auch wenn Sie $ python verwalten.py Makemigrationen, nichts Neues wurde erkannt.

    8. Jetzt den letzten Schliff: beseitige die Klasse Meta von app2.YourModel und mache $ python manage.py makemigrations app2 && python manage.py migrate app2 (wenn du in diese Migration schaust, wirst du so etwas sehen 🙂

        migrations.AlterModelTable( name='yourmodel', table=None, ), 

    Table = None, bedeutet, dass es den Standard-Tabellennamen nehmen wird, der in diesem Fall app2_yourmodel ist.

    1. DONE, mit gespeicherten Daten.

    PS während der Migration wird es sehen, dass das content_type app1.yourmodel entfernt wurde und gelöscht werden kann. Du kannst ja dazu sagen, aber nur wenn du es nicht benutzt hast. Für den Fall, dass Sie stark davon abhängen, dass FKs zu diesem Inhalt-Typ intakt sind, antworten Sie nicht ja oder nein noch, aber gehen Sie in die db diese Zeit manuell, und entfernen Sie den Inhaltstyp app2.yourmodel und benennen Sie den Inhaltstyp app1 um. Dein model zu app2.yourmodel, und dann weiter mit der antwort nein

    Dies wird grob getestet, also vergessen Sie nicht, Ihre DB zu sichern!

    Zum Beispiel gibt es zwei Apps: src_app und dst_app , wir wollen das Modell MoveMe von src_app in dst_app .

    Leere Migrationen für beide Apps erstellen:

     python manage.py makemigrations --empty src_app python manage.py makemigrations --empty dst_app 

    Nehmen wir an, dass neue Migrationen sind XXX1_src_app_new und XXX1_dst_app_new , previuos Top-Migrationen sind XXX0_src_app_old und XXX0_dst_app_old .

    Fügen Sie eine Operation hinzu, die die Tabelle für das MoveMe Modell MoveMe und ihr app_label in ProjectState in XXX1_dst_app_new . Vergessen Sie nicht, die Abhängigkeit von der XXX0_src_app_old Migration hinzuzufügen. Die resultierende XXX1_dst_app_new Migration ist:

     # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations # this operations is almost the same as RenameModel # https://github.com/django/django/blob/1.7/django/db/migrations/operations/models.py#L104 class MoveModelFromOtherApp(migrations.operations.base.Operation): def __init__(self, name, old_app_label): self.name = name self.old_app_label = old_app_label def state_forwards(self, app_label, state): # Get all of the related objects we need to repoint apps = state.render(skip_cache=True) model = apps.get_model(self.old_app_label, self.name) related_objects = model._meta.get_all_related_objects() related_m2m_objects = model._meta.get_all_related_many_to_many_objects() # Rename the model state.models[app_label, self.name.lower()] = state.models.pop( (self.old_app_label, self.name.lower()) ) state.models[app_label, self.name.lower()].app_label = app_label for model_state in state.models.values(): try: i = model_state.bases.index("%s.%s" % (self.old_app_label, self.name.lower())) model_state.bases = model_state.bases[:i] + ("%s.%s" % (app_label, self.name.lower()),) + model_state.bases[i+1:] except ValueError: pass # Repoint the FKs and M2Ms pointing to us for related_object in (related_objects + related_m2m_objects): # Use the new related key for self referential related objects. if related_object.model == model: related_key = (app_label, self.name.lower()) else: related_key = ( related_object.model._meta.app_label, related_object.model._meta.object_name.lower(), ) new_fields = [] for name, field in state.models[related_key].fields: if name == related_object.field.name: field = field.clone() field.rel.to = "%s.%s" % (app_label, self.name) new_fields.append((name, field)) state.models[related_key].fields = new_fields def database_forwards(self, app_label, schema_editor, from_state, to_state): old_apps = from_state.render() new_apps = to_state.render() old_model = old_apps.get_model(self.old_app_label, self.name) new_model = new_apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, new_model): # Move the main table schema_editor.alter_db_table( new_model, old_model._meta.db_table, new_model._meta.db_table, ) # Alter the fields pointing to us related_objects = old_model._meta.get_all_related_objects() related_m2m_objects = old_model._meta.get_all_related_many_to_many_objects() for related_object in (related_objects + related_m2m_objects): if related_object.model == old_model: model = new_model related_key = (app_label, self.name.lower()) else: model = related_object.model related_key = ( related_object.model._meta.app_label, related_object.model._meta.object_name.lower(), ) to_field = new_apps.get_model( *related_key )._meta.get_field_by_name(related_object.field.name)[0] schema_editor.alter_field( model, related_object.field, to_field, ) def database_backwards(self, app_label, schema_editor, from_state, to_state): self.old_app_label, app_label = app_label, self.old_app_label self.database_forwards(app_label, schema_editor, from_state, to_state) app_label, self.old_app_label = self.old_app_label, app_label def describe(self): return "Move %s from %s" % (self.name, self.old_app_label) class Migration(migrations.Migration): dependencies = [ ('dst_app', 'XXX0_dst_app_old'), ('src_app', 'XXX0_src_app_old'), ] operations = [ MoveModelFromOtherApp('MoveMe', 'src_app'), ] 

    XXX1_dst_app_new Abhängigkeit von XXX1_dst_app_new zu XXX1_src_app_new . XXX1_src_app_new ist no-op Migration, die benötigt wird, um sicherzustellen, dass zukünftige src_app Migrationen nach XXX1_dst_app_new ausgeführt XXX1_dst_app_new .

    Bewege MoveMe von src_app/models.py zu dst_app/models.py . Dann renne:

     python manage.py migrate 

    Das ist alles!

    Sie können versuchen, die folgenden (ungetestet):

    1. Verschiebe das Modell von src_app zu dest_app
    2. dest_app ; src_app sicher, dass die Schema-Migration von der neuesten src_app Migration src_app ( https://docs.djangoproject.com/de/dev/topics/migrations/#migration-files )
    3. dest_app Sie eine Datenmigration zu dest_app , die alle Daten von src_app
    4. src_app ; dest_app sicher, dass die Schema-Migration von der letzten (Daten-) Migration von dest_app – das heißt: die Migration von Schritt 3

    Beachten Sie, dass Sie die ganze Tabelle kopieren werden, anstatt sie zu verschieben, aber auf diese Weise müssen beide Apps nicht einen Tisch berühren, der zur anderen App gehört, was ich für wichtig hält.

    Lets sagen, dass du das Model TheModel von app_a zu app_b bewegst.

    Eine alternative Lösung ist, die bestehenden Migrationen von Hand zu verändern. Die Idee ist, dass jedes Mal, wenn Sie sehen, eine Operation Ändern von TheModel in app_a Migrationen, kopieren Sie diese Operation an das Ende der App_b die erste Migration. Und jedes Mal, wenn du einen Hinweis 'app_a.TheModel' in den Migrationen von app_a siehst, wechsest du ihn in 'app_b.TheModel'.

    Ich habe dies gerade für ein bestehendes Projekt gemacht, wo ich ein bestimmtes Modell in eine wiederverwendbare App extrahieren wollte. Das Verfahren verlief reibungslos. Ich denke, es wäre viel schwieriger, wenn es Referenzen von app_b zu app_a gab. Außerdem hatte ich eine manuell definierte Meta.db_table für mein Modell, die geholfen haben könnte.

    Sie werden vor allem mit einer veränderten Migrationsgeschichte beginnen. Das spielt keine Rolle, auch wenn man eine Datenbank mit den ursprünglichen Migrationen hat. Wenn sowohl die Original- als auch die umgeschriebenen Migrationen mit demselben Datenbankschema enden, sollte diese Umschreibung in Ordnung sein.

    1. Ändere die Namen der alten Modelle zu "model_name_old"
    2. Makemigrationen
    3. Machen neue Modelle namens 'model_name_new' mit identischen Beziehungen zu den verwandten Modellen (zB User Model hat nun user.blog_old und user.blog_new)
    4. Makemigrationen
    5. Schreiben Sie eine benutzerdefinierte Migration, die alle Daten in die neuen Modelltabellen migriert
    6. Teste die Hölle aus diesen Migrationen, indem ich Backups mit neuen db-Kopien vor und nach dem Ausführen der Migrationen vergesse
    7. Wenn alles zufrieden stellend ist, löschen Sie die alten Modelle
    8. Makemigrationen
    9. Ändern Sie die neuen Modelle in den richtigen Namen 'model_name_new' -> 'model_name'
    10. Testen Sie die ganze Reihe von Migrationen auf einem Staging-Server
    11. Nehmen Sie Ihre Produktionsstätte für ein paar Minuten, um alle Migrationen ohne Benutzer zu stören

    Tun Sie dies individuell für jedes Modell, das bewegt werden muss. Ich würde nicht vorschlagen zu tun, was die andere Antwort sagt, indem sie sich auf Ganzzahlen und zurück zu Fremdschlüsseln Es gibt eine Chance, dass neue Fremdschlüssel werden anders und Zeilen können unterschiedliche IDs nach den Migrationen haben und ich wollte kein Risiko ausführen Von Mismatching-IDs beim Umschalten auf Fremdschlüssel.

    Eine weitere hackige Alternative, wenn die Daten nicht groß oder zu kompliziert sind, aber immer noch wichtig zu pflegen ist:

    • Holen Sie sich Daten-Fixtures mit manage.py dumpdata
    • Gehen Sie fort, Änderungen und Migrationen ordnungsgemäß zu modellieren, ohne die Änderungen zu verknüpfen
    • Globale ersetzen die Fixtures aus dem alten Modell und App Namen auf die neue
    • Laden von Daten mit manage.py loaddata
    Python ist die beste Programmiersprache der Welt.