Cara memindahkan model antara dua aplikasi Django (Django 1.7)

133

Jadi sekitar setahun yang lalu saya memulai sebuah proyek dan seperti semua pengembang baru saya tidak terlalu fokus pada struktur, tetapi sekarang saya lebih jauh bersama dengan Django sudah mulai tampak bahwa tata letak proyek saya terutama model saya strukturnya mengerikan dalam struktur. .

Saya memiliki sebagian besar model diadakan di satu aplikasi dan benar-benar sebagian besar model ini harus di aplikasi masing-masing, saya memang mencoba dan menyelesaikan ini dan memindahkannya dengan selatan tetapi saya merasa rumit dan sangat sulit karena kunci asing dll.

Namun karena Django 1.7 dan dukungan migrasi yang dibangun, apakah ada cara yang lebih baik untuk melakukannya sekarang?

Sam Buckingham
sumber
4
Anda mungkin ingin mempertimbangkan untuk mengubah jawaban yang diterima.
Babken Vardanyan
Untuk orang-orang yang menemukan ini di masa depan: Django 3.x di sini, dan pendekatan yang terperinci di realpython.com/move-django-model/… bekerja untuk saya. Saya memiliki beberapa kunci asing antara model dalam aplikasi lama, dan model dalam aplikasi baru.
pradeepcep

Jawaban:

16

Saya menghapus jawaban lama karena dapat menyebabkan hilangnya data. Seperti yang disebutkan ozan , kami dapat membuat 2 migrasi satu di setiap aplikasi. Komentar di bawah posting ini merujuk pada jawaban lama saya.

Migrasi pertama untuk menghapus model dari aplikasi pertama.

$ python manage.py makemigrations old_app --empty

Edit file migrasi untuk memasukkan operasi ini.

class Migration(migrations.Migration):

    database_operations = [migrations.AlterModelTable('TheModel', 'newapp_themodel')]

    state_operations = [migrations.DeleteModel('TheModel')]

    operations = [
      migrations.SeparateDatabaseAndState(
        database_operations=database_operations,
        state_operations=state_operations)
    ]

Migrasi kedua yang bergantung pada migrasi pertama dan membuat tabel baru di aplikasi kedua. Setelah memindahkan kode model ke aplikasi ke-2

$ python manage.py makemigrations new_app 

dan edit file migrasi ke sesuatu seperti ini.

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)
    ]
ChillarAnand
sumber
Saya memiliki data yang sudah ada dan banyak yang tidak bisa saya hilangkan, apakah mungkin melakukannya dengan ini?
Sam Buckingham
@KevinChristopherHenry Memodifikasi kode. Ini mempertahankan data yang ada.
ChillarAnand
@ SamBuckingham Ya, Anda dapat mencoba dengan kode yang dimodifikasi untuk bermigrasi tanpa kehilangan data.
ChillarAnand
2
Saya pikir itu akan menjadi cara terbaik, terima kasih atas semua bantuannya, semuanya brilian.
Sam Buckingham
1
IMO ini adalah solusi yang salah, asumsi dasar migrasi adalah bahwa jika Anda menjalankan ./manage.py migratesemuanya akan berakhir dalam keadaan baik. Memalsukan migrasi secara manual adalah IMO dengan cara yang salah.
jb.
341

Ini bisa dilakukan dengan cukup mudah menggunakan migrations.SeparateDatabaseAndState. Pada dasarnya, kami menggunakan operasi basis data untuk mengubah nama tabel bersamaan dengan dua operasi negara untuk menghapus model dari satu riwayat aplikasi dan membuatnya di yang lain.

Hapus dari aplikasi lama

python manage.py makemigrations old_app --empty

Dalam migrasi:

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

Tambahkan ke aplikasi baru

Pertama, salin model ke model.py aplikasi baru, lalu:

python manage.py makemigrations new_app

Ini akan menghasilkan migrasi dengan CreateModeloperasi naif sebagai operasi tunggal. Bungkus itu dalam SeparateDatabaseAndStateoperasi sehingga kami tidak mencoba membuat ulang tabel. Juga termasuk migrasi sebelumnya sebagai ketergantungan:

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)
    ]
ozan
sumber
14
Penjelasan yang sangat bagus. Ini harus menjadi jawabannya, dengan mengganti nama tabel Anda menghindari kehilangan data apa pun.
Remiz
11
Ini adalah cara terbaik untuk melakukannya dan itu jauh lebih baik daripada milikku. Catatan ditambahkan di atas jawaban saya.
ChillarAnand
4
Saya melakukan ini, tetapi ketika saya menjalankan "makemigrations" pada aplikasi baru setelah ini, itu menghasilkan migrasi AlterModelTable yang menamai namanya menjadi Tidak ada.
Diego Ponciano
4
Menemukan cara untuk menyelesaikan masalah saya berdasarkan instruksi ini. Masalahnya lebih rumit jika Anda memiliki referensi kunci asing yang wajib diisi. Saya harus menambahkan beberapa langkah untuk memindahkan referensi.
Nostalg.io
14
Karena beberapa permintaan, saya telah membuat jawaban terinci tentang migrasi model FK dengan contoh GitHub. stackoverflow.com/questions/30601107/…
Nostalg.io
26

Saya mengalami masalah yang sama. Jawaban Ozan membantu saya tetapi sayangnya tidak cukup. Memang saya memiliki beberapa ForeignKey yang menghubungkan ke model yang ingin saya pindahkan. Setelah beberapa sakit kepala saya menemukan solusi sehingga memutuskan untuk mempostingnya untuk menyelesaikan waktu orang.

Anda membutuhkan 2 langkah lagi:

  1. Sebelum melakukan apa pun, ubah semua ForeignKeytautan AndaTheModel menjadi Integerfield. Lalu laripython manage.py makemigrations
  2. Setelah melakukan langkah-langkah Ozan, konversikan kembali kunci asing Anda: pasang kembali, ForeignKey(TheModel)bukan IntegerField(). Kemudian lakukan migrasi lagi (python manage.py makemigrations ). Anda kemudian dapat bermigrasi dan seharusnya berfungsi (python manage.py migrate )

Semoga ini bisa membantu. Tentu saja mengujinya di lokal sebelum mencoba produksi untuk menghindari kejutan buruk :)

otranzer
sumber
8
bagaimana dengan hubungan ManyToManyField ??
tomcounsell
1
@tomcounsell komentar hebat, saya akan berasumsi dengan menambahkan model spesifik melalui hanya untuk tujuan migrasi. Banyak pekerjaan yang diperlukan untuk
menjaga
Karena hubungan banyak-ke-banyak biasanya hanya sebuah tabel dengan dua kunci asing, dari sudut pandang SQL Anda dapat menerapkan trik jawaban ini. Tetapi untuk mencapai ini hanya melalui Django, satu pendekatan yang dapat saya pikirkan adalah mengikuti baris jawaban @ozan, kecuali langkah pertama adalah menduplikasi tabel yang terlibat dalam hubungan MTM (satu versi dupes di setiap aplikasi) , migrasikan semua Kunci Asing ke aplikasi baru, dan baru kemudian menghapus dupes di aplikasi lama. Penafian: Saya belum menguji :)
Arnaud P
15

Bagaimana saya melakukannya (diuji pada Django == 1.8, dengan postgres, jadi mungkin juga 1.7)

Situasi

app1.YourModel

tetapi Anda ingin membuka: app2.YourModel

  1. Salin YourModel (kode) dari app1 ke app2.
  2. tambahkan ini ke app2.YourModel:

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

  4. Migrasi baru (mis. 0009_auto_something.py) dibuat di app2 dengan pernyataan migrasi.CreateModel (), pindahkan pernyataan ini ke migrasi awal app2 (mis. 0001_initial.py) (akan seperti itu selalu ada di sana). Dan sekarang hapus migrasi yang dibuat = 0009_auto_something.py

  5. Sama seperti Anda bertindak, seperti app2.YourModel selalu ada di sana, sekarang hapus keberadaan app1.YourModel dari migrasi Anda. Artinya: beri komentar pada pernyataan CreateModel, dan setiap penyesuaian atau migrasi data yang Anda gunakan setelah itu.

  6. Dan tentu saja, setiap referensi ke app1.YourModel harus diubah ke app2.YourModel melalui proyek Anda. Juga, jangan lupa bahwa semua kunci asing yang mungkin untuk app1.YourModel Anda dalam migrasi harus diubah ke app2.YourModel Anda

  7. Sekarang jika Anda melakukan migrasi $ python manage.py, tidak ada yang berubah, juga ketika Anda melakukan $ emsegemigrasie python manage.py, tidak ada yang baru terdeteksi.

  8. Sekarang sentuhan akhir: hapus Class Meta dari app2.YourModel dan lakukan $ python manage.py makemigrations app2 && python manage.py migrasi app2 (jika Anda melihat migrasi ini Anda akan melihat sesuatu seperti ini :)

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

table = None, berarti akan mengambil nama tabel default, yang dalam hal ini adalah app2_yourmodel.

  1. DIBUAT, dengan data yang disimpan.

PS selama migrasi akan melihat bahwa content_type app1.yourmodel Anda telah dihapus dan dapat dihapus. Anda bisa mengatakan ya untuk itu tetapi hanya jika Anda tidak menggunakannya. Jika Anda sangat bergantung padanya untuk memiliki FK agar jenis kontennya tetap utuh, jangan jawab ya atau belum, tetapi pergilah ke db saat itu secara manual, dan hapus app typey app2.yourmodel, dan ganti nama app typetype1. model Anda ke app2.yourmodel Anda, dan kemudian lanjutkan dengan menjawab tidak.

Michael van de Waeter
sumber
3
Meskipun solusi ini jelas "peretas" daripada @ ozan dan itu pasti membutuhkan lebih banyak pengeditan, itu berfungsi baik bagi saya (dan tidak masalah untuk mengedit migrasi - mereka seharusnya dapat diedit, menurut dokumen).
pgcd
1
Mungkin juga menggunakan app_label = 'app1'opsi meta.
Wtower
Jenius! Ini bekerja baik untuk saya untuk hubungan ForeignKey. Saya kira ini juga akan bekerja untuk banyak bidang ManyToMany.
Babken Vardanyan
Saya mengikuti langkah Anda tetapi bidang dalam beberapa model milik app1 terdiri dari Kunci Asing dengan hubungan rekursif dengan model (myModel) yang akan dipindahkan. Seperti field1 = models.ForeignKey('app1.myModel').ketika saya bermigrasi, saya mendapatkan ValueError yang menyatakan itufield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Deesha
12

Saya mendapatkan migrasi kode tangan yang gugup (seperti yang diminta oleh jawaban Ozan ) sehingga yang berikut ini menggabungkan strategi Ozan dan Michael untuk meminimalkan jumlah kode tangan yang diperlukan:

  1. Sebelum memindahkan model apa pun, pastikan Anda bekerja dengan baseline bersih dengan menjalankan makemigrations.
  2. Pindahkan kode untuk Model dari app1keapp2
  3. Seperti yang direkomendasikan oleh @Michael, kami mengarahkan model baru ke tabel database lama menggunakan db_tableopsi Meta pada model "baru":

    class Meta:
        db_table = 'app1_yourmodel'
  4. Lari makemigrations. Ini akan menghasilkan CreateModeldalam app2dan DeleteModeldalam app1. Secara teknis, migrasi ini merujuk ke tabel yang sama persis dan akan menghapus (termasuk semua data) dan membuat ulang tabel.

  5. Pada kenyataannya, kita tidak ingin (atau perlu) melakukan apa pun di atas meja. Kami hanya perlu Django untuk percaya bahwa perubahan telah dibuat. Per @ Ozan menjawab, state_operationsbendera di SeparateDatabaseAndStatemelakukan ini. Jadi kita membungkus semua migrationsentri DI KEDUA MIGRASI FILES dengan SeparateDatabaseAndState(state_operations=[...]). Sebagai contoh,

    operations = [
        ...
        migrations.DeleteModel(
            name='YourModel',
        ),
        ...
    ]

    menjadi

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=[
            ...
            migrations.DeleteModel(
                name='YourModel',
            ),
            ...
        ])
    ]
  6. Anda juga perlu memastikan CreateModelmigrasi "virtual" baru tergantung pada migrasi apa pun yang benar - benar membuat atau mengubah tabel asli . Misalnya, jika migrasi baru Anda adalah app2.migrations.0004_auto_<date>(untuk Create) dan app1.migrations.0007_auto_<date>(untuk Delete), hal paling sederhana untuk dilakukan adalah:

    • Buka app1.migrations.0007_auto_<date>dan salin app1ketergantungannya (mis ('app1', '0006...'),.). Ini adalah migrasi "segera sebelum" di app1dan harus mencakup dependensi pada semua logika pembangunan model yang sebenarnya.
    • Buka app2.migrations.0004_auto_<date>dan tambahkan dependensi yang baru saja Anda salin ke dependenciesdaftar.

Jika Anda memiliki ForeignKeyhubungan dengan model yang Anda pindahkan, hal di atas mungkin tidak berfungsi. Ini terjadi karena:

  • Dependensi tidak secara otomatis dibuat untuk ForeignKeyperubahan
  • Kami tidak ingin membungkus ForeignKeyperubahan state_operationssehingga kami perlu memastikan mereka terpisah dari operasi tabel.

CATATAN: Django 2.2 menambahkan peringatan ( models.E028) yang merusak metode ini. Anda mungkin dapat mengatasinya dengan managed=Falsetetapi saya belum mengujinya.

Rangkaian operasi "minimum" berbeda tergantung pada situasinya, tetapi prosedur berikut harus bekerja untuk sebagian besar / semua ForeignKeymigrasi:

  1. SALINAN model dari app1ke app2, setdb_table , tetapi JANGAN mengubah referensi FK.
  2. Jalankan makemigrationsdan bungkus semua app2migrasi masukstate_operations (lihat di atas)
    • Seperti di atas, tambahkan ketergantungan app2 CreateTableke app1migrasi terbaru
  3. Arahkan semua referensi FK ke model baru. Jika Anda tidak menggunakan referensi string, pindahkan model lama ke bawah models.py(JANGAN hapus) sehingga tidak bersaing dengan kelas yang diimpor.
  4. Jalankan makemigrationstetapi JANGAN masukkan apa pun state_operations(perubahan FK seharusnya benar-benar terjadi). Tambahkan dependensi di semua ForeignKeymigrasi (yaitu AlterField) ke CreateTablemigrasi di app2(Anda akan membutuhkan daftar ini untuk langkah selanjutnya jadi tetap ikuti mereka). Sebagai contoh:

    • Temukan migrasi yang menyertakan CreateModeleg app2.migrations.0002_auto_<date>dan salin nama migrasi itu.
    • Temukan semua migrasi yang memiliki ForeignKey ke model itu (mis. Dengan mencari app2.YourModeluntuk menemukan migrasi seperti:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
          ]
      
          operations = [
              migrations.AlterField(
                  model_name='relatedmodel',
                  name='fieldname',
                  field=models.ForeignKey(... to='app2.YourModel'),
              ),
          ]
    • Tambahkan CreateModelmigrasi sebagai ketergantungan:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
              ('app2', '0002_auto_<date>'),
          ]  
  5. Hapus model dari app1

  6. Jalankan makemigrationsdan bungkus app1migrasistate_operations .
    • Tambahkan ketergantungan ke semua ForeignKeymigrasi (mis. AlterField) Dari langkah sebelumnya (mungkin termasuk migrasi dalam app1dan app2).
    • Ketika saya membangun migrasi ini, yang DeleteTablesudah tergantung pada AlterFieldmigrasi jadi saya tidak perlu memberlakukannya secara manual (yaitu Altersebelumnya Delete).

Pada titik ini, Django sudah bagus. Model baru menunjuk ke tabel lama dan migrasi Django telah meyakinkan bahwa semuanya telah dipindahkan dengan tepat. Peringatan besar (dari jawaban @ Michael) adalah bahwa yang baru ContentTypedibuat untuk model baru. Jika Anda menautkan (mis. Oleh ForeignKey) ke jenis konten, Anda harus membuat migrasi untuk memperbarui ContentTypetabel.

Saya ingin membersihkan diri saya (opsi Meta dan nama tabel) jadi saya menggunakan prosedur berikut (dari @Michael):

  1. Hapus db_tableentri Meta
  2. Jalankan makemigrationslagi untuk menghasilkan penggantian nama basis data
  3. Edit migrasi terakhir ini dan pastikan itu tergantung pada DeleteTablemigrasi. Sepertinya tidak perlu karena Deleteharus murni logis, tetapi saya mengalami kesalahan (misalnya app1_yourmodeltidak ada) jika saya tidak.
claytond
sumber
Ini bekerja dengan baik, terima kasih! Saya tidak berpikir bahwa mengedit migrasi terakhir penting karena ini ada di bagian bawah pohon dependensi.
James Meakin
1
Jawaban yang bagus! Saya pikir Anda perlu menambahkan kurung tutup untuk migrasi. MemisahkanDatabaseAndState, kan?
atm
Ini berhasil untuk saya. Saya juga tidak mengedit migrasi terakhir (langkah 3, baris terakhir dari seluruh jawaban) seperti @JamesMeakin dan masih berfungsi dengan baik
Megawatt
dalam skenario kedua, yang dengan FK, langkah kedua gagal untuk saya dengan kesalahan yang masuk akal:table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
Mihai Zamfir
Saya telah menggunakan prosedur ini beberapa kali. Jika Anda membandingkan dokumentasi untuk 2.2 ( docs.djangoproject.com/en/2.2/ref/checks ) dan 2.1 ( docs.djangoproject.com/en/2.1/ref/checks ), Anda dapat melihatnya ditambahkan dalam 2.2. Dimungkinkan untuk bekerja dengan managed=Falsetetapi saya tidak ada tempat untuk memeriksa.
claytond
1

Alternatif peretasan lain jika datanya tidak besar atau terlalu rumit, tetapi tetap penting untuk dipelihara, adalah dengan:

  • Dapatkan perlengkapan data menggunakan manage.py dumpdata
  • Lanjutkan untuk memodelkan perubahan dan migrasi dengan benar, tanpa menghubungkan perubahan tersebut
  • Global mengganti perlengkapan dari model lama dan nama aplikasi ke yang baru
  • Muat data menggunakan manage.py loaddata
Wtower
sumber
1

Disalin dari jawaban saya di https://stackoverflow.com/a/47392970/8971048

Jika Anda perlu memindahkan model dan Anda tidak memiliki akses ke aplikasi lagi (atau Anda tidak ingin akses), Anda dapat membuat Operasi baru dan mempertimbangkan untuk membuat model baru hanya jika model yang dimigrasi tidak ada.

Dalam contoh ini saya meneruskan 'MyModel' dari old_app ke myapp.

class MigrateOrCreateTable(migrations.CreateModel):
    def __init__(self, source_table, dst_table, *args, **kwargs):
        super(MigrateOrCreateTable, self).__init__(*args, **kwargs)
        self.source_table = source_table
        self.dst_table = dst_table

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        table_exists = self.source_table in schema_editor.connection.introspection.table_names()
        if table_exists:
            with schema_editor.connection.cursor() as cursor:
                cursor.execute("RENAME TABLE {} TO {};".format(self.source_table, self.dst_table))
        else:
            return super(MigrateOrCreateTable, self).database_forwards(app_label, schema_editor, from_state, to_state)


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_some_migration'),
    ]

    operations = [
        MigrateOrCreateTable(
            source_table='old_app_mymodel',
            dst_table='myapp_mymodel',
            name='MyModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=18))
            ],
        ),
    ]
Gal Singer
sumber
Tolong jangan tambahkan jawaban yang sama untuk beberapa pertanyaan. Jawab yang terbaik dan tandai sisanya sebagai duplikat. Lihat Apakah diterima untuk menambahkan jawaban rangkap ke beberapa pertanyaan?
Petter Friberg
0

Ini diuji secara kasar, jadi jangan lupa untuk membuat cadangan DB Anda !!!

Misalnya, ada dua aplikasi: src_appdan dst_app, kami ingin memindahkan model MoveMedari src_appke dst_app.

Buat migrasi kosong untuk kedua aplikasi:

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

Mari kita asumsikan, bahwa migrasi baru adalah XXX1_src_app_newdan XXX1_dst_app_new, previuos migrasi teratas adalah XXX0_src_app_olddan XXX0_dst_app_old.

Tambahkan operasi yang MoveMemengubah nama tabel untuk model dan ganti nama app_label di ProjectState menjadi XXX1_dst_app_new. Jangan lupa menambahkan ketergantungan pada XXX0_src_app_oldmigrasi. XXX1_dst_app_newMigrasi yang dihasilkan adalah:

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

Tambahkan ketergantungan XXX1_dst_app_newke XXX1_src_app_new. XXX1_src_app_newadalah migrasi no-op yang diperlukan untuk memastikan bahwa src_appmigrasi di masa depan akan dilaksanakan setelah XXX1_dst_app_new.

Pindah MoveMedari src_app/models.pyke dst_app/models.py. Lalu lari:

python manage.py migrate

Itu saja!

Sergey Fedoseev
sumber
Perhatikan bahwa kode ini mungkin hanya berguna untuk Django 1.7. Mencoba ini di Django 2.0 tidak akan berhasil. Ini juga berarti bahwa menggunakan mekanisme ini untuk model bergerak menambah overhead pemeliharaan untuk meningkatkan versi Django Anda.
Paul di 't Hout
0

Anda dapat mencoba yang berikut (belum teruji):

  1. pindahkan model dari src_appkedest_app
  2. bermigrasi dest_app; pastikan migrasi skema tergantung pada src_appmigrasi terbaru ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )
  3. tambahkan migrasi data ke dest_app, yang menyalin semua data darisrc_app
  4. bermigrasi src_app; pastikan migrasi skema tergantung pada migrasi (data) terbaru dest_app- yaitu: migrasi langkah 3

Perhatikan bahwa Anda akan menjadi menyalin seluruh tabel, alih-alih memindahkannya , tetapi dengan cara itu kedua aplikasi tidak harus menyentuh tabel milik aplikasi lain, yang saya pikir lebih penting.

Pencuri web
sumber
0

Katakanlah Anda sedang memindahkan model TheModel dari app_a ke app_b.

Solusi alternatif adalah mengubah migrasi yang ada dengan tangan. Idenya adalah bahwa setiap kali Anda melihat operasi mengubah TheModel dalam migrasi app_a, Anda menyalin operasi itu ke akhir migrasi awal app_b. Dan setiap kali Anda melihat referensi 'app_a.Model' dalam migrasi app_a, Anda mengubahnya menjadi 'app_b.Model'.

Saya hanya melakukan ini untuk proyek yang ada, di mana saya ingin mengekstraksi model tertentu ke aplikasi yang dapat digunakan kembali. Prosedur berjalan dengan lancar. Saya kira segalanya akan lebih sulit jika ada referensi dari app_b ke app_a. Juga, saya memiliki Meta.db_table yang ditentukan secara manual untuk model saya yang mungkin membantu.

Khususnya Anda akan berakhir dengan riwayat migrasi yang diubah. Ini tidak masalah, bahkan jika Anda memiliki database dengan migrasi asli yang diterapkan. Jika migrasi asli dan yang ditulis ulang berakhir dengan skema database yang sama, maka penulisan ulang seperti itu harus OK.

aliasariai
sumber
0
  1. ubah nama model lama menjadi 'model_name_old'
  2. makemigration
  3. membuat model baru bernama 'model_name_new' dengan hubungan yang identik pada model terkait (mis. model pengguna sekarang memiliki user.blog_old dan user.blog_new)
  4. makemigration
  5. tulis migrasi khusus yang memigrasikan semua data ke tabel model baru
  6. uji coba migrasi ini dengan membandingkan cadangan dengan salinan db baru sebelum dan sesudah menjalankan migrasi
  7. bila semuanya memuaskan, hapus model lama
  8. makemigration
  9. ubah model baru ke nama yang benar 'model_name_new' -> 'model_name'
  10. uji seluruh pembunuhan migrasi pada server pementasan
  11. turunkan situs produksi Anda selama beberapa menit untuk menjalankan semua migrasi tanpa campur tangan pengguna

Lakukan ini secara terpisah untuk setiap model yang perlu dipindahkan. Saya tidak akan menyarankan melakukan apa yang jawaban lain katakan dengan mengubah ke bilangan bulat dan kembali ke kunci asing. Ada kemungkinan kunci asing baru akan berbeda dan baris mungkin memiliki ID yang berbeda setelah migrasi dan saya tidak ingin mengambil risiko apa pun. dari ketidakcocokan id ketika beralih kembali ke kunci asing.

Tomcounsell
sumber