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

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

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

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



답변

데이터 손실이 발생할 수 있으므로 이전 답변을 제거하고 있습니다. 으로 오잔 언급 , 우리는 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)
    ]


답변

이를 사용하면 상당히 쉽게 수행 할 수 있습니다 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)
    ]


답변

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

2 단계가 더 필요합니다.

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

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


답변

내가 한 방법 (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로 계속 응답하십시오.


답변

나는 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그렇지 않으면 오류가 발생합니다 (예 : 존재하지 않음).

답변

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

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

답변

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