Làm thế nào để hoàn nguyên việc di chuyển cuối cùng?


446

Tôi đã thực hiện một di chuyển đã thêm một bảng mới và muốn hoàn nguyên nó và xóa di chuyển mà không tạo ra một di chuyển mới.

Tôi phải làm nó như thế nào? Có một lệnh để hoàn nguyên di chuyển cuối cùng và sau đó tôi chỉ có thể xóa tệp di chuyển?

Câu trả lời:


794

Bạn có thể hoàn nguyên bằng cách di chuyển đến lần di chuyển trước.

Ví dụ: nếu hai lần di chuyển cuối cùng của bạn là:

  • 0010_previous_migration
  • 0011_migration_to_revert

Sau đó, bạn sẽ làm:

./manage.py migrate my_app 0010_previous_migration 

Sau đó bạn có thể xóa di chuyển 0011_migration_to_revert .

Nếu bạn đang sử dụng Django 1.8+, bạn có thể hiển thị tên của tất cả các lần di chuyển với

./manage.py showmigrations my_app

Để đảo ngược tất cả các lần di chuyển cho một ứng dụng, bạn có thể chạy:

./manage.py migrate my_app zero

7
Tôi đã thấy nhiều câu trả lời về SO cho vấn đề này đã cũ và đơn giản là không còn hoạt động nữa. +1 vì điều này hoạt động với Django 1.8.
AlanSE

2
Làm thế nào nếu ứng dụng chỉ có một tệp di chuyển / di chuyển ban đầu. và tôi cần hoàn tác di chuyển ban đầu?
Adiyat Mubarak

37
Các migratelet lệnh là bạn sử dụng ./manage.py migrate my_app zerođể unapply tất cả di cư cho các ứng dụng.
Alasdair

4
Vì một số lý do, ./manage.py di chuyển my_app 0010_preingly_migration không hoạt động với tôi. Vì vậy, tôi đã thử sử dụng ./manage.py di chuyển my_app 0010 và nó đã hoạt động. Bất kỳ lý do tại sao Django 1.8 sẽ không lấy toàn bộ tên di chuyển?
Varun Verma

4
Miễn là bạn đang sử dụng tên di chuyển thực tế của mình, và không '0010_previous_migration', tôi không biết tại sao bạn sẽ thấy hành vi đó.
Alasdair

36

Câu trả lời của Alasdair bao gồm những điều cơ bản

  • Xác định các di chuyển bạn muốn bằng ./manage.py showmigrations
  • migrate sử dụng tên ứng dụng và tên di chuyển

Nhưng cần phải chỉ ra rằng không phải tất cả các cuộc di cư đều có thể đảo ngược. Điều này xảy ra nếu Django không có quy tắc để thực hiện đảo ngược. Đối với hầu hết các thay đổi mà bạn tự động thực hiện di chuyển theo ./manage.py makemigrations, việc đảo ngược sẽ có thể. Tuy nhiên, các tập lệnh tùy chỉnh sẽ cần phải có cả văn bản chuyển tiếp và ngược lại, như được mô tả trong ví dụ ở đây:

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

Làm thế nào để thực hiện đảo ngược không-op

Nếu bạn đã có một RunPythonhoạt động, thì có lẽ bạn chỉ muốn sao lưu di chuyển mà không viết một kịch bản đảo ngược logic chặt chẽ. Việc hack nhanh vào ví dụ từ các tài liệu (liên kết ở trên) cho phép điều này, khiến cơ sở dữ liệu ở trạng thái giống như sau khi di chuyển được áp dụng, ngay cả sau khi đảo ngược nó.

# -*- 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),
    ]

Điều này hoạt động cho Django 1.8, 1.9


Cập nhật: Một cách tốt hơn để viết này sẽ là thay thế lambda apps, schema_editor: Nonebằng migrations.RunPython.nooptrong đoạn trích ở trên. Cả hai đều có chức năng giống nhau. (ghi nhận ý kiến)


5
Từ Django 1.8 trở đi, bạn nên sử dụng RunPython.noopthay vì lambda nội tuyến hoặc tương đương: docs.djangoproject.com/en/1.8/ref/migration-operations/ory
SpoonMeiser

@SpoonMeiser Trong cú pháp của ví dụ, tôi nghĩ rằng nó trông như thế migrations.RunPython(forwards_func, migrations.RunPython.noop). Cần kiểm tra chức năng đó. Điều đó sẽ được thêm vào như một câu trả lời hoặc chỉnh sửa đôi khi.
AlanSE

13

Đây là giải pháp của tôi, vì giải pháp trên không thực sự bao gồm trường hợp sử dụng, khi bạn sử dụng RunPython .

Bạn có thể truy cập bảng thông qua ORM với

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

Vì vậy, bạn có thể truy vấn các bảng và xóa các mục có liên quan đến bạn. Bằng cách này bạn có thể sửa đổi chi tiết. Với RynPythonviệc di chuyển, bạn cũng cần chăm sóc dữ liệu đã được thêm / thay đổi / xóa. Ví dụ trên chỉ hiển thị, cách bạn truy cập bảng thông qua Djang ORM.


Khi tạo các mô hình mới bằng ForeignKeys, với nhiều lần di chuyển, sau đó nhận ra tất cả là sai và khởi động lại 2-3 lần di chuyển ngược, với các mô hình mới nhưng đôi khi cùng tên mô hình hoặc cùng tên mối quan hệ ... giải pháp này rõ ràng là người chiến thắng. Tôi đã có thông báo lỗi như django.db.utils.ProgrammingError: relation "<relation name>" already existsvậy vì vậy tôi đã gửi một migrate --fakelỗi sai, vì vậy tôi đã cố gắng quay lại, sau đó tôi đã nhận được psycopg2.ProgrammingError: relation "<other <relation name>" does not existTHANKS
onekiloparsec

10

Một điều khác mà bạn có thể làm là xóa bảng được tạo thủ công.

Cùng với đó, bạn sẽ phải xóa tệp di chuyển cụ thể đó. Ngoài ra, bạn sẽ phải xóa mục cụ thể đó trong bảng di chuyển django (có thể là mục cuối cùng trong trường hợp của bạn) có liên quan đến việc di chuyển cụ thể đó.


hãy cẩn thận trong trường hợp này - bạn có nghĩa vụ xác minh db là đủ.
Sławomir Lenart

4
Tôi sẽ thêm RẤT cẩn thận. Bạn có thể phá vỡ rất nhiều thứ trong Postgres, ví dụ như các ràng buộc.
joedborg

9

Không xóa tệp di chuyển cho đến sau khi đảo ngược. Tôi đã mắc lỗi này và không có tệp di chuyển, cơ sở dữ liệu không biết phải xóa những gì.

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

Xóa tệp di chuyển. Khi di chuyển mong muốn trong mô hình của bạn ...

python manage.py makemigrations
python manage.py migrate

8

Tôi đã làm điều này trong 1.9.1 (để xóa di chuyển cuối cùng hoặc mới nhất được tạo):

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

    thí dụ: rm myapp/migrations/0011*

  2. đăng nhập vào cơ sở dữ liệu và chạy SQL này (postgres trong ví dụ này)

    delete from django_migrations where name like '0011%';

Sau đó tôi đã có thể tạo các di chuyển mới bắt đầu bằng số di chuyển mà tôi vừa xóa (trong trường hợp này là 11).


1
+1 Mặc dù điều này sẽ hoạt động, Bạn cần lưu cách này như là phương sách cuối cùng. Ngoài ra, bạn phải nhớ chỉnh sửa / thả cột / bảng trong đó di chuyển có vấn đề đã đóng góp.
nehem

điểm hay - Tôi đã sử dụng điều này khi tôi tạo một di chuyển nhưng chưa chạy "./manage.py di chuyển"
MIkee 23/2/2017

3

Câu trả lời này dành cho các trường hợp tương tự nếu câu trả lời hàng đầu của Alasdair không có ích . (Ví dụ: nếu di chuyển không mong muốn được tạo lại một lần nữa với mỗi lần di chuyển mới hoặc nếu di chuyển lớn hơn không thể được hoàn nguyên hoặc bảng đã bị xóa theo cách thủ công.)

... Xóa di chuyển mà không tạo di chuyển mới?

TL; DR : Bạn có thể xóa một vài lần di chuyển được hoàn nguyên (nhầm lẫn) cuối cùng và thực hiện một lần di chuyển mới sau khi sửa mô hình . Bạn cũng có thể sử dụng các phương thức khác để cấu hình nó để không tạo bảng bằng cách di chuyển lệnh. Di chuyển cuối cùng phải được tạo để nó phù hợp với các mô hình hiện tại .


Các trường hợp tại sao mọi người không muốn tạo bảng cho Mô hình phải tồn tại:

A) Không có bảng như vậy tồn tại trong cơ sở dữ liệu không có máy và không có điều kiện

  • Khi nào: Đó là một mô hình cơ sở chỉ được tạo cho kế thừa mô hình của mô hình khác.
  • Giải pháp: Đặtclass Meta: abstract = True

B) Bảng được tạo hiếm khi, bằng cách khác hoặc thủ công theo cách đặc biệt.

  • Giải pháp: Sử dụng class Meta: managed = False
    Việc di chuyển được tạo, nhưng không bao giờ được sử dụng, chỉ trong các thử nghiệm. Tệp di chuyển rất quan trọng, nếu không, các kiểm tra cơ sở dữ liệu không thể chạy, bắt đầu từ trạng thái ban đầu có thể lặp lại.

C) Bảng chỉ được sử dụng trên một số máy (ví dụ như trong quá trình phát triển).

  • Giải pháp: Di chuyển mô hình sang một ứng dụng mới được thêm vào INSTALLED_APPS chỉ trong các điều kiện đặc biệt hoặc sử dụng một điều kiện class Meta: managed = some_switch.

D) Dự án sử dụng nhiều cơ sở dữ liệu trongsettings.DATABASES


Di chuyển được tạo trong tất cả các trường hợp A), B), C), D) với Django 1.9+ (và chỉ trong các trường hợp B, C, D với Django 1.8), nhưng chỉ áp dụng cho cơ sở dữ liệu trong các trường hợp thích hợp hoặc có thể không bao giờ Yêu cầu như vậy. Di chuyển là cần thiết để chạy thử nghiệm kể từ Django 1.8. Trạng thái hiện tại hoàn toàn có liên quan được ghi lại bằng cách di chuyển ngay cả đối với các mô hình có Managed = false trong Django 1.9+ để có thể tạo ForeignKey giữa các mô hình được quản lý / không được quản lý hoặc để có thể tạo mô hình được quản lý = True sau này. (Câu hỏi này đã được viết tại thời điểm Django 1.8. Mọi thứ ở đây phải hợp lệ cho các phiên bản từ 1.8 đến 2.2 hiện tại.)

Nếu lần di chuyển cuối cùng là (không) dễ dàng hoàn nguyên thì có thể thận trọng (sau khi sao lưu cơ sở dữ liệu) thực hiện hoàn nguyên giả ./manage.py migrate --fake my_app 0010_previous_migration , xóa bảng theo cách thủ công.

Nếu cần, hãy tạo một di chuyển cố định từ mô hình cố định và áp dụng nó mà không thay đổi cấu trúc cơ sở dữ liệu ./manage.py migrate --fake my_app 0011_fixed_migration.


3

Nếu bạn đang phải đối mặt với rắc rối trong khi hoàn nguyên việc di chuyển và bằng cách nào đó đã làm hỏng nó, bạn có thể thực hiện fakedi chuyển.

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

Đối với phiên bản django <1.7, điều này sẽ tạo mục trong south_migrationhistorybảng, bạn cần xóa mục đó.

Bây giờ bạn sẽ có thể hoàn nguyên việc di chuyển trở lại một cách dễ dàng.

Tái bút: Tôi đã bị mắc kẹt trong nhiều thời gian và thực hiện di chuyển giả và sau đó quay trở lại đã giúp tôi ra ngoài.


1
Câu trả lời này dành cho Django <1.7.
hynekcer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.