จะคืนค่าการย้ายข้อมูลครั้งล่าสุดได้อย่างไร


447

ฉันได้ทำการย้ายข้อมูลที่เพิ่มตารางใหม่และต้องการเปลี่ยนกลับและลบการย้ายข้อมูลโดยไม่ต้องสร้างการย้ายข้อมูลใหม่

ฉันต้องทำอย่างไร? มีคำสั่งให้ยกเลิกการย้ายข้อมูลครั้งล่าสุดแล้วฉันสามารถลบไฟล์การโยกย้ายได้หรือไม่

คำตอบ:


797

คุณสามารถเปลี่ยนกลับได้โดยย้ายไปใช้การโยกย้ายครั้งก่อน

ตัวอย่างเช่นหากการโยกย้ายสองครั้งสุดท้ายของคุณคือ:

  • 0010_previous_migration
  • 0011_migration_to_revert

จากนั้นคุณจะทำ:

./manage.py migrate my_app 0010_previous_migration 

0011_migration_to_revertจากนั้นคุณสามารถลบการโยกย้าย

หากคุณใช้ Django 1.8+ คุณสามารถแสดงชื่อของการย้ายข้อมูลทั้งหมดด้วย

./manage.py showmigrations my_app

หากต้องการย้อนกลับการโยกย้ายทั้งหมดสำหรับแอปคุณสามารถเรียกใช้:

./manage.py migrate my_app zero

7
ฉันเคยเห็นคำตอบมากมายเกี่ยวกับเรื่องนี้กับปัญหาที่เก่าและก็ไม่ทำงานอีกต่อไป +1 เพราะใช้งานได้กับ Django 1.8
AlanSE

2
จะทำอย่างไรถ้าแอพมีไฟล์การโยกย้ายเพียงไฟล์เดียว / การย้ายข้อมูลเริ่มต้น และฉันต้องเลิกทำการโยกย้ายครั้งแรกหรือไม่
Adiyat Mubarak

37
migrateขอคำสั่งที่คุณใช้./manage.py migrate my_app zeroเพื่อ unapply โยกย้ายทั้งหมดสำหรับ app
Alasdair

4
ด้วยเหตุผลบางประการ. /manage.py โอนย้าย my_app 0010_previous_migration ไม่ได้ผลสำหรับฉัน ดังนั้นฉันจึงลองใช้. /manage.py โยกย้าย my_app 0010 และใช้งานได้ สาเหตุใดที่ Django 1.8 ไม่ใช้ชื่อการย้ายข้อมูลทั้งหมด
Varun Verma

4
ตราบใดที่คุณใช้ชื่อการโยกย้ายที่แท้จริงของคุณและไม่'0010_previous_migration'ฉันไม่รู้ว่าทำไมคุณจะเห็นพฤติกรรมนั้น
Alasdair

36

คำตอบโดย Alasdair ครอบคลุมพื้นฐาน

  • ระบุการโยกย้ายที่คุณต้องการ ./manage.py showmigrations
  • migrate ใช้ชื่อแอพและชื่อการย้ายข้อมูล

แต่ควรชี้ให้เห็นว่าการย้ายข้อมูลบางอย่างไม่สามารถย้อนกลับได้ สิ่งนี้จะเกิดขึ้นหาก Django ไม่มีกฎในการกลับรายการ สำหรับการเปลี่ยนแปลงส่วนใหญ่ที่คุณทำการโอนย้ายโดยอัตโนมัติภายใน./manage.py makemigrationsการกลับรายการจะเป็นไปได้ อย่างไรก็ตามสคริปต์ที่กำหนดเองจะต้องมีทั้งการเขียนไปข้างหน้าและย้อนกลับดังที่อธิบายไว้ในตัวอย่างที่นี่:

https://docs.djangoproject.com/en/1.9/ref/migration-operations/

วิธีการย้อนกลับแบบไม่ใช้งาน

หากคุณมีการRunPythonดำเนินการบางทีคุณอาจต้องการสำรองข้อมูลการย้ายข้อมูลโดยไม่ต้องเขียนสคริปต์การกลับรายการที่เข้มงวดอย่างมีเหตุผล แฮ็คด่วนต่อไปนี้เป็นตัวอย่างจาก docs (ลิงก์ด้านบน) อนุญาตให้ทำเช่นนี้ปล่อยให้ฐานข้อมูลอยู่ในสถานะเดียวกับที่มันเกิดขึ้นหลังจากการย้ายข้อมูลถูกนำไปใช้แม้ว่าจะย้อนกลับไปแล้วก็ตาม

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models

def forwards_func(apps, schema_editor):
    # We get the model from the versioned app registry;
    # if we directly import it, it'll be the wrong version
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias).bulk_create([
        Country(name="USA", code="us"),
        Country(name="France", code="fr"),
    ])

class Migration(migrations.Migration):

    dependencies = []

    operations = [
        migrations.RunPython(forwards_func, lambda apps, schema_editor: None),
    ]

ใช้งานได้กับ Django 1.8, 1.9


อัปเดต: วิธีที่ดีกว่าในการเขียนสิ่งนี้คือแทนที่lambda apps, schema_editor: Noneด้วยmigrations.RunPython.noopในข้อมูลโค้ดด้านบน สิ่งเหล่านี้ทั้งสองเป็นสิ่งเดียวกัน (เครดิตกับความคิดเห็น)


5
ตั้งแต่ Django 1.8 เป็นต้นไปคุณควรใช้RunPython.noopแทนแลมบ์ดาแบบอินไลน์หรือequivelent
SpoonMeiser

@SpoonMeiser migrations.RunPython(forwards_func, migrations.RunPython.noop)ในไวยากรณ์ของตัวอย่างที่ผมคิดว่ามีลักษณะเหมือน จำเป็นต้องตรวจสอบว่าใช้งานได้จริง ที่ควรได้รับการเพิ่มเป็นคำตอบหรือแก้ไขหนึ่งนี้บางครั้ง
AlanSE

13

RunPythonนี่คือวิธีการแก้ปัญหาของฉันตั้งแต่วิธีการแก้ปัญหาดังกล่าวข้างต้นไม่ได้จริงๆครอบคลุมการใช้งานในกรณีที่เมื่อคุณใช้

คุณสามารถเข้าถึงตารางผ่าน ORM ด้วย

from django.db.migrations.recorder import MigrationRecorder

>>> MigrationRecorder.Migration.objects.all()
>>> MigrationRecorder.Migration.objects.latest('id')
Out[5]: <Migration: Migration 0050_auto_20170603_1814 for model>
>>> MigrationRecorder.Migration.objects.latest('id').delete()
Out[4]: (1, {u'migrations.Migration': 1})

ดังนั้นคุณสามารถค้นหาตารางและลบรายการที่เกี่ยวข้องกับคุณได้ วิธีนี้คุณสามารถแก้ไขรายละเอียดได้ ด้วยการRynPythonโยกย้ายคุณยังต้องดูแลข้อมูลที่ถูกเพิ่ม / เปลี่ยนแปลง / ลบออก ตัวอย่างด้านบนแสดงเฉพาะวิธีที่คุณเข้าถึงตารางผ่าน Djang ORM


เมื่อสร้างโมเดลใหม่ด้วย ForeignKeys ที่มีการโยกย้ายหลายครั้งการรับรู้ทั้งหมดนั้นผิดและเริ่มต้นการโอนย้าย 2-3 ครั้งย้อนหลังด้วยรุ่นใหม่ แต่บางครั้งชื่อรุ่นเดียวกันหรือชื่อความสัมพันธ์เดียวกัน ... โซลูชันนี้เป็นผู้ชนะอย่างชัดเจน ฉันมีข้อผิดพลาดเช่นdjango.db.utils.ProgrammingError: relation "<relation name>" already existsนั้นฉันทำmigrate --fakeผิดซึ่งฉันพยายามกลับไปอีกครั้งจากนั้นฉันก็psycopg2.ProgrammingError: relation "<other <relation name>" does not existขอขอบคุณ
onekiloparsec

10

สิ่งอื่น ๆ ที่คุณสามารถทำได้คือลบตารางที่สร้างขึ้นด้วยตนเอง

นอกจากนั้นคุณจะต้องลบไฟล์การโยกย้ายนั้น ๆ นอกจากนี้คุณจะต้องลบรายการนั้นในตาราง django-migrations (อาจเป็นรายการสุดท้ายในกรณีของคุณ) ซึ่งสัมพันธ์กับการโยกย้ายนั้น


ระวังในกรณีนี้ - คุณมีหน้าที่ต้องตรวจสอบความถูกต้องของข้อมูลให้เพียงพอ
Sławomir Lenart

4
ฉันจะเพิ่มอย่างระมัดระวัง คุณสามารถทำลายหลายสิ่งใน Postgres เช่นข้อ จำกัด
joedborg

9

อย่าลบไฟล์การโยกย้ายจนกว่าหลังจากการพลิกกลับ ฉันทำผิดพลาดและไม่มีไฟล์การโยกย้ายฐานข้อมูลไม่ทราบว่าจะลบอะไรออก

python manage.py showmigrations
python manage.py migrate {app name from show migrations} {00##_migration file.py}

ลบไฟล์การโยกย้าย เมื่อการโยกย้ายที่ต้องการอยู่ในแบบจำลองของคุณ ...

python manage.py makemigrations
python manage.py migrate

8

ฉันทำสิ่งนี้ใน 1.9.1 (เพื่อลบการย้ายข้อมูลล่าสุดหรือสร้างล่าสุด):

  1. rm <appname>/migrations/<migration #>*

    ตัวอย่าง: rm myapp/migrations/0011*

  2. ล็อกอินเข้าสู่ฐานข้อมูลและรัน SQL นี้ (postgres ในตัวอย่างนี้)

    delete from django_migrations where name like '0011%';

ฉันสามารถสร้างการย้ายข้อมูลใหม่ที่เริ่มต้นด้วยหมายเลขการย้ายข้อมูลที่ฉันเพิ่งลบไป (ในกรณีนี้คือ 11)


1
+1 แม้ว่าจะใช้งานได้ แต่คุณต้องบันทึกวิธีนี้เป็นวิธีสุดท้าย นอกจากนี้คุณต้องจำไว้ว่าให้แก้ไข / วางคอลัมน์ / ตารางซึ่งการโอนย้ายข้อมูลที่เป็นปัญหา
nehem

จุดดี - ฉันใช้สิ่งนี้เมื่อฉันสร้างการย้ายข้อมูล แต่ไม่ได้เรียกใช้ "./manage.py โยกย้าย" ยัง
MIkee

3

คำตอบนี้เป็นกรณีที่คล้ายกันถ้าคำตอบด้านบนโดย Alasdair ไม่ได้ช่วย (เช่นหากการย้ายข้อมูลที่ไม่ต้องการถูกสร้างขึ้นอีกครั้งในทุกครั้งที่มีการย้ายข้อมูลใหม่หรืออยู่ในการย้ายข้อมูลที่ใหญ่กว่าซึ่งไม่สามารถเปลี่ยนกลับได้หรือตารางถูกลบด้วยตนเอง)

... ลบการย้ายข้อมูลโดยไม่สร้างการย้ายข้อมูลใหม่หรือไม่

TL; DR : คุณสามารถลบไม่กี่สุดท้ายหวนกลับ (สับสน) การโยกย้ายและสร้างใหม่หลังจากแก้ไขรุ่น คุณยังสามารถใช้วิธีอื่นเพื่อกำหนดค่าให้ไม่สร้างตารางโดยคำสั่งโยกย้าย การโยกย้ายที่ผ่านมาจะต้องสร้างขึ้นเพื่อให้ตรงกับรุ่นปัจจุบัน


กรณีที่ทำไมทุกคนไม่ต้องการสร้างตารางสำหรับแบบจำลองที่ต้องมีอยู่:

A)ไม่ควรมีตารางดังกล่าวในฐานข้อมูลที่ไม่มีในเครื่องและไม่มีเงื่อนไข

  • เมื่อ:มันเป็นรูปแบบพื้นฐานที่สร้างขึ้นเฉพาะสำหรับการสืบทอดรุ่นของรุ่นอื่น ๆ
  • วิธีแก้ไข:ตั้งค่าclass Meta: abstract = True

B)ตารางถูกสร้างขึ้นไม่ค่อยมีอย่างอื่นหรือด้วยตนเองในวิธีพิเศษ

  • โซลูชัน:ใช้class Meta: managed = False
    การโยกย้ายถูกสร้างขึ้น แต่ไม่เคยใช้ในการทดสอบเท่านั้น ไฟล์การโอนย้ายเป็นสิ่งสำคัญมิฉะนั้นการทดสอบฐานข้อมูลจะไม่สามารถทำงานได้โดยเริ่มจากสถานะเริ่มต้นที่ทำซ้ำได้

C)ตารางนี้ใช้กับเครื่องบางเครื่องเท่านั้น (เช่นในการพัฒนา)

  • การแก้ไข:ย้ายแบบจำลองเพื่อเป็นโปรแกรมใหม่ที่จะถูกเพิ่ม INSTALLED_APPS class Meta: managed = some_switchเฉพาะภายใต้เงื่อนไขพิเศษหรือใช้เงื่อนไข

D)โครงการใช้หลายฐานข้อมูลในsettings.DATABASES

  • วิธีแก้ไข:เขียนเราเตอร์ฐานข้อมูลด้วยวิธีการallow_migrateเพื่อแยกความแตกต่างของฐานข้อมูลที่ควรสร้างตารางและตำแหน่งที่ไม่

การโยกย้ายถูกสร้างขึ้นในทุกกรณี A), B), C), D) กับ Django 1.9+ (และเฉพาะในกรณี B, C, D กับ Django 1.8) แต่นำไปใช้กับฐานข้อมูลเฉพาะในกรณีที่เหมาะสมหรืออาจไม่เคยถ้า จำเป็นต้องใช้ดังนั้น การย้ายจำเป็นสำหรับการทดสอบตั้งแต่ Django 1.8 สถานะปัจจุบันที่เกี่ยวข้องทั้งหมดจะถูกบันทึกโดยการย้ายข้อมูลแม้สำหรับรุ่นที่มีการจัดการ = False ใน Django 1.9+ เพื่อเป็นไปได้ในการสร้าง ForeignKey ระหว่างรุ่นที่มีการจัดการ / ไม่มีการจัดการหรือเพื่อให้สามารถจัดการรูปแบบ = True ในภายหลัง (คำถามนี้เขียนขึ้นในช่วงเวลาของ Django 1.8 ทุกอย่างที่นี่ควรจะใช้ได้สำหรับรุ่นระหว่าง 1.8 ถึงปัจจุบัน 2.2)

หากการโยกย้ายครั้งล่าสุดคือ (เป็น) ไม่สามารถย้อนกลับได้อย่างง่ายดายก็เป็นไปได้ที่จะระมัดระวัง (หลังจากการสำรองฐานข้อมูล) ทำการเปลี่ยนกลับปลอม ./manage.py migrate --fake my_app 0010_previous_migrationให้ลบตารางด้วยตนเอง

./manage.py migrate --fake my_app 0011_fixed_migrationหากจำเป็นต้องสร้างการโยกย้ายคงที่จากรูปแบบคงที่และใช้มันโดยไม่ต้องเปลี่ยนโครงสร้างฐานข้อมูล


3

หากคุณกำลังประสบปัญหาในขณะที่คืนค่าการย้ายข้อมูลกลับมาและทำให้เกิดความสับสนคุณสามารถทำการfakeย้ายข้อมูลได้

./manage.py migrate <name> --ignore-ghost-migrations --merge --fake

สำหรับdjango เวอร์ชัน <1.7สิ่งนี้จะสร้างรายการในsouth_migrationhistoryตารางคุณต้องลบรายการนั้น

ตอนนี้คุณจะสามารถย้อนกลับการโยกย้ายได้อย่างง่ายดาย

PS: ฉันติดอยู่เป็นเวลานานและทำการย้ายถิ่นของปลอมแล้วการคืนค่ากลับช่วยฉันออก


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