Django - วิธีเปลี่ยนชื่อฟิลด์โมเดลโดยใช้ South?


209

ฉันต้องการเปลี่ยนชื่อของฟิลด์เฉพาะในโมเดล:

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

ควรเปลี่ยนเป็น:

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

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


7
ดูเพิ่มเติมstackoverflow.com/questions/2862979/...สำหรับเปลี่ยนชื่อรุ่นมากกว่าข้อมูลรูปแบบ
หอยทากวิศวกรรม

คำตอบ:


230

คุณสามารถใช้db.rename_columnฟังก์ชั่น

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

อาร์กิวเมนต์แรกของdb.rename_columnคือชื่อตารางดังนั้นสิ่งสำคัญคือต้องจำว่าDjango สร้างชื่อตารางอย่างไร :

Django ได้รับชื่อตารางฐานข้อมูลโดยอัตโนมัติจากชื่อรุ่นคลาสของคุณและแอพที่มีอยู่ ชื่อตารางฐานข้อมูลของโมเดลสร้างขึ้นโดยการเข้าร่วม "ป้ายกำกับแอป" ของโมเดล - ชื่อที่คุณใช้ใน Manage.py startapp - กับชื่อคลาสของโมเดลโดยมีการขีดเส้นใต้ระหว่างทั้งสอง

ในกรณีที่คุณมีชื่อรุ่นของอูฐที่ใส่คำหลายคำเช่น ProjectItem ชื่อตารางจะเป็นapp_projectitem(เช่นขีดล่างจะไม่ถูกแทรกระหว่างprojectและitemแม้ว่าจะเป็นตัวอูฐก็ตาม)


2
นั่นจะใช้ได้กับชื่อ \ full_name แต่ไม่ใช่ในฟิลด์ความสัมพันธ์ใช่ไหม
Jonathan

23
หมายเหตุสำคัญ: หากคุณจะใช้สิ่งนี้ตรวจสอบให้แน่ใจว่า "app_foo" เป็นชื่อตารางฐานข้อมูลดังนั้นตัวอย่างเช่น: "mainapp_profile", "name" เป็นชื่อคอลัมน์เก่าของฐานข้อมูล (ไม่ใช่ชื่อฟิลด์โมเดล) ตัวอย่างเช่น: "user_id" และ "full_name" จะเป็นชื่อใหม่ที่คุณต้องการให้คอลัมน์มี (อีกครั้งคือคอลัมน์ฐานข้อมูลและไม่ใช่ชื่อฟิลด์) ดังนั้น: db.rename_column ('mainapp_profile', 'user_id', 'new_user_id') นอกจากนี้หากคุณจัดการกับคีย์ต่างประเทศคุณควรมีส่วน "_id" ในการเปลี่ยนชื่อ
Gezim

3
หากคุณทำเช่นนี้ด้วยตนเอง db.rename_column คุณอาจต้องทำการตรวจสอบสถานะปลอมหลังจากนั้นจึงทำการล้างข้อมูลสำรอง นั่นคือการโยกย้ายการเปลี่ยนแปลงครั้งแรกด้วยการเปลี่ยนชื่อคอลัมน์ จากนั้นแก้ไขแบบจำลอง (เพื่อให้มีชื่อที่อัปเดตแล้ว) จากนั้นทำแอป. / manage.py schemamigration - auto && ./manage.py โยกย้ายแอป - ปลอม)
dr jimbob

24
นอกจากนี้คุณยังสามารถใช้. /manage.py schemamigration my_app renaming_column_x - ยกเว้นเพื่อสร้างการย้ายข้อมูลที่ว่างเปล่าและเพียงวางรหัสไว้ในนั้น
Ilian Iliev

11
- ยกเว้นไม่ช่วยอะไรมากให้ใช้ - อัตโนมัติและแก้ไขการย้ายข้อมูลที่สร้างขึ้น วิธีนี้มันจะไม่อ้างสิทธิ์ฟิลด์ถูกลบสำหรับการโยกย้าย models.py ครั้งต่อไป
yasc

39

นี่คือสิ่งที่ฉันทำ:

  1. ทำการเปลี่ยนชื่อคอลัมน์ในโมเดลของคุณ (ในตัวอย่างนี้จะเป็นmyapp/models.py)
  2. วิ่ง ./manage.py schemamigration myapp renaming_column_x --auto

หมายเหตุrenaming_column_xสามารถเป็นอะไรก็ได้ที่คุณชอบมันเป็นเพียงวิธีการให้ชื่อที่สื่อความหมายกับไฟล์การย้ายข้อมูล

สิ่งนี้จะสร้างไฟล์ชื่อคุณmyapp/migrations/000x_renaming_column_x.pyซึ่งจะลบคอลัมน์เก่าของคุณและเพิ่มคอลัมน์ใหม่

แก้ไขรหัสในไฟล์นี้เพื่อเปลี่ยนพฤติกรรมการย้ายข้อมูลเป็นการเปลี่ยนชื่อแบบง่าย:

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

ชื่อของคอลัมน์ในคำสั่งของคุณคืออะไร? xหรือcolumn_x?
andilabs

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

2
การสร้างการ--autoโยกย้ายครั้งแรกเป็นเคล็ดลับที่ยอดเยี่ยม มันหลีกเลี่ยงปัญหากับช่องแช่แข็ง ORM ใต้ซึ่งเกิดขึ้นหากการโยกย้ายมีforwardsและbackwardsวิธีการเท่านั้น แต่ไม่มีmodelวัตถุที่ถูกตรึง
mwcz

ฉันจะตอบคำถามของภาคใต้ 'เนื่องจากคุณกำลังลบฟิลด์นี้คุณต้องระบุค่าเริ่มต้น'
ไบรซ์

3
ปัญหาเดียวที่ฉันมีกับวิธีนี้คือdb.rename_columnไม่ได้เปลี่ยนชื่อข้อ จำกัด ที่เกี่ยวข้องกับคอลัมน์ การย้ายข้อมูลจะยังคงใช้งานได้ แต่คุณจะมีข้อ จำกัด ที่ตั้งชื่อตามชื่อคอลัมน์เก่า ฉันมีคอลัมน์ที่มีข้อ จำกัด ที่ไม่ซ้ำเปลี่ยนชื่อโดยใช้วิธีนี้ทดสอบว่าข้อ จำกัด ที่ไม่ซ้ำยังคงมีอยู่และมีข้อผิดพลาด แต่ชื่อของข้อ จำกัด นั้นยังคงใช้ชื่อคอลัมน์เดิม อาจจะชัดเจนdb.delete_uniqueและdb.create_uniqueจะทำ แต่ฉันตัดสินใจที่จะไปแก้ปัญหาของ sjh
หลุยส์

15

ฉันไม่ทราบเกี่ยวกับคอลัมน์ db.rename ฟังดูมีประโยชน์ แต่ในอดีตฉันได้เพิ่มคอลัมน์ใหม่เป็นหนึ่ง schemamigration แล้วสร้าง datamigration เพื่อย้ายค่าลงในเขตข้อมูลใหม่จากนั้น schemamigration ที่สองเพื่อลบคอลัมน์เก่า


ฉันด้วย. ปัญหาเกี่ยวกับเทคนิค schema-data-schema คือคุณท้ายชื่อต่างกันสำหรับฟิลด์ / โมเดลของคุณ บางครั้งปัญหานี้เป็นปัญหาถ้าคุณใช้มาตรฐานชื่อเพื่อเปิดใช้งานมอบหมายงานแล้ว ...
โจนาธาน

ฉันเพิ่งลองทำสิ่งนี้และมันใช้งานไม่ได้เนื่องจากมีความขัดแย้งของชื่อ ฉันมี FK ที่แน่นอน แต่ชื่อต่างกัน มันไม่ได้ตรวจสอบ ดังนั้นอย่าทำเช่นนี้
Gezim

2
@ โจนาธานเขาต้องการชื่อที่แตกต่างกัน! ... @pilgrim สนใจโพสต์โค้ดบ้างไหม? ฉันทำสิ่งนี้หลายครั้งในสัปดาห์นี้ถ้าแบบจำลองของคุณไม่ผ่านการตรวจสอบแล้วทางใต้จะไม่สร้างการย้ายถิ่น
sjh

อาจใช้งานได้ แต่อาจช้ากว่า 'rename_column' ที่คนอื่นแนะนำ ฉันรู้ว่า MySQL สามารถทำ "แก้ไขตาราง ... เปลี่ยนชื่อคอลัมน์" (หรืออะไรก็ได้) ค่อนข้างเร็ว
โรรี่

1
@Rory db.rename_columnจะไม่เปลี่ยนชื่อข้อ จำกัด สำหรับคุณดังนั้นคุณต้องจัดการด้วยตนเอง หากคุณลืมที่จะทำการโยกย้ายจะทำงานได้ยกเว้นว่าคุณไม่รู้จักอาจมีข้อ จำกัด ที่ยังคงใช้ชื่อคอลัมน์เก่า มันไม่ชัดเจนสำหรับฉันว่าปัญหานี้เป็นเพียงแค่ความงามหรือในอนาคตของการโยกย้ายที่ซึ่งข้อ จำกัด ควรได้รับการแก้ไขหรือลดลงในภาคใต้จะไม่สามารถค้นหาได้ ไม่ว่าจะทำอะไรที่นี่อย่างที่แนะนำ sjh เป็นวิธีที่ปลอดภัยที่จะทำ: คุณสามารถปล่อยให้ทางใต้คิดออกว่าควรจะหาอะไร
หลุยส์

10

Django 1.7 แนะนำMigrationsดังนั้นตอนนี้คุณไม่จำเป็นต้องติดตั้งแพ็คเกจเพิ่มเติมเพื่อจัดการการย้ายข้อมูลของคุณ

หากต้องการเปลี่ยนชื่อรุ่นของคุณคุณต้องสร้างการย้ายข้อมูลที่ว่างเปล่าก่อน:

$ manage.py makemigrations <app_name> --empty

จากนั้นคุณต้องแก้ไขรหัสการโยกย้ายดังนี้:

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

และหลังจากนั้นคุณต้องเรียกใช้:

$ manage.py migrate <app_name>

5

เพียงแค่เปลี่ยนรูปแบบและเรียกใช้makemigrationsใน 1.9

Django ตรวจพบโดยอัตโนมัติว่าคุณได้ลบและสร้างฟิลด์เดียวและถามว่า:

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

พูดว่าใช่แล้วการโยกย้ายที่ถูกต้องก็จะถูกสร้างขึ้น มายากล.


0
  1. เพิ่มsouthไปยังแอปที่ติดตั้งของคุณในไฟล์การตั้งค่าโครงการ
  2. ใส่ความคิดเห็นลงในฟิลด์ / ตารางที่เพิ่ม / แก้ไข
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. ยกเลิกการแสดงความคิดเห็นฟิลด์และเขียนหนึ่งการปรับเปลี่ยน
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

หากคุณใช้ 'pycharm' คุณสามารถใช้ 'ctrl + shift + r' แทน 'Manage.py' และ 'shift' สำหรับพารามิเตอร์

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