Son geçiş nasıl geri alınır?


447

Yeni bir tablo ekleyen ve yeni bir taşıma oluşturmadan onu geri döndürüp taşımayı silmek istediğiniz bir geçiş yaptım.

Nasıl yaparım? Son geçişi geri almak için bir komut var mı ve daha sonra geçiş dosyasını silebilir miyim?

Yanıtlar:


797

Önceki taşıma işlemine geçerek geri dönebilirsiniz.

Örneğin, son iki taşıma işleminiz şuysa:

  • 0010_previous_migration
  • 0011_migration_to_revert

Sonra yapardınız:

./manage.py migrate my_app 0010_previous_migration 

Ardından taşımayı silebilirsiniz 0011_migration_to_revert.

Django 1.8+ kullanıyorsanız, tüm taşıma işlemlerinin adlarını

./manage.py showmigrations my_app

Bir uygulamanın tüm taşıma işlemlerini geri almak için şunları çalıştırabilirsiniz:

./manage.py migrate my_app zero

7
Bu soruna eski ve sadece artık işe yaramayan birçok cevap gördüm. +1 çünkü bu Django 1.8 ile çalışıyor.
AlanSE

2
Uygulamada yalnızca bir taşıma dosyası / ilk taşıma varsa. ve bu ilk göçü geri almam gerekiyor mu?
Adiyat Mubarak

37
migrateKomut let Kullanmak var ./manage.py migrate my_app zerouygulamanın tüm göçler uygulamasını kaldırmak.
Alasdair

4
Herhangi bir nedenle, ./manage.py migrat my_app 0010_previous_migration benim için çalışmadı. Bu yüzden ./manage.py migrate my_app 0010 kullanmayı denedim ve işe yaradı. Django 1.8'in taşıma adının tamamını almamasının herhangi bir nedeni var mı?
Varun Verma

4
Gerçek taşıma adınızı kullandığınız sürece değil '0010_previous_migration', bu davranışı neden göreceğinizi bilmiyorum.
Alasdair

36

Alasdair'in cevabı temelleri kapsıyor

  • İstediğiniz taşıma işlemlerini tanımlayın ./manage.py showmigrations
  • migrate uygulama adını ve taşıma adını kullanma

Ama hepsi göçler değil işaret edilmelidir olabilir tersine. Bu, Django'nun geri dönüşü yapmak için bir kuralı yoksa olur. Otomatik olarak geçiş yaptığınız çoğu değişiklik ./manage.py makemigrationsiçin geri dönüş mümkün olacaktır. Ancak, özel komut dosyalarının aşağıdaki örnekte açıklandığı gibi hem ileri hem de geri yazılmış olması gerekir:

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

Operasyon dışı geri dönüş nasıl yapılır

Bir RunPythonişleminiz varsa, belki de mantıksal olarak titiz bir ters komut dosyası yazmadan taşımayı geri almak isteyebilirsiniz. Dokümanlardaki örnekteki aşağıdaki hızlı saldırı (yukarıdaki bağlantı) buna izin verir ve veritabanını, geçiş yapıldıktan sonra bile geri döndükten sonra olduğu gibi bırakır.

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

Bu Django 1.8, 1.9 için çalışır


Güncelleme: Bu yazı daha iyi bir yolu değiştirmek şeklinde olur lambda apps, schema_editor: Noneile migrations.RunPython.noopyukarıdaki kod parçasında. Bunların ikisi de işlevsel olarak aynı şeydir. (yorumlara kredi)


5
Django 1.8'den itibaren RunPython.noopsatır içi lambda veya eşdeğeri yerine kullanmalısınız : docs.djangoproject.com/en/1.8/ref/migration-operations/…
SpoonMeiser

@SpoonMeiser Örneğin sözdiziminde, sanırım bu gibi görünüyor migrations.RunPython(forwards_func, migrations.RunPython.noop). Bunu işlevsel olarak kontrol etmeniz gerekiyor. Bu bir ara bir cevap ya da düzenleme olarak eklenmelidir.
AlanSE

13

İşte benim çözümüm, çünkü yukarıdaki çözüm kullandığınızda kullanım kutusunu gerçekten kapsamaz RunPython.

Tabloya ORM üzerinden

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

Böylece tabloları sorgulayabilir ve sizin için uygun olan girişleri silebilirsiniz. Bu şekilde ayrıntılı olarak değiştirebilirsiniz. Taşıma işlemlerinde RynPython, eklenen / değiştirilen / kaldırılan verilerle de ilgilenmeniz gerekir. Yukarıdaki örnek yalnızca, tabloya Djang ORM üzerinden nasıl eriştiğinizi gösterir.


ForeignKeys ile yeni modeller oluştururken, birden fazla geçişle, o zaman her şeyin yanlış olduğunu ve 2-3 geçişi yeni modellerle, ancak bazen aynı model adlarıyla veya aynı ilişki adlarıyla yeniden başlatırken ... bu çözüm açıkça kazanır. Bu yüzden yanlış django.db.utils.ProgrammingError: relation "<relation name>" already existsyaptığım gibi hata mesajı vardı migrate --fake, bu yüzden geri dönmeye çalıştım, sonra psycopg2.ProgrammingError: relation "<other <relation name>" does not existTEŞEKKÜRLER var
onekiloparsec

10

Yapabileceğiniz bir diğer şey, manuel olarak oluşturulan tabloyu silmektir.

Bununla birlikte, söz konusu taşıma dosyasını silmeniz gerekir. Ayrıca, django-migrations tablosundaki (muhtemelen sizin durumunuzdaki son giriş) ilgili belirli bir geçişle ilişkili olan bu girişi silmeniz gerekir .


bu durumda dikkatli olun - db'nin yeterli olduğunu doğrulamak zorundasınız.
Sławomir Lenart

4
ÇOK dikkatli eklersiniz. Postgres'teki birçok şeyi kırabilirsiniz, örneğin kısıtlamalar.
joedborg

9

Geçiş dosyasını geri dönüşün sonuna kadar silmeyin. Bu hatayı yaptım ve geçiş dosyası olmadan, veritabanı neyin kaldırılacağını bilmiyordu.

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

Taşıma dosyasını silin. İstediğiniz taşıma modellerinizde olduğunda ...

python manage.py makemigrations
python manage.py migrate

8

Bunu 1.9.1'de yaptım (oluşturulan son veya en son geçişi silmek için):

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

    misal: rm myapp/migrations/0011*

  2. veritabanına giriş yaptı ve bu SQL'i çalıştırdı (bu örnekte postgres)

    delete from django_migrations where name like '0011%';

Daha sonra, yeni sildiğim taşıma numarasıyla başlayan yeni taşıma işlemleri oluşturabildim (bu durumda, 11).


1
+1 Bu işe yarayacak olsa da, bu yolu son çare olarak kaydetmeniz gerekiyor. Ayrıca sorunlu geçişin katkıda bulunduğu sütun / tabloları düzenlemeyi / bırakmayı da hatırlamanız gerekir.
nehem

iyi bir nokta - Bir taşıma oluşturduğumda ancak henüz "./manage.py migrate" çalıştırmadığım zaman kullandım
MIkee

3

Alasdair'in en iyi cevabı yardımcı olmazsa bu cevap benzer durumlar içindir . (Örneğin, istenmeyen geçiş her yeni geçişle kısa süre sonra tekrar oluşturulursa veya geri döndürülemeyen daha büyük bir geçişte ise veya tablo manuel olarak kaldırılmışsa.)

... yeni bir taşıma oluşturmadan taşıma silinsin mi?

TL; DR : Geri döndürülen (karışık) birkaç geçişi silebilir ve modelleri düzelttikten sonra yeni bir tane oluşturabilirsiniz . Ayrıca migrate komutuyla tablo oluşturmayacak şekilde yapılandırmak için başka yöntemler de kullanabilirsiniz . Son taşıma, mevcut modellerle eşleşecek şekilde oluşturulmalıdır .


Kılıflar kimse mevcut olması gereken Modeli için bir tablo oluşturmak istemiyorum nedenleri:

A) Böyle bir tablo, hiçbir makinede ve hiçbir koşulda veritabanında bulunmamalıdır.

  • Ne zaman: Yalnızca diğer modelin model mirası için oluşturulan temel bir modeldir.
  • Çözüm: Ayarlaclass Meta: abstract = True

B) Tablo nadiren, başka bir şeyle veya manuel olarak özel bir şekilde oluşturulur.

  • Çözüm: Kullanım class Meta: managed = False
    Taşıma yalnızca testlerde oluşturulur, ancak hiçbir zaman kullanılmaz. Geçiş dosyası önemlidir, aksi takdirde tekrarlanabilir başlangıç ​​durumundan başlayarak veritabanı testleri çalıştırılamaz.

C) Masa sadece bazı makinelerde kullanılır (örneğin geliştirme aşamasında).

  • Çözüm: Modeli yalnızca özel koşullar altında INSTALLED_APPS öğesine eklenen yeni bir uygulamaya taşıyın veya bir koşul kullanın class Meta: managed = some_switch.

D) Proje,settings.DATABASES

  • Çözüm: Tablonun oluşturulacağı ve oluşturulmayacağı veritabanlarını ayırt etmek için yöntemle bir Veritabanı yönlendiricisi yazın allow_migrate.

Taşıma, Django 1.9+ ile tüm A), B), C), D) durumlarında (ve yalnızca Django 1.8 ile B, C, D durumlarında) oluşturulur, ancak veritabanına yalnızca uygun durumlarda uygulanır veya gerekli. Django 1.8'den bu yana testlerin yürütülmesi için göçler gerekli olmuştur. Yönetilen / yönetilmeyen modeller arasında bir ForeignKey oluşturmak veya modeli daha sonra yönetebilmek için yönetilen = False 1.9 + 'da yönetilen modellerde bile geçerli ilgili durumun tamamı göçlerle kaydedilir. (Bu soru Django 1.8 zamanında yazılmıştır. Buradaki her şey 1.8 ile şu anki 2.2 arasındaki sürümler için geçerli olmalıdır.)

Son taşıma (geri alınamazsa) kolayca geri döndürülemezse (veritabanı yedeklemesinden sonra) sahte bir geri alma işlemi yapmak dikkatlice yapılabilir ./manage.py migrate --fake my_app 0010_previous_migration, tabloyu manuel olarak silin.

Gerekirse, sabit modelden sabit bir taşıma oluşturun ve veritabanı yapısını değiştirmeden uygulayın ./manage.py migrate --fake my_app 0011_fixed_migration.


3

Taşımayı geri alırken sorun yaşıyorsanız ve bir şekilde onu karıştırdıysanız, faketaşıma işlemi gerçekleştirebilirsiniz .

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

İçin Django sürümü <1.7 bunda bir giriş oluşturulur south_migrationhistorymasaya, o girdiyi silmek için gerek yoktur.

Artık taşımayı kolayca geri alabileceksiniz.

PS: Çok zaman takılıp takıldım ve sahte göç yaptım ve sonra geri dönmek bana yardımcı oldu.


1
Bu cevap Django <1.7 içindir.
hynekcer
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.