Django - Bagaimana cara mengganti nama bidang model menggunakan Selatan?

209

Saya ingin mengubah nama bidang tertentu dalam model:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

harus berubah menjadi:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

Apa cara termudah untuk melakukan ini menggunakan Selatan?

Jonathan
sumber
7
Lihat juga stackoverflow.com/questions/2862979/… untuk mengganti nama model daripada bidang model .
Mekanik keong

Jawaban:

230

Anda dapat menggunakan db.rename_columnfungsi ini.

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

Argumen pertama db.rename_columnadalah nama tabel, jadi penting untuk mengingat bagaimana Django membuat nama tabel :

Django secara otomatis memperoleh nama tabel basis data dari nama kelas model Anda dan aplikasi yang memuatnya. Nama tabel basis data model dibuat dengan bergabung dengan "label aplikasi" model - nama yang Anda gunakan di manage.py startapp - ke nama kelas model, dengan garis bawah di antaranya.

Dalam kasus di mana Anda memiliki nama model multi-kata, berhuruf unta, seperti ProjectItem, nama tabel akan menjadi app_projectitem(yaitu, garis bawah tidak akan disisipkan di antara projectdan itemmeskipun mereka adalah camel-cased).

googletorp
sumber
2
Itu akan bekerja pada nama \ full_name, tetapi tidak pada bidang relasi, kan?
Jonathan
23
CATATAN PENTING: jika Anda akan menggunakan ini, pastikan "app_foo" adalah nama tabel database, jadi misalnya: "mainapp_profile", "name" adalah nama kolom lama dari basis data (bukan nama bidang model), misalnya: "user_id" dan "full_name" akan menjadi nama baru yang Anda ingin memiliki kolom (sekali lagi, kolom basis data dan bukan nama bidang). Jadi: db.rename_column ('mainapp_profile', 'user_id', 'new_user_id') Juga, jika Anda berurusan dengan kunci asing, Anda harus memiliki bagian "_id" di dalamnya ketika mengganti nama.
Gezim
3
Jika Anda secara manual melakukan db.rename_column ini, Anda mungkin harus melakukan schemamigration palsu sesudahnya untuk membersihkan barang-barang. Yang pertama memigrasikan perubahan dengan mengganti nama kolom. Kemudian perbaiki model (untuk memiliki nama yang diperbarui), dan kemudian lakukan aplikasi schemamigration ./manage.py --auto && ./manage.py migrasi --fake).
dr jimbob
24
Anda juga dapat menggunakan ./manage.py schemamigration my_app renaming_column_x --empty untuk membuat migrasi kosong dan hanya untuk menempatkan kode di dalamnya
Ilian Iliev
11
--empty tidak banyak membantu, alih-alih gunakan --auto dan ubah migrasi yang dibuat. Dengan cara ini, tidak akan mengklaim bidang telah dihapus untuk migrasi models.py berikutnya.
yasc
39

Inilah yang saya lakukan:

  1. Buat perubahan nama kolom dalam model Anda (dalam contoh ini akan menjadi myapp/models.py)
  2. Lari ./manage.py schemamigration myapp renaming_column_x --auto

Catatan renaming_column_xdapat berupa apa saja yang Anda suka, itu hanya cara memberikan nama deskriptif ke file migrasi.

Ini akan menghasilkan Anda file yang disebut myapp/migrations/000x_renaming_column_x.pyyang akan menghapus kolom lama Anda dan menambahkan kolom baru.

Ubah kode dalam file ini untuk mengubah perilaku migrasi menjadi ganti nama sederhana:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')
pelanggar
sumber
apa nama kolom dalam perintah Anda? xatau column_x?
andilabs
Bagian dari perintah yang Anda maksud hanya digunakan untuk memberi nama migrasi, itu bisa apa saja yang Anda suka. Nama kolom harus ditentukan dalam file migrasi saat Anda mengeditnya.
donturner
2
Membuat --automigrasi terlebih dahulu adalah tip yang bagus. Ini menghindari masalah dengan FreeM ORM Selatan, yang terjadi jika migrasi hanya memiliki forwardsdan backwardsmetode, tetapi tidak mengandung modelobjek beku .
mwcz
Bagaimana cara menjawab pertanyaan selatan 'Karena Anda menghapus bidang ini, Anda HARUS menentukan default'?
Bryce
3
Satu masalah yang saya miliki dengan metode ini adalah db.rename_columntidak mengubah nama batasan yang terkait dengan kolom. Migrasi masih akan berfungsi tetapi Anda akan memiliki kendala dinamai setelah nama kolom yang lama. Saya memiliki kolom dengan kendala keunikan, menamainya menggunakan metode ini, menguji bahwa kendala keunikan masih ada dan mendapat kesalahan tetapi nama kendala itu sendiri masih menggunakan nama kolom yang lama. Mungkin eksplisit db.delete_uniquedan db.create_uniqueakan melakukannya tetapi saya memutuskan untuk pergi dengan solusi sjh.
Louis
15

Saya tidak tahu tentang kolom db.rename, kedengarannya mudah, namun di masa lalu saya telah menambahkan kolom baru sebagai satu schemamigration, kemudian membuat datamigration untuk memindahkan nilai ke bidang baru, lalu schemamigration kedua untuk menghapus kolom lama

sjh
sumber
Saya juga. Masalah dengan teknik skema-data-skema adalah bahwa Anda berakhir dengan nama yang berbeda untuk bidang / model Anda. Terkadang ini menjadi masalah jika Anda menggunakan nama kanonik untuk mengaktifkan dispatcher ...
Jonathan
Saya hanya mencoba ini dan tidak berhasil karena ada konflik nama. Saya memiliki FK yang sama tetapi nama yang berbeda. Itu tidak valid. Jadi, jangan lakukan ini.
Gezim
2
@jonathan, dia ingin nama yang berbeda! ... @pilgrim, mau memposting beberapa kode? Saya telah melakukan ini beberapa kali minggu ini, jika model Anda tidak valid maka selatan tidak akan membuat migrasi.
sjh
Itu akan berhasil, tetapi mungkin akan lebih lambat daripada 'rename_column' yang disarankan orang lain. Saya tahu MySQL dapat melakukan "ALTER TABLE ... rename kolom" (atau apa pun itu) dengan cepat.
Rory
1
@Rory db.rename_columntidak akan mengubah nama batasan untuk Anda sehingga Anda harus mengatasinya secara manual. Jika Anda lupa melakukannya, migrasi akan berfungsi kecuali jika tanpa sepengetahuan Anda mungkin ada kendala masih menggunakan nama kolom yang lama. Tidak jelas bagi saya apakah masalahnya hanya kosmetik atau apakah dalam migrasi di masa depan di mana kendala harus dimanipulasi atau dijatuhkan Selatan tidak akan dapat menemukannya. Bagaimanapun, melakukannya di sini seperti yang disarankan sjh adalah cara yang aman untuk melakukannya: Anda dapat membiarkan Selatan mencari tahu apa yang harus dipikirkan.
Louis
10

Django 1.7 memperkenalkan Migrasi jadi sekarang Anda bahkan tidak perlu menginstal paket tambahan untuk mengelola migrasi Anda.

Untuk mengganti nama model Anda, Anda perlu membuat migrasi kosong terlebih dahulu:

$ manage.py makemigrations <app_name> --empty

Maka Anda perlu mengedit kode migrasi Anda seperti ini:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

Dan setelah itu Anda perlu menjalankan:

$ manage.py migrate <app_name>
Dmitrii Mikhailov
sumber
5

Cukup ganti model dan jalankan makemigrationsdi 1.9

Django secara otomatis mendeteksi bahwa Anda telah menghapus dan membuat satu bidang, dan bertanya:

Did you rename model.old to model.new (a IntegerField)? [y/N]

Katakan ya, dan migrasi yang tepat akan dibuat. Sihir.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
0
  1. Tambahkan southke aplikasi yang Anda instal dalam file pengaturan proyek.
  2. Berikan komentar pada bidang / tabel yang ditambahkan / dimodifikasi.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Hapus komentar bidang dan tulis yang dimodifikasi
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

Jika Anda menggunakan 'pycharm', maka Anda dapat menggunakan 'ctrl + shift + r' alih-alih 'manage.py', dan 'shift' untuk parameter.

ancho
sumber