วิธีที่ง่ายที่สุดในการเปลี่ยนชื่อรุ่นโดยใช้ Django / South คืออะไร?


141

ฉันตามล่าหาคำตอบสำหรับสิ่งนี้ในเว็บไซต์ของ South, Google และ SO แต่ไม่สามารถหาวิธีง่ายๆในการทำเช่นนี้

ฉันต้องการเปลี่ยนชื่อรุ่น Django โดยใช้ South สมมติว่าคุณมีดังต่อไปนี้:

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

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

และคุณต้องการแปลง Foo เป็น Bar ได้แก่

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

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

เพื่อให้ง่ายฉันแค่พยายามเปลี่ยนชื่อจากFooเป็นBarแต่ไม่สนใจfooสมาชิกในFooTwoตอนนี้

วิธีที่ง่ายที่สุดในการทำเช่นนี้โดยใช้ South คืออะไร

  1. ฉันอาจจะทำการโยกย้ายข้อมูล แต่ที่เกี่ยวข้องสวย
  2. เขียนการย้ายข้อมูลที่กำหนดเองเช่นdb.rename_table('city_citystate', 'geo_citystate')แต่ฉันไม่แน่ใจว่าจะแก้ไขคีย์ต่างประเทศในกรณีนี้ได้อย่างไร
  3. วิธีที่ง่ายกว่าที่คุณรู้

5
ดูเพิ่มเติมstackoverflow.com/questions/3235995/...สำหรับเปลี่ยนชื่อฟิลด์รูปแบบมากกว่ารูปแบบ
หอยทากวิศวกรรม

โซลูชันที่ปรับให้เหมาะสมสำหรับ Django> = 1.8 stackoverflow.com/questions/25091130/…
Chemical Programmer

คำตอบ:


130

เพื่อตอบคำถามแรกของคุณการเปลี่ยนชื่อรุ่น / ตารางอย่างง่ายนั้นค่อนข้างตรงไปตรงมา เรียกใช้คำสั่ง:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(อัปเดต 2: ลอง--autoแทน--emptyเพื่อหลีกเลี่ยงคำเตือนด้านล่างขอบคุณ @KFB สำหรับคำแนะนำ)

หากคุณกำลังใช้รุ่นเก่าของภาคใต้คุณจะต้องแทนstartmigrationschemamigration

จากนั้นแก้ไขไฟล์การโยกย้ายด้วยตนเองเพื่อให้มีลักษณะดังนี้:

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

คุณสามารถทำได้มากกว่านี้โดยใช้db_tableตัวเลือก Meta ในรุ่นของคุณ แต่ทุกครั้งที่คุณทำเช่นนั้นคุณเพิ่มน้ำหนักดั้งเดิมของ codebase ของคุณ - การมีชื่อคลาสแตกต่างจากชื่อตารางทำให้รหัสของคุณยากต่อการเข้าใจและบำรุงรักษา ฉันสนับสนุนอย่างเต็มที่ในการทำ refactorings อย่างนี้เพื่อความชัดเจน

(อัปเดต) ฉันเพิ่งลองใช้งานจริงและได้รับคำเตือนแปลก ๆ เมื่อฉันไปใช้การย้ายถิ่น มันพูดว่า:

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'.

ฉันตอบว่า "ไม่" และทุกอย่างดูเหมือนจะโอเค


3
ฉันสามารถหลีกเลี่ยงข้อความแสดงข้อผิดพลาดที่ Leopd โดยการสร้างการโยกย้ายสคีมาโดยใช้ - อัตโนมัติแทนที่จะ - ว่างเปล่า จากนั้นฉันแก้ไขไฟล์การย้ายข้อมูลโดยเปลี่ยนการลบ / การสร้างตารางเป็นการเรียก db.rename_table () ดูเหมือนว่าจะทำงานได้ดีมาก
KFB

4
ฉันใช้เทคนิคนี้ในวันที่ 9/2/2554 โดยไม่มีข้อผิดพลาด อาจเป็นเวอร์ชันใหม่ของภาคใต้ที่แก้ไขปัญหาด้วยข้อผิดพลาด
ชิป Tol

1
ขอบคุณสำหรับการปรับปรุงนี้! คำตอบของเจียนด้านล่างบอกว่ามันสำคัญที่จะต้องโทรออก "send_create_signal" คุณมีความรู้เกี่ยวกับเรื่องนี้ไหม? หากคุณเห็นด้วยจะเป็นการดีที่จะอัปเดตการโยกย้ายตัวอย่างของคุณ
mrooney

5
ระวังว่าสิ่งนี้จะไม่เปลี่ยนชื่อดัชนีในตารางนั้น หากคุณสร้างตารางใหม่ในอนาคตด้วยชื่อเดียวกันกับตารางเก่าคุณสามารถรับข้อผิดพลาดจากการชนกันของชื่อดัชนี เราใช้เทคนิคนี้ แต่จากนี้ไปเราจะสร้างตารางใหม่อย่างชัดเจนโยกย้ายข้อมูลแล้วลบตารางเก่า
Jeremy Banks

3
ชื่อคอลัมน์ในตารางที่สร้างขึ้นโดยอัตโนมัติเช่นตาราง M2M ไปยังรุ่นดั้งเดิมจะไม่ถูกย้ายด้วยวิธีนี้
spookylukey

66

ทำการเปลี่ยนแปลงในmodels.pyแล้วเรียกใช้

./manage.py schemamigration --auto myapp

เมื่อคุณตรวจสอบไฟล์การโยกย้ายคุณจะเห็นว่ามันลบตารางและสร้างไฟล์ใหม่

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):
        ...

นี่ไม่ใช่สิ่งที่คุณต้องการ ให้แก้ไขการโยกย้ายแทนเพื่อให้ดูเหมือนว่า:

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

ในกรณีที่ไม่มีupdateคำสั่งการdb.send_create_signalโทรจะสร้างใหม่ContentTypeด้วยชื่อรุ่นใหม่ แต่มันจะดีกว่าที่จะเพียงแค่คุณมีอยู่แล้วในกรณีที่มีวัตถุฐานข้อมูลชี้ไปที่มัน (เช่นผ่านทาง)updateContentTypeGenericForeignKey

นอกจากนี้หากคุณเปลี่ยนชื่อบางคอลัมน์ซึ่งเป็นคีย์ต่างประเทศเป็นรุ่นที่เปลี่ยนชื่ออย่าลืม

db.rename_column(myapp_model, foo_id, bar_id)

2
ฉันได้รับข้อผิดพลาด KeyError: "โมเดล 'contenttype' จากแอพ 'contenttypes' ไม่สามารถใช้ได้ในการย้ายข้อมูลนี้" นอกจากนี้ฉันมีตาราง django_content_type แต่ไม่ใช่ตาราง contenttypes (Django 1.6)
เซท

2
@Seth ผมทำงานรอบนี้ด้วยการทำการปรับปรุงรุ่น ContentType ในการโยกย้ายข้อมูลที่แยกต่างหากและโดยการเพิ่มcontenttypes.ContentTypeรูปแบบกับรุ่นแช่แข็งโดยใช้ธง--frozen ./manage.py datamigrationตัวอย่างเช่น./manage.py datamigration --frozen contenttypes myapp update_contenttypes. จากนั้นแก้ไข myapp_migrations / NNNN_update_contenttypes.py ด้วยรหัสการอัปเดตประเภทเนื้อหาตามที่ระบุข้างต้น
Geoffrey Hing

@GeoffreyHing ฉันคิดว่าพารามิเตอร์นั้นไม่มีการแช่แข็ง south.readthedocs.io/en/latest/ormfreezing.htmlแต่ขอบคุณมากสำหรับความช่วยเหลือของคุณมันมีประโยชน์จริงๆ
ccsakuweb

5

ใต้ไม่สามารถทำเอง - รู้ได้อย่างไรว่าBarหมายถึงสิ่งที่Fooเคยทำ นี่คือสิ่งที่ฉันจะเขียนการย้ายแบบกำหนดเองสำหรับ คุณสามารถเปลี่ยนForeignKeyรหัสของคุณตามที่คุณได้ทำไปแล้วจากนั้นเป็นเพียงกรณีของการเปลี่ยนชื่อเขตข้อมูลและตารางที่เหมาะสมซึ่งคุณสามารถทำได้ทุกอย่างที่คุณต้องการ

ในที่สุดคุณจำเป็นต้องทำสิ่งนี้จริงๆหรือ? ฉันยังไม่จำเป็นต้องเปลี่ยนชื่อรุ่น - ชื่อรุ่นเป็นเพียงรายละเอียดการใช้งาน - โดยเฉพาะอย่างยิ่งเมื่อมีความพร้อมของverbose_nameตัวเลือก Meta


7
หรือเปลี่ยนชื่อโมเดลเป็นโค้ด แต่ใช้db_tableตัวเลือก Meta เพื่อคงชื่อตารางฐานข้อมูลไว้เหมือนเดิม
Daniel Roseman

@Daniel - คุณรู้หรือไม่ว่าdb_tableใช้เพื่อรับชื่อคีย์ต่างประเทศหรือไม่
โดมินิกร็อดเจอร์

ฉันเชื่อว่ามันเป็น หากคุณเปลี่ยนชื่อรุ่นและตั้งค่า db_table ทุกอย่างควรทำงานตามที่คาดไว้
Davor Lucic

1
@DanielRoseman นี่เป็นทางออกที่ดีที่สุดในกระทู้ทั้งหมด!
joerick

-1

ฉันติดตามโซลูชันของ Leopd ด้านบน แต่นั่นไม่ได้เปลี่ยนชื่อรุ่น ฉันเปลี่ยนมันด้วยตนเองในรหัส (รวมถึงในรุ่นที่เกี่ยวข้องซึ่งเรียกว่า FK) และทำการอพยพอีกครั้งทางใต้ แต่ด้วย - ตัวเลือกปลอม ทำให้ชื่อรุ่นและชื่อตารางเหมือนกัน

เพิ่งรู้ว่าใครจะเริ่มด้วยการเปลี่ยนชื่อรุ่นจากนั้นแก้ไขไฟล์การย้ายข้อมูลก่อนนำไปใช้ ทำความสะอาดมาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.