두 Django 앱간에 모델을 이동하는 방법 (Django 1.7)


133

약 1 년 전에 프로젝트를 시작했고 모든 새로운 개발자와 마찬가지로 구조에 너무 집중하지 않았지만 이제는 Django와 함께 내 프로젝트 레이아웃이 주로 내 모델의 구조가 끔찍한 것처럼 보이기 시작했습니다. .

나는 주로 단일 응용 프로그램에서 모델을 보유하고 있으며 실제로 대부분의 모델은 자체 응용 프로그램에 있어야합니다.이를 해결하고 남쪽으로 이동했지만 외래 키 요법으로 인해 까다 롭고 실제로 어렵다는 것을 알았습니다.

그러나 Django 1.7으로 인해 마이그레이션 지원 기능이 내장되어 있으므로 지금 더 좋은 방법이 있습니까?


4
허용되는 답변 변경을 고려할 수 있습니다.
Babken Vardanyan

장래 3.x 여기에 오는 사람들과 Realpython.com/move-django-model/…에 자세히 설명 된 접근 방식 이 저에게 효과적 이었습니다. 이전 앱의 모델과 새 앱의 모델간에 여러 외래 키가있었습니다.
pradeepcep

답변:


16

데이터 손실이 발생할 수 있으므로 이전 답변을 제거하고 있습니다. 으로 오잔 언급 , 우리는 2 마이그레이션 각 응용 프로그램에서 계정을 생성 할 수 있습니다. 이 게시물 아래의 의견은 이전 답변을 나타냅니다.

첫 번째 앱에서 모델을 제거하기위한 첫 번째 마이그레이션

$ python manage.py makemigrations old_app --empty

이러한 작업을 포함하도록 마이그레이션 파일을 편집하십시오.

class Migration(migrations.Migration):

    database_operations = [migrations.AlterModelTable('TheModel', 'newapp_themodel')]

    state_operations = [migrations.DeleteModel('TheModel')]

    operations = [
      migrations.SeparateDatabaseAndState(
        database_operations=database_operations,
        state_operations=state_operations)
    ]

첫 번째 마이그레이션에 의존하고 두 번째 앱에서 새 테이블을 생성하는 두 번째 마이그레이션. 모델 코드를 두 번째 앱으로 옮긴 후

$ python manage.py makemigrations new_app 

마이그레이션 파일을 이와 같이 편집하십시오.

class Migration(migrations.Migration):

    dependencies = [
        ('old_app', 'above_migration')
    ]

    state_operations = [
        migrations.CreateModel(
            name='TheModel',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
            ],
            options={
                'db_table': 'newapp_themodel',
            },
            bases=(models.Model,),
        )
    ]

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]

나는 기존의 데이터와 내가 잃을 수없는 많은 것을 가지고 있는데, 아마도 이것으로 할 수 있습니까?
Sam Buckingham

@KevinChristopherHenry 코드를 수정했습니다. 기존 데이터가 유지됩니다.
ChillarAnand

@SamBuckingham 예, 수정 된 코드를 사용하여 데이터 손실없이 마이그레이션 할 수 있습니다.
ChillarAnand

2
나는 그것이 최선의 방법이 될 것이라고 생각합니다. 모든 도움을 주신 분들께 감사드립니다.
Sam Buckingham

1
IMO 이것은 잘못된 솔루션입니다. 마이그레이션의 기본 가정은 ./manage.py migrate모든 것을 실행하면 모든 것이 양호한 상태로 종료 된다는 것입니다. 마이그레이션을 수동으로 가짜로하는 것은 잘못된 방법입니다.
jb.

341

이를 사용하면 상당히 쉽게 수행 할 수 있습니다 migrations.SeparateDatabaseAndState. 기본적으로 데이터베이스 작업을 사용하여 두 상태 작업과 동시에 테이블 이름을 바꾸어 한 앱의 기록에서 모델을 제거하고 다른 앱의 기록에서 모델을 만듭니다.

오래된 앱에서 제거

python manage.py makemigrations old_app --empty

마이그레이션에서 :

class Migration(migrations.Migration):

    dependencies = []

    database_operations = [
        migrations.AlterModelTable('TheModel', 'newapp_themodel')
    ]

    state_operations = [
        migrations.DeleteModel('TheModel')
    ]

    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations=database_operations,
            state_operations=state_operations)
    ]

새 앱에 추가

먼저 모델을 새 앱의 model.py에 복사 한 후 다음을 수행하십시오.

python manage.py makemigrations new_app

순진한 CreateModel작업을 단독 작업으로 사용하여 마이그레이션을 생성 합니다. SeparateDatabaseAndState테이블을 다시 작성하지 않도록 조작으로 랩 하십시오. 또한 이전 마이그레이션을 종속성으로 포함하십시오.

class Migration(migrations.Migration):

    dependencies = [
        ('old_app', 'above_migration')
    ]

    state_operations = [
        migrations.CreateModel(
            name='TheModel',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
            ],
            options={
                'db_table': 'newapp_themodel',
            },
            bases=(models.Model,),
        )
    ]

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]

14
정말 좋은 설명입니다. 이것은 데이터 손실을 피하기 위해 테이블의 이름을 바꾸는 대답입니다.
Remiz

11
이것이 가장 좋은 방법이며 내 것보다 훨씬 낫습니다. 내 답변 상단에 메모를 추가했습니다.
ChillarAnand

4
이 작업을 수행했지만이 이후에 newapp에서 "makemigrations"를 실행하면 AlterModelTable 마이그레이션이 None으로 이름이 변경됩니다.
Diego Ponciano

4
이 지침에 따라 내 문제를 해결할 수있는 방법을 찾았습니다. 필수 필드 인 외래 키 참조가 있으면 문제가 더 복잡합니다. 참조를 옮기기 위해 몇 단계를 추가해야했습니다.
Nostalg.io

14
여러 요청으로 인해 GitHub 예제를 사용하여 FK 모델 마이그레이션에 대한 자세한 답변을 만들었습니다. stackoverflow.com/questions/30601107/…
Nostalg.io

26

같은 문제가 발생했습니다. 오잔의 대답 은 많은 도움이되었지만 불행히도 충분하지 않았습니다. 실제로 이동하려는 모델에 몇 가지 ForeignKey 연결이있었습니다. 두통 후 나는 해결책을 찾았으므로 사람들의 시간을 해결하기 위해 게시하기로 결정했습니다.

2 단계가 더 필요합니다.

  1. 작업을 수행하기 전에 모든 ForeignKey연결을 TheModel로 변경하십시오 Integerfield. 그런 다음 실행python manage.py makemigrations
  2. Ozan의 단계를 수행 한 후 외래 키를 다시 변환하십시오 . ForeignKey(TheModel)대신에 다시 넣으십시오 IntegerField(). 그런 다음 마이그레이션을 다시 수행하십시오 ( python manage.py makemigrations). 그런 다음 마이그레이션 할 수 있으며 작동해야합니다 ( python manage.py migrate)

도움이 되길 바랍니다. 물론 나쁜 놀라움을 피하기 위해 프로덕션에서 시도하기 전에 로컬에서 테스트하십시오 :)


8
ManyToManyField 관계는 어떻습니까?
tomcounsell

1
@tomcounsell 큰 의견, 나는 마이그레이션 목적으로 만 특정 통해 모델을 추가하여 가정합니다. 데이터를 그대로 유지하려면 많은 작업이 필요합니다.
Wtower

다 대다 관계는 일반적으로 두 개의 외래 키가있는 테이블이므로 SQL 관점에서이 답변의 트릭을 적용 할 수 있습니다. 그러나 Django를 통해서만 이것을 달성하기 위해 내가 생각할 수있는 한 가지 접근법은 @ ozan 답변을 따르는 것입니다. 첫 번째 단계는 MTM 관계에 관련된 테이블을 복제하는 것입니다 (각 앱의 한 버전의 듀피) 모든 외래 키를 새 앱으로 마이그레이션 한 다음 이전 앱에서 듀피 만 삭제합니다. 면책 조항 : 나는 테스트하지 않았습니다 :)
Arnaud P

15

내가 한 방법 (Django = = 1.8에서 postgres로 테스트 했으므로 1.7)

상태

app1.YourModel

하지만 당신은 그것을 원합니다 : app2.YourModel

  1. YourModel (코드)을 app1에서 app2로 복사하십시오.
  2. 이것을 app2에 추가하십시오.

    Class Meta:
        db_table = 'app1_yourmodel'
  3. $ python manage.py makemigrations app2

  4. 새로운 마이그레이션 (예 : 0009_auto_something.py)은 app2에서 migrations.CreateModel () 문을 사용하여 이루어지며,이 문을 app2의 초기 마이그레이션 (예 : 0001_initial.py)으로 옮깁니다 (항상 항상 있었던 것처럼). 이제 생성 된 마이그레이션 = 0009_auto_something.py를 제거하십시오.

  5. app2.YourModel과 같이 항상 행동했던 것처럼 이제 마이그레이션에서 app1.YourModel의 존재를 제거하십시오. 의미 : CreateModel 문과 그 이후에 사용한 모든 조정 또는 데이터 마이그레이션을 주석 처리하십시오.

  6. 물론 app1.YourModel에 대한 모든 참조는 프로젝트를 통해 app2.YourModel로 변경해야합니다. 또한 app1에 가능한 모든 외래 키를 잊지 마십시오. 마이그레이션시 Model은 app2로 변경해야합니다.

  7. 이제 $ python manage.py 마이그레이션을 수행하면 아무것도 변경되지 않으며 $ python manage.py makemigrations를 수행해도 새로운 것이 감지되지 않습니다.

  8. 이제 마무리 손질 : app2.YourModel에서 Class Meta를 제거하고 $ python manage.py makemigrations app2 && python manage.py migrate app2를 수행하십시오 (이 마이그레이션을 살펴보면 다음과 같이 표시됩니다).

        migrations.AlterModelTable(
        name='yourmodel',
        table=None,
    ),

table = 없음은 기본 table-name을 사용하며이 경우 app2_yourmodel이됩니다.

  1. 완료, 데이터가 저장되었습니다.

PS는 마이그레이션하는 동안 content_type app1.yourmodel이 제거되었으며 삭제할 수 있음을 알 수 있습니다. 당신이 그것을 사용하지 않는 경우에만 당신은 그것에 예라고 말할 수 있습니다. 해당 컨텐츠 유형에 대한 FK가 그대로 유지되도록 크게 의존하는 경우, 예 또는 아니오로 대답하지 말고 수동으로 db로 이동하여 contentype app2.yourmodel을 제거하고 컨텐츠 유형 app1의 이름을 바꾸십시오. yourmodel을 app2.yourmodel로 보낸 다음 no로 계속 응답하십시오.


3
이 솔루션은 @ozan보다 확실히 "해커"이며 더 많은 편집이 필요하지만 나에게 잘 작동했습니다 (그리고 마이그레이션을 편집해도 괜찮습니다-문서에 따르면 편집 가능해야합니다).
pgcd

1
app_label = 'app1'메타 옵션 도 사용할 수 있습니다.
Wtower

천재! 이것은 ForeignKey 관계에서 나에게 효과적이었습니다. 나는 이것이 ManyToMany 필드에서도 작동한다고 생각합니다.
Babken Vardanyan

나는 당신의 단계를 밟았지만 app1에 속하는 일부 모델의 필드는 이동할 모델 (myModel)과 재귀 관계가있는 외래 키로 구성됩니다. field1 = models.ForeignKey('app1.myModel').마이그레이션 할 때 와 같이 ValueError가 발생합니다.field1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Deesha

12

나는 Ozan의 답변에 필요한대로 긴장된 핸드 코딩 마이그레이션을 얻으 므로 다음은 Ozan과 Michael의 전략을 결합하여 필요한 핸드 코딩의 양을 최소화합니다.

  1. 모델을 이동하기 전에을 실행하여 깨끗한 기준선으로 작업하고 있는지 확인하십시오 makemigrations.
  2. 에서 모델에 대한 코드를 이동 app1하는app2
  3. @Michael이 권장하는대로 db_table"새"모델 에서 메타 옵션을 사용하여 새 모델을 이전 데이터베이스 테이블을 가리 킵니다 .

    class Meta:
        db_table = 'app1_yourmodel'
  4. 를 실행하십시오 makemigrations. CreateModelapp2DeleteModel안으로 생성 됩니다 app1. 기술적으로 이러한 마이그레이션은 정확히 동일한 테이블을 참조하며 모든 데이터를 포함하여 테이블을 제거하고 다시 만듭니다.

  5. 실제로, 우리는 테이블에 아무것도하지 않기를 원합니다. 우리는 변화가 이루어 졌다고 믿기 위해 장고가 필요합니다. @Ozan의 답변에 따라 state_operations플래그 SeparateDatabaseAndState가이 작업 을 수행합니다. 그래서 우리는 모든 랩 migrations항목을 BOTH 마이그레이션 파일SeparateDatabaseAndState(state_operations=[...]). 예를 들어

    operations = [
        ...
        migrations.DeleteModel(
            name='YourModel',
        ),
        ...
    ]

    된다

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=[
            ...
            migrations.DeleteModel(
                name='YourModel',
            ),
            ...
        ])
    ]
  6. 또한 새로운 "가상" CreateModel마이그레이션 이 원래 테이블실제로 만들거나 변경 한 마이그레이션에 의존 하는지 확인해야 합니다 . 예를 들어, 새 마이그레이션이 app2.migrations.0004_auto_<date>( Create) 및 app1.migrations.0007_auto_<date>( Delete) 인 경우 가장 간단한 방법은 다음과 같습니다.

    • 종속성을 열고 app1.migrations.0007_auto_<date>복사하십시오 app1(예 :) ('app1', '0006...'),. 이는 "즉시 이전"마이그레이션이며 app1모든 실제 모델 구축 로직에 대한 종속성을 포함해야합니다.
    • app2.migrations.0004_auto_<date>방금 복사 한 종속성을 dependencies목록에 열고 추가하십시오 .

ForeignKey이동중인 모델과 관계 가 있으면 위의 작동하지 않을 수 있습니다. 이것은 다음과 같은 이유로 발생합니다.

  • ForeignKey변경 사항에 대한 종속성이 자동으로 작성되지 않습니다
  • ForeignKey변경 사항 을 랩핑하고 싶지 않으므로 변경 사항 state_operations이 테이블 조작과 분리되어 있는지 확인해야합니다.

참고 : Django 2.2에는 models.E028이 방법을 위반 하는 경고 ( )가 추가되었습니다 . 문제를 해결할 수는 managed=False있지만 테스트하지는 않았습니다.

"최소"작업 집합은 상황에 따라 다르지만 다음 절차는 대부분 / 모든 ForeignKey마이그레이션에 적용됩니다.

  1. 모델을에서 app1복사하고을app2 설정 db_table하되 FK 참조를 변경하지 마십시오.
  2. makemigrations모든 app2마이그레이션을 실행 하고 래핑합니다 state_operations(위 참조).
    • 위와 같이 app2 CreateTable최신 app1마이그레이션에 종속성을 추가하십시오.
  3. 모든 FK 참조를 새 모델로 지정하십시오. 문자열 참조를 사용하지 않는 경우 이전 모델을 맨 아래로 이동 models.py(제거하지 마십시오)하여 가져온 클래스와 경쟁하지 않도록하십시오.
  4. 실행 makemigrations하지만 아무것도 포장하지 마십시오 state_operations(FK 변경 사항이 실제로 발생해야 함). 모두의 종속성을 추가 ForeignKey마이그레이션 (즉, AlterField받는 사람) CreateTable에서 마이그레이션 app2(그렇게 그들을 추적 다음 단계에 대한 목록이 필요합니다). 예를 들면 다음과 같습니다.

    • CreateModel예 를 포함하는 마이그레이션을 찾아 해당 마이그레이션 app2.migrations.0002_auto_<date>의 이름을 복사하십시오.
    • 해당 모델에 대한 ForeignKey가있는 모든 마이그레이션을 찾습니다 (예 app2.YourModel: 다음과 같은 마이그레이션을 찾기 위해 검색) .

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
          ]
      
          operations = [
              migrations.AlterField(
                  model_name='relatedmodel',
                  name='fieldname',
                  field=models.ForeignKey(... to='app2.YourModel'),
              ),
          ]
    • CreateModel종속성으로 마이그레이션을 추가하십시오 .

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
              ('app2', '0002_auto_<date>'),
          ]  
  5. 에서 모델을 제거하십시오 app1

  6. makemigrations에서 app1마이그레이션을 실행 하고 래핑하십시오 state_operations.
    • 이전 단계에서 모든 ForeignKey마이그레이션 (예 :)에 대한 종속성을 추가 합니다 ( 및에 AlterField마이그레이션이 포함될 수 있음 ).app1app2
    • 이러한 마이그레이션을 구축 할 때 DeleteTable이미 마이그레이션에 의존 AlterField했으므로 수동으로 (즉, Alter이전 Delete) 시행 할 필요가 없었습니다 .

이 시점에서 Django가 좋습니다. 새로운 모델은 이전 테이블을 가리키고 Django의 마이그레이션은 모든 것이 적절하게 재배치되었다고 확신했습니다. @Michael의 답변에서 큰주의 사항 ContentType은 새 모델에 대해 새 것이 생성 된다는 것 입니다. ForeignKey콘텐츠 유형에 (예 :로 ) 연결하는 경우 ContentType테이블 을 업데이트하기 위해 마이그레이션을 만들어야 합니다.

나는 나 자신 (메타 옵션 및 테이블 이름)을 정리하고 싶었으므로 다음 절차 (@Michael)를 사용했습니다.

  1. db_table메타 항목 제거
  2. makemigrations데이터베이스 이름 바꾸기를 생성하기 위해 다시 실행
  3. 이 마지막 마이그레이션을 편집하고 DeleteTable마이그레이션에 의존하는지 확인하십시오 . 꼭 Delete논리적 이어야하기 때문에 필요한 것 같지는 않지만, app1_yourmodel그렇지 않으면 오류가 발생합니다 (예 : 존재하지 않음).

이것은 완벽하게 작동했습니다, 감사합니다! 어쨌든 종속성 트리의 맨 아래에 있기 때문에 마지막 마이그레이션을 편집하는 것이 중요하다고 생각하지 않습니다.
James Meakin

1
좋은 대답입니다! 마이그레이션에 닫는 괄호를 추가해야한다고 생각합니다 .SeparateDatabaseAndState, 맞습니까?
atm

이것은 나를 위해 일했습니다. 또한 @JamesMeakin처럼 마지막 마이그레이션 (3 단계, 전체 응답의 맨 마지막 줄)을 편집하지 않았고 그것은 여전히 잘 작동
메가 와트

두 번째 시나리오에서는 FK가있는 두 번째 단계에서 다음과 같은 오류가 발생하여 실패했습니다.table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
Mihai Zamfir

절차를 두 번 사용했습니다. 2.2 ( docs.djangoproject.com/en/2.2/ref/checks ) 및 2.1 ( docs.djangoproject.com/en/2.1/ref/checks )에 대한 문서를 비교하면 2.2 에 추가 된 것을 볼 수 있습니다. 문제를 해결할 수는 managed=False있지만 확인할 곳이 없습니다.
claytond

1

데이터가 크거나 복잡하지 않지만 여전히 유지 관리해야 할 중요한 또 다른 해킹 대안은 다음과 같습니다.

  • manage.py dumpdata를 사용하여 데이터 픽스쳐 가져 오기
  • 변경 사항을 관련시키지 않고 변경 사항 및 마이그레이션을 올바르게 모델링합니다.
  • 전역은 이전 모델 및 앱 이름에서 새로운 조명기로 조명기를 대체합니다.
  • manage.py loaddata를 사용하여 데이터로드

1

https://stackoverflow.com/a/47392970/8971048 에서 내 답변에서 복사했습니다.

모델을 이동해야하고 더 이상 앱에 액세스 할 수 없거나 (또는 ​​액세스하지 않으려는 경우) 새 오퍼레이션을 작성하고 마이그레이션 된 모델이 그렇지 않은 경우에만 새 모델을 작성하는 것을 고려할 수 있습니다 있다.

이 예에서는 'MyModel'을 old_app에서 myapp로 전달합니다.

class MigrateOrCreateTable(migrations.CreateModel):
    def __init__(self, source_table, dst_table, *args, **kwargs):
        super(MigrateOrCreateTable, self).__init__(*args, **kwargs)
        self.source_table = source_table
        self.dst_table = dst_table

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        table_exists = self.source_table in schema_editor.connection.introspection.table_names()
        if table_exists:
            with schema_editor.connection.cursor() as cursor:
                cursor.execute("RENAME TABLE {} TO {};".format(self.source_table, self.dst_table))
        else:
            return super(MigrateOrCreateTable, self).database_forwards(app_label, schema_editor, from_state, to_state)


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_some_migration'),
    ]

    operations = [
        MigrateOrCreateTable(
            source_table='old_app_mymodel',
            dst_table='myapp_mymodel',
            name='MyModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=18))
            ],
        ),
    ]

여러 질문에 동일한 답변을 추가하지 마십시오. 가장 좋은 것에 대답하고 나머지는 중복으로 표시하십시오. 여러 질문에 중복 답변을 추가해도 괜찮습니까?를
Petter Friberg

0

이것은 대략적으로 테스트되었으므로 DB를 백업하는 것을 잊지 마십시오!

예를 들어, 두 개의 앱이 있습니다. src_appdst_app모델을 MoveMe에서 src_app로 이동하려고 합니다 dst_app.

두 앱 모두에 대해 빈 마이그레이션을 만듭니다.

python manage.py makemigrations --empty src_app
python manage.py makemigrations --empty dst_app

의 새로운 마이그레이션이라는 것을, 가정하자 XXX1_src_app_new하고 XXX1_dst_app_new, previuos 위로 마이그레이션이 XXX0_src_app_oldXXX0_dst_app_old.

MoveMe모델의 테이블 이름을 바꾸고 ProjectState의 app_label 이름을로 바꾸는 작업을 추가하십시오 XXX1_dst_app_new. XXX0_src_app_old마이그레이션 에 대한 종속성을 추가하는 것을 잊지 마십시오 . 결과 XXX1_dst_app_new마이그레이션은 다음과 같습니다.

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

from django.db import models, migrations

# this operations is almost the same as RenameModel
# https://github.com/django/django/blob/1.7/django/db/migrations/operations/models.py#L104
class MoveModelFromOtherApp(migrations.operations.base.Operation):

    def __init__(self, name, old_app_label):
        self.name = name
        self.old_app_label = old_app_label

    def state_forwards(self, app_label, state):

        # Get all of the related objects we need to repoint
        apps = state.render(skip_cache=True)
        model = apps.get_model(self.old_app_label, self.name)
        related_objects = model._meta.get_all_related_objects()
        related_m2m_objects = model._meta.get_all_related_many_to_many_objects()
        # Rename the model
        state.models[app_label, self.name.lower()] = state.models.pop(
            (self.old_app_label, self.name.lower())
        )
        state.models[app_label, self.name.lower()].app_label = app_label
        for model_state in state.models.values():
            try:
                i = model_state.bases.index("%s.%s" % (self.old_app_label, self.name.lower()))
                model_state.bases = model_state.bases[:i] + ("%s.%s" % (app_label, self.name.lower()),) + model_state.bases[i+1:]
            except ValueError:
                pass
        # Repoint the FKs and M2Ms pointing to us
        for related_object in (related_objects + related_m2m_objects):
            # Use the new related key for self referential related objects.
            if related_object.model == model:
                related_key = (app_label, self.name.lower())
            else:
                related_key = (
                    related_object.model._meta.app_label,
                    related_object.model._meta.object_name.lower(),
                )
            new_fields = []
            for name, field in state.models[related_key].fields:
                if name == related_object.field.name:
                    field = field.clone()
                    field.rel.to = "%s.%s" % (app_label, self.name)
                new_fields.append((name, field))
            state.models[related_key].fields = new_fields

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        old_apps = from_state.render()
        new_apps = to_state.render()
        old_model = old_apps.get_model(self.old_app_label, self.name)
        new_model = new_apps.get_model(app_label, self.name)
        if self.allowed_to_migrate(schema_editor.connection.alias, new_model):
            # Move the main table
            schema_editor.alter_db_table(
                new_model,
                old_model._meta.db_table,
                new_model._meta.db_table,
            )
            # Alter the fields pointing to us
            related_objects = old_model._meta.get_all_related_objects()
            related_m2m_objects = old_model._meta.get_all_related_many_to_many_objects()
            for related_object in (related_objects + related_m2m_objects):
                if related_object.model == old_model:
                    model = new_model
                    related_key = (app_label, self.name.lower())
                else:
                    model = related_object.model
                    related_key = (
                        related_object.model._meta.app_label,
                        related_object.model._meta.object_name.lower(),
                    )
                to_field = new_apps.get_model(
                    *related_key
                )._meta.get_field_by_name(related_object.field.name)[0]
                schema_editor.alter_field(
                    model,
                    related_object.field,
                    to_field,
                )

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        self.old_app_label, app_label = app_label, self.old_app_label
        self.database_forwards(app_label, schema_editor, from_state, to_state)
        app_label, self.old_app_label = self.old_app_label, app_label

    def describe(self):
        return "Move %s from %s" % (self.name, self.old_app_label)


class Migration(migrations.Migration):

    dependencies = [
       ('dst_app', 'XXX0_dst_app_old'),
       ('src_app', 'XXX0_src_app_old'),
    ]

    operations = [
        MoveModelFromOtherApp('MoveMe', 'src_app'),
    ]

에 종속성을 추가 XXX1_dst_app_newXXX1_src_app_new. XXX1_src_app_new이후 src_app마이그레이션이 실행되도록 하는 데 필요한 no-op 마이그레이션입니다 XXX1_dst_app_new.

MoveMe에서 src_app/models.py로 이동하십시오 dst_app/models.py. 그런 다음 다음을 실행하십시오.

python manage.py migrate

그게 다야!


이 코드는 django 1.7에만 유용합니다. django 2.0에서 이것을 시도하면 작동하지 않습니다. 이는 또한 모델 이동에이 메커니즘을 사용하면 django 버전 업그레이드에 유지 보수 오버 헤드가 추가됨을 의미합니다.
Paul

0

다음을 시도해 볼 수 있습니다 (예상되지 않음).

  1. 모델을에서 src_app로 이동dest_app
  2. 이주 dest_app; 스키마 마이그레이션이 최신 src_app마이그레이션 에 의존하는지 확인하십시오 ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )
  3. 에 데이터 이전을 추가하여의 dest_app모든 데이터를 복사합니다.src_app
  4. 이주 src_app; 스키마 마이그레이션이 최신 (데이터) 마이그레이션에 의존하는지 확인하십시오 dest_app. 즉, 3 단계 마이그레이션

당신이되므로주의 복사 전체 테이블을 대신 이동 을하지만, 그 방법은 모두 애플 리케이션은 내가 더 중요하다고 생각하는 다른 앱에 속하는 테이블을 터치 할 필요가 없습니다.


0

모델 TheModel을 app_a에서 app_b로 이동한다고 가정 해 봅시다.

대체 솔루션은 기존 마이그레이션을 수동으로 변경하는 것입니다. app_a의 마이그레이션에서 오퍼레이션이 TheModel을 변경하는 것을 볼 때마다 해당 오퍼레이션을 app_b의 초기 마이그레이션 끝에 복사하는 것이 좋습니다. app_a의 마이그레이션에서 'app_a.TheModel'참조가 표시 될 때마다 'app_b.TheModel'로 변경합니다.

방금 기존 프로젝트 에서이 작업을 수행했는데 재사용 가능한 앱에 특정 모델을 추출하려고했습니다. 절차는 순조롭게 진행되었습니다. app_b에서 app_a에 대한 참조가 있으면 상황이 훨씬 어려울 것 같습니다. 또한, 도움이 될 수있는 모델에 대해 수동으로 정의 된 Meta.db_table이있었습니다.

특히 마이그레이션 기록이 변경됩니다. 원래 마이그레이션이 적용된 데이터베이스가 있어도 문제가되지 않습니다. 원래 마이그레이션과 다시 작성된 마이그레이션이 모두 동일한 데이터베이스 스키마로 끝나는 경우에는 다시 작성해도됩니다.


0
  1. 기존 모델의 이름을 'model_name_old'로 변경
  2. 이주하다
  3. 관련 모델에서 동일한 관계로 'model_name_new'라는 새 모델을 만듭니다 (예 : 사용자 모델에 user.blog_old 및 user.blog_new가 있음)
  4. 이주하다
  5. 모든 데이터를 새 모델 테이블로 마이그레이션하는 사용자 정의 마이그레이션 작성
  6. 마이그레이션을 실행하기 전과 후에 백업을 새 DB 복사본과 비교하여 이러한 마이그레이션에서 지옥을 테스트하십시오.
  7. 모든 것이 만족 스러우면 이전 모델을 삭제하십시오.
  8. 이주하다
  9. 새 모델을 올바른 이름 'model_name_new'-> 'model_name'으로 변경하십시오.
  10. 준비 서버에서 전체 마이그레이션을 테스트합니다.
  11. 사용자의 간섭없이 모든 마이그레이션을 실행하기 위해 프로덕션 사이트를 몇 분 동안 중단하십시오.

이동해야하는 각 모델에 대해 개별적으로 수행하십시오. 정수로 변경하고 외래 키로 다시 변경하여 다른 대답이 말하는 것을 제안하지 않을 것입니다. 새 외래 키가 다를 수 있으며 마이그레이션 후 행에 다른 ID가있을 가능성이 있으며 위험을 실행하고 싶지 않습니다. 외래 키로 다시 전환 할 때 ID가 일치하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.