Cara termudah untuk mengubah nama model menggunakan Django / South?

141

Saya telah mencari jawaban untuk ini di situs Selatan, Google, dan SO, tetapi tidak dapat menemukan cara sederhana untuk melakukan ini.

Saya ingin mengganti nama model Django menggunakan Selatan. Katakanlah Anda memiliki yang berikut ini:

class Foo(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

dan Anda ingin mengonversi Foo ke Bar, yaitu

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

Sederhananya, saya hanya mencoba mengubah nama dari Foomenjadi Bar, tetapi abaikan fooanggota FooTwountuk saat ini.

Apa cara termudah untuk melakukan ini menggunakan Selatan?

  1. Saya mungkin bisa melakukan migrasi data, tetapi itu tampaknya cukup terlibat.
  2. Tulis migrasi khusus, misalnya db.rename_table('city_citystate', 'geo_citystate'), tetapi saya tidak yakin bagaimana cara memperbaiki kunci asing dalam kasus ini.
  3. Cara mudah yang Anda tahu?
vaughnkoch
sumber
5
Lihat juga stackoverflow.com/questions/3235995/… untuk mengganti nama bidang model daripada model .
Siput mekanik
Solusi yang dioptimalkan untuk Django> = 1,8 stackoverflow.com/questions/25091130/...
Chemical Programmer

Jawaban:

130

Untuk menjawab pertanyaan pertama Anda, penggantian nama model / tabel sederhana cukup mudah. Jalankan perintah:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Pembaruan 2: cobalah --autoalih-alih --emptymenghindari peringatan di bawah ini. Terima kasih kepada @KFB untuk tipnya.)

Jika Anda menggunakan versi lama dari selatan, Anda harus startmigrationbukannya schemamigration.

Kemudian edit file migrasi secara manual agar terlihat seperti ini:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

Anda dapat melakukannya lebih mudah dengan menggunakan db_tableopsi Meta di kelas model Anda. Tetapi setiap kali Anda melakukannya, Anda menambah bobot warisan basis kode Anda - memiliki nama kelas yang berbeda dari nama tabel membuat kode Anda lebih sulit untuk dipahami dan dipelihara. Saya sepenuhnya mendukung melakukan refactor sederhana seperti ini demi kejelasan.

(pembaruan) Saya baru saja mencoba ini dalam produksi, dan mendapat peringatan aneh ketika saya pergi untuk menerapkan migrasi. Itu berkata:

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

Saya menjawab "tidak" dan semuanya tampak baik-baik saja.

Leopd
sumber
3
Saya bisa menghindari pesan kesalahan yang Leopd dengan membuat skema migrasi menggunakan --auto bukan --empty. Saya kemudian mengedit file migrasi, mengubah penghapusan / pembuatan tabel menjadi panggilan db.rename_table (). Ini tampaknya telah bekerja dengan sangat baik.
KFB
4
Saya menggunakan teknik ini pada 9/2/2011 tanpa mendapatkan kesalahan. Mungkin versi yang lebih baru dari Selatan menyelesaikan masalah dengan kesalahan.
Chip Tol
1
Terima kasih telah memperbarui ini! Jawaban Jian di bawah ini mengatakan penting untuk menjaga panggilan "send_create_signal", apakah Anda memiliki pengetahuan tentang itu? Jika Anda setuju, akan lebih baik untuk memperbarui contoh migrasi Anda.
mrooney
5
Berhati-hatilah karena ini tidak akan mengubah nama indeks pada tabel itu. Jika Anda membuat tabel baru di masa depan dengan nama yang sama dengan tabel lama, Anda bisa mendapatkan kesalahan dari bertabrakan nama indeks. Kami menggunakan teknik ini, tetapi mulai sekarang kita akan secara eksplisit membuat tabel baru, memigrasikan data, lalu menghapus tabel lama.
Jeremy Banks
3
Nama kolom dalam tabel yang dibuat secara otomatis, seperti tabel M2M ke model asli, juga tidak dimigrasi oleh metode ini.
spookylukey
66

Lakukan perubahan models.pylalu jalankan

./manage.py schemamigration --auto myapp

Saat Anda memeriksa file migrasi, Anda akan melihat bahwa itu menghapus tabel dan membuat yang baru

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

Ini tidak seperti yang Anda inginkan. Alih-alih, edit migrasi sehingga terlihat seperti:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(
                app_label='myapp', model='foo').update(model='bar')

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')

Dengan tidak adanya updatepernyataan, db.send_create_signalpanggilan akan membuat yang baru ContentTypedengan nama model yang baru. Tapi itu lebih baik untuk hanya updateyang ContentTypesudah Anda miliki dalam kasus ada objek database menunjuk ke itu (misalnya, melalui GenericForeignKey).

Juga, jika Anda telah mengganti nama beberapa kolom yang merupakan kunci asing untuk model yang diubah namanya, jangan lupa untuk melakukannya

db.rename_column(myapp_model, foo_id, bar_id)
Jian
sumber
2
Saya mendapatkan kesalahan, KeyError: "Model 'tipe konten' dari aplikasi 'tipe konten' tidak tersedia dalam migrasi ini." Juga, saya memiliki tabel django_content_type, tetapi bukan tabel contenttypes. (Django 1.6)
Seth
2
@Seth Saya mengatasinya dengan melakukan pembaruan model ContentType dalam migrasi data yang terpisah dan dengan menambahkan contenttypes.ContentTypemodel ke model beku dengan menggunakan --frozenflag untuk ./manage.py datamigration. Sebagai contoh: ./manage.py datamigration --frozen contenttypes myapp update_contenttypes. Kemudian edit myapp_migrations / NNNN_update_contenttypes.py dengan kode pembaruan jenis konten seperti yang ditentukan di atas.
Geoffrey Hing
@ GeoffreyHing Saya pikir parameternya tidak membeku beku. south.readthedocs.io/en/latest/ormfreezing.html Tapi terima kasih banyak atas bantuan Anda, itu sangat membantu.
ccsakuweb
5

Selatan tidak bisa melakukannya sendiri - bagaimana ia tahu itu Barmewakili apa yang Foodulu? Ini adalah jenis hal yang ingin saya tuliskan migrasi khusus. Anda dapat mengubah ForeignKeykode Anda seperti yang Anda lakukan di atas, dan itu hanya kasus mengubah nama bidang dan tabel yang sesuai, yang dapat Anda lakukan dengan cara apa pun yang Anda inginkan.

Akhirnya, apakah Anda benar-benar perlu melakukan ini? Saya belum perlu mengubah nama model - nama model hanyalah detail implementasi - terutama mengingat ketersediaan verbose_nameopsi Meta.

Dominic Rodger
sumber
7
Atau, ganti nama model dalam kode tetapi gunakan db_tableopsi Meta untuk menjaga nama tabel database tetap sama.
Daniel Roseman
@Aniel - Anda tahu apakah db_tabledigunakan untuk mendapatkan nama kunci asing?
Dominic Rodger
saya percaya itu. Jika Anda mengubah nama model dan mengatur db_table, semuanya tetap berfungsi seperti yang diharapkan.
Davor Lucic
1
@DanielRoseman ini adalah solusi terbaik di seluruh utas!
joerick
-1

Saya mengikuti solusi Leopd di atas. Tapi, itu tidak mengubah nama model. Saya mengubahnya secara manual dalam kode (juga dalam model terkait di mana ini disebut sebagai FK). Dan melakukan migrasi selatan lain, tetapi dengan opsi --fake. Ini membuat nama model dan nama tabel menjadi sama.

Baru sadar, seseorang dapat mulai dengan mengubah nama model, kemudian mengedit file migrasi sebelum menerapkannya. Jauh lebih bersih.

gowthaman
sumber