Django 2.1 - Writing database migrations

डेटाबेस माइग्रेशन लिखना




django

डेटाबेस माइग्रेशन लिखना

यह दस्तावेज़ बताता है कि विभिन्न परिदृश्यों के लिए डेटाबेस माइग्रेशन की संरचना और लेखन कैसे किया जा सकता है। पलायन पर परिचयात्मक सामग्री के लिए, विषय गाइड देखें।

डेटा माइग्रेशन और कई डेटाबेस

कई डेटाबेस का उपयोग करते समय, आपको यह पता लगाने की आवश्यकता हो सकती है कि किसी विशेष डेटाबेस के खिलाफ माइग्रेशन चलाना है या नहीं। उदाहरण के लिए, आप किसी विशेष डेटाबेस पर केवल एक माइग्रेशन चलाना चाहते हैं।

ऐसा करने के लिए कि आप RunPython कनेक्शन को एक RunPython ऑपरेशन के अंदर schema_editor.connection.alias विशेषता को schema_editor.connection.alias हैं:

from django.db import migrations

def forwards(apps, schema_editor):
    if schema_editor.connection.alias != 'default':
        return
    # Your migration code goes here

class Migration(migrations.Migration):

    dependencies = [
        # Dependencies to other migrations
    ]

    operations = [
        migrations.RunPython(forwards),
    ]

आप ऐसे संकेत भी प्रदान कर सकते हैं जो डेटाबेस राउटर के allow_migrate() विधि से **hints रूप में दिए allow_migrate() :

class MyRouter:

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if 'target_db' in hints:
            return db == hints['target_db']
        return True

फिर, अपने माइग्रेशन में इसका लाभ उठाने के लिए, निम्नलिखित कार्य करें:

from django.db import migrations

def forwards(apps, schema_editor):
    # Your migration code goes here
    ...

class Migration(migrations.Migration):

    dependencies = [
        # Dependencies to other migrations
    ]

    operations = [
        migrations.RunPython(forwards, hints={'target_db': 'default'}),
    ]

यदि आपका RunPython या RunSQL ऑपरेशन केवल एक मॉडल को प्रभावित करता है, तो इसे रूटर के रूप में पारदर्शी बनाने के लिए एक संकेत के रूप में model_name पास करना अच्छा है। यह पुन: प्रयोज्य और तृतीय-पक्ष एप्लिकेशन के लिए विशेष रूप से महत्वपूर्ण है।

पलायन जो अद्वितीय क्षेत्रों को जोड़ते हैं

एक "सादे" माइग्रेशन को लागू करना, जो मौजूदा पंक्तियों के साथ एक गैर-अशक्त फ़ील्ड को तालिका में जोड़ता है, एक त्रुटि उत्पन्न करेगा क्योंकि मौजूदा पंक्तियों को पॉप्युलेट करने के लिए उपयोग किया जाने वाला मान केवल एक बार उत्पन्न होता है, इस प्रकार अद्वितीय बाधा को तोड़ता है।

इसलिए, निम्नलिखित कदम उठाए जाने चाहिए। इस उदाहरण में, हम एक डिफ़ॉल्ट मान के साथ एक गैर- UUIDField । अपनी आवश्यकताओं के अनुसार संबंधित क्षेत्र को संशोधित करें।

  • अपने मॉडल पर फ़ील्ड को default=uuid.uuid4 और unique=True तर्कों के साथ जोड़ें (आपके द्वारा जोड़े जा रहे फ़ील्ड के प्रकार के लिए एक उपयुक्त डिफ़ॉल्ट चुनें)।
  • makemigrations कमांड चलाएं। यह एक AddField ऑपरेशन के साथ एक माइग्रेशन उत्पन्न करना चाहिए।
  • एक ही ऐप के लिए दो खाली माइग्रेशन फ़ाइलों को उत्पन्न करें, जो दो बार makemigrations myapp --empty । हमने नीचे दिए गए उदाहरणों में उन्हें सार्थक नाम देने के लिए माइग्रेशन फ़ाइलों का नाम बदला है।
  • ऑटो-जनरेट किए गए माइग्रेशन (तीन नई फ़ाइलों में से पहली) से AddField ऑपरेशन को अंतिम माइग्रेशन में जोड़ें, AddField को AlterField बदलें, और AlterField और models आयात जोड़ें। उदाहरण के लिए:

    # Generated by Django A.B on YYYY-MM-DD HH:MM
    from django.db import migrations, models
    import uuid
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('myapp', '0005_populate_uuid_values'),
        ]
    
        operations = [
            migrations.AlterField(
                model_name='mymodel',
                name='uuid',
                field=models.UUIDField(default=uuid.uuid4, unique=True),
            ),
        ]
    
  • पहली माइग्रेशन फ़ाइल संपादित करें। उत्पन्न माइग्रेशन क्लास इस तरह दिखना चाहिए:

    class Migration(migrations.Migration):
    
        dependencies = [
            ('myapp', '0003_auto_20150129_1705'),
        ]
    
        operations = [
            migrations.AddField(
                model_name='mymodel',
                name='uuid',
                field=models.UUIDField(default=uuid.uuid4, unique=True),
            ),
        ]
    

    unique=True बदलें unique=True to null=True - यह मध्यस्थ नल क्षेत्र बनाएगा और अद्वितीय अवरोध पैदा करेगा जब तक कि हम सभी पंक्तियों पर अद्वितीय मानों को पॉप्युलेट नहीं करते।

  • पहली खाली माइग्रेशन फ़ाइल में, प्रत्येक मौजूदा पंक्ति के लिए एक अद्वितीय मान (उदाहरण में UUID) उत्पन्न करने के लिए RunPython या RunSQL ऑपरेशन जोड़ें। इसके अलावा uuid का एक आयात जोड़ें। उदाहरण के लिए:

    # Generated by Django A.B on YYYY-MM-DD HH:MM
    from django.db import migrations
    import uuid
    
    def gen_uuid(apps, schema_editor):
        MyModel = apps.get_model('myapp', 'MyModel')
        for row in MyModel.objects.all():
            row.uuid = uuid.uuid4()
            row.save(update_fields=['uuid'])
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('myapp', '0004_add_uuid_field'),
        ]
    
        operations = [
            # omit reverse_code=... if you don't want the migration to be reversible.
            migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
        ]
    
  • अब आप migrate कमांड के साथ हमेशा की तरह माइग्रेशन लागू कर सकते migrate

    ध्यान दें कि एक दौड़ की स्थिति है यदि आप वस्तुओं को बनाने की अनुमति देते हैं जबकि यह प्रवास चल रहा है। AddField के बाद और AddField से पहले बनाई गई वस्तुओं में उनका मूल RunPython ओवरराइट होगा।

गैर-परमाणु प्रवासन

डीडीएल लेनदेन (SQLite और PostgreSQL) का समर्थन करने वाले डेटाबेस पर, माइग्रेशन डिफ़ॉल्ट रूप से लेनदेन के अंदर चलेगा। बड़े तालिकाओं पर डेटा माइग्रेशन करने जैसे मामलों का उपयोग करने के लिए, आप atomic विशेषता को False सेट करके लेनदेन में माइग्रेशन को रोकने से रोक सकते हैं:

from django.db import migrations

class Migration(migrations.Migration):
    atomic = False

इस तरह के प्रवास के भीतर, सभी ऑपरेशन बिना किसी लेनदेन के चलाए जाते हैं। atomic() का उपयोग करके या atomic=True से RunPython लेनदेन के अंदर प्रवास के कुछ हिस्सों को निष्पादित करना संभव है।

यहां एक गैर-परमाणु डेटा प्रवासन का एक उदाहरण है जो छोटे बैचों में एक बड़ी तालिका को अपडेट करता है:

import uuid

from django.db import migrations, transaction

def gen_uuid(apps, schema_editor):
    MyModel = apps.get_model('myapp', 'MyModel')
    while MyModel.objects.filter(uuid__isnull=True).exists():
        with transaction.atomic():
            for row in MyModel.objects.filter(uuid__isnull=True)[:1000]:
                row.uuid = uuid.uuid4()
                row.save()

class Migration(migrations.Migration):
    atomic = False

    operations = [
        migrations.RunPython(gen_uuid),
    ]

atomic विशेषता का उन डेटाबेसों पर प्रभाव नहीं पड़ता है जो DDL लेनदेन (जैसे MySQL, Oracle) का समर्थन नहीं करते हैं।

पलायन का क्रम नियंत्रित करना

Django उस क्रम को निर्धारित करता है जिसमें माइग्रेशन को प्रत्येक माइग्रेशन के फ़ाइल नाम से नहीं, बल्कि Migration क्लास पर दो गुणों का उपयोग करके एक ग्राफ़ run_before dependencies : dependencies और run_before

यदि आपने makemigrations कमांड का उपयोग किया है, तो संभवतः आपने पहले से ही कार्रवाई में dependencies देखी हैं क्योंकि ऑटो-निर्मित माइग्रेशन ने इसे उनकी निर्माण प्रक्रिया के हिस्से के रूप में परिभाषित किया है।

इस तरह dependencies संपत्ति घोषित की जाती है:

from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0123_the_previous_migration'),
    ]

आमतौर पर यह पर्याप्त होगा, लेकिन समय-समय पर आपको यह सुनिश्चित करने की आवश्यकता हो सकती है कि आपका माइग्रेशन अन्य माइग्रेशन से पहले चलता है। यह उपयोगी है, उदाहरण के लिए, आपके AUTH_USER_MODEL प्रतिस्थापन के बाद चलने वाले तृतीय-पक्ष एप्लिकेशन के माइग्रेशन बनाने के लिए।

इसे प्राप्त करने के लिए, उन सभी माइग्रेशनों को run_before जो आपके Migration क्लास के run_before में आप पर निर्भर होने चाहिए:

class Migration(migrations.Migration):
    ...

    run_before = [
        ('third_party_app', '0001_do_awesome'),
    ]

जब संभव हो तो run_before पर dependencies का उपयोग dependencies पसंद करें। यदि आप जो लिखना चाहते हैं, उसके बाद माइग्रेशन में dependencies को निर्दिष्ट करने के लिए यह अवांछनीय या अव्यवहारिक है, तो आपको केवल run_before उपयोग करना चाहिए।

तृतीय-पक्ष एप्लिकेशन के बीच डेटा स्थानांतरित करना

आप डेटा को एक तृतीय-पक्ष एप्लिकेशन से दूसरे में स्थानांतरित करने के लिए डेटा माइग्रेशन का उपयोग कर सकते हैं।

यदि आप बाद में पुराने ऐप को हटाने की योजना बनाते हैं, तो आपको पुराने ऐप इंस्टॉल होने या न होने के आधार पर dependencies संपत्ति सेट करनी होगी। एक बार पुराने ऐप को अनइंस्टॉल करने के बाद, आपके पास निर्भरताएँ गायब हो जाएंगी। इसी तरह, आपको apps.get_model() कॉल में LookupError को पकड़ना होगा जो पुराने ऐप से मॉडल को पुनर्प्राप्त करता है। यह दृष्टिकोण आपको पहली बार स्थापित किए बिना और फिर पुराने ऐप को अनइंस्टॉल किए बिना अपनी परियोजना को तैनात करने की अनुमति देता है।

यहाँ एक नमूना प्रवासन है:

from django.apps import apps as global_apps
from django.db import migrations

def forwards(apps, schema_editor):
    try:
        OldModel = apps.get_model('old_app', 'OldModel')
    except LookupError:
        # The old app isn't installed.
        return

    NewModel = apps.get_model('new_app', 'NewModel')
    NewModel.objects.bulk_create(
        NewModel(new_attribute=old_object.old_attribute)
        for old_object in OldModel.objects.all()
    )

class Migration(migrations.Migration):
    operations = [
        migrations.RunPython(forwards, migrations.RunPython.noop),
    ]
    dependencies = [
        ('myapp', '0123_the_previous_migration'),
        ('new_app', '0001_initial'),
    ]

    if global_apps.is_installed('old_app'):
        dependencies.append(('old_app', '0001_initial'))

यह भी विचार करें कि माइग्रेशन के अप्रभावी होने पर आप क्या करना चाहते हैं। आप या तो कुछ नहीं कर सकते हैं (जैसा कि ऊपर दिए गए उदाहरण में है) या नए एप्लिकेशन से कुछ या सभी डेटा हटा दें। तदनुसार RunPython ऑपरेशन के दूसरे तर्क को समायोजित करें।

प्रबंधित करने के लिए एक अप्रबंधित मॉडल को बदलना

यदि आप managed=False करने के लिए एक अप्रबंधित मॉडल ( managed=False ) बदलना चाहते हैं, तो आपको managed=False को हटाना होगा और मॉडल में अन्य स्कीमा-संबंधी परिवर्तन करने से पहले एक माइग्रेशन उत्पन्न करना होगा, क्योंकि स्कीमा में माइग्रेशन में प्रकट होने वाले परिवर्तन जिसमें ऑपरेशन होता है परिवर्तन Meta.managed लागू नहीं किया जा सकता है।