Database
 sql >> डेटाबेस >  >> RDS >> Database

डाउनटाइम के बिना Django में एक इंडेक्स कैसे बनाएं

किसी भी सॉफ्टवेयर प्रोजेक्ट में डेटाबेस माइग्रेशन को मैनेज करना एक बड़ी चुनौती है। सौभाग्य से, संस्करण 1.7 के अनुसार, Django एक अंतर्निहित माइग्रेशन फ्रेमवर्क के साथ आता है। डेटाबेस में परिवर्तन के प्रबंधन में ढांचा बहुत शक्तिशाली और उपयोगी है। लेकिन ढांचे द्वारा प्रदान किए गए लचीलेपन के लिए कुछ समझौतों की आवश्यकता थी। Django माइग्रेशन की सीमाओं को समझने के लिए, आप एक प्रसिद्ध समस्या से निपटने जा रहे हैं:बिना डाउनटाइम के Django में एक इंडेक्स बनाना।

इस ट्यूटोरियल में, आप सीखेंगे:

  • कैसे और कब Django नए माइग्रेशन जेनरेट करता है
  • माइग्रेशन निष्पादित करने के लिए Django द्वारा जनरेट किए गए आदेशों का निरीक्षण कैसे करें
  • अपनी आवश्यकताओं के अनुसार माइग्रेशन को सुरक्षित रूप से कैसे संशोधित करें

यह मध्यवर्ती-स्तरीय ट्यूटोरियल उन पाठकों के लिए डिज़ाइन किया गया है जो पहले से ही Django माइग्रेशन से परिचित हैं। उस विषय के परिचय के लिए, Django माइग्रेशन:एक प्राइमर देखें।

निःशुल्क बोनस: अतिरिक्त Django ट्यूटोरियल और संसाधनों तक मुफ्त पहुंच प्राप्त करने के लिए यहां क्लिक करें जिनका उपयोग आप अपने पायथन वेब विकास कौशल को गहरा करने के लिए कर सकते हैं।


Django माइग्रेशन में इंडेक्स बनाने में समस्या

एक सामान्य परिवर्तन जो आमतौर पर तब आवश्यक हो जाता है जब आपके एप्लिकेशन द्वारा संग्रहीत डेटा बढ़ता है एक अनुक्रमणिका जोड़ रहा है। इंडेक्स का इस्तेमाल क्वेरी को तेज़ करने और आपके ऐप को तेज़ और प्रतिक्रियाशील बनाने के लिए किया जाता है।

अधिकांश डेटाबेस में, एक इंडेक्स जोड़ने के लिए टेबल पर एक विशेष लॉक की आवश्यकता होती है। एक विशेष लॉक डेटा संशोधन (डीएमएल) संचालन को रोकता है जैसे कि UPDATE , INSERT , और DELETE , जबकि सूचकांक बनाया जाता है।

कुछ कार्यों को निष्पादित करते समय डेटाबेस द्वारा ताले को निहित रूप से प्राप्त किया जाता है। उदाहरण के लिए, जब कोई उपयोगकर्ता आपके ऐप में लॉग इन करता है, तो Django last_login . को अपडेट करेगा auth_user . में फ़ील्ड टेबल। अद्यतन करने के लिए, डेटाबेस को पहले पंक्ति पर लॉक प्राप्त करना होगा। यदि पंक्ति को वर्तमान में किसी अन्य कनेक्शन द्वारा लॉक किया जा रहा है, तो आपको डेटाबेस अपवाद मिल सकता है।

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

कुछ डेटाबेस विक्रेता तालिका को लॉक किए बिना अनुक्रमणिका बनाने का एक तरीका प्रदान करते हैं। उदाहरण के लिए, किसी तालिका को लॉक किए बिना PostgreSQL में एक अनुक्रमणिका बनाने के लिए, आप CONCURRENTLY का उपयोग कर सकते हैं कीवर्ड:

CREATE INDEX CONCURRENTLY ix ON table (column);

Oracle में, एक ONLINE होता है इंडेक्स बनाते समय टेबल पर डीएमएल संचालन की अनुमति देने का विकल्प:

CREATE INDEX ix ON table (column) ONLINE;

माइग्रेशन उत्पन्न करते समय, Django इन विशेष कीवर्ड का उपयोग नहीं करेगा। माइग्रेशन को यथावत चलाने से डेटाबेस टेबल पर एक विशेष लॉक प्राप्त कर लेगा और इंडेक्स बनाते समय DML संचालन को रोक देगा।

समवर्ती रूप से एक सूचकांक बनाना कुछ चेतावनी है। अपने डेटाबेस बैकएंड के लिए विशिष्ट मुद्दों को पहले से समझना महत्वपूर्ण है। उदाहरण के लिए, PostgreSQL में एक चेतावनी यह है कि एक साथ एक इंडेक्स बनाने में अधिक समय लगता है क्योंकि इसके लिए एक अतिरिक्त टेबल स्कैन की आवश्यकता होती है।

इस ट्यूटोरियल में, आप बिना किसी डाउनटाइम के एक बड़ी टेबल पर इंडेक्स बनाने के लिए Django माइग्रेशन का उपयोग करेंगे।

नोट: इस ट्यूटोरियल का अनुसरण करने के लिए, यह अनुशंसा की जाती है कि आप PostgreSQL बैकएंड, Django 2.x, और Python 3 का उपयोग करें।

अन्य डेटाबेस बैकएंड के साथ-साथ अनुसरण करना संभव है। उन जगहों पर जहां PostgreSQL के लिए अद्वितीय SQL सुविधाओं का उपयोग किया जाता है, अपने डेटाबेस बैकएंड से मिलान करने के लिए SQL को बदलें।



सेटअप

आप एक निर्मित Sale . का उपयोग करने जा रहे हैं app . नामक ऐप में मॉडल . वास्तविक जीवन की स्थिति में, Sale . जैसे मॉडल डेटाबेस में मुख्य टेबल हैं, और वे आमतौर पर बहुत बड़ी होंगी और बहुत सारा डेटा स्टोर करेंगी:

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
    )
    charged_amount = models.PositiveIntegerField()

तालिका बनाने के लिए, प्रारंभिक माइग्रेशन जनरेट करें और इसे लागू करें:

$ python manage.py makemigrations
Migrations for 'app':
  app/migrations/0001_initial.py
    - Create model Sale

$ python manage migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0001_initial... OK

थोड़ी देर बाद, बिक्री तालिका बहुत बड़ी हो जाती है, और उपयोगकर्ता धीमेपन की शिकायत करने लगते हैं। डेटाबेस की निगरानी करते समय, आपने देखा कि बहुत सी क्वेरी sold_at . का उपयोग करती हैं कॉलम। चीजों को गति देने के लिए, आप तय करते हैं कि आपको कॉलम पर एक इंडेक्स की आवश्यकता है।

sold_at . पर एक इंडेक्स जोड़ने के लिए , आप मॉडल में निम्नलिखित परिवर्तन करते हैं:

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
    )
    charged_amount = models.PositiveIntegerField()

यदि आप इस माइग्रेशन को वैसे ही चलाते हैं, तो Django टेबल पर इंडेक्स बनाएगा, और इंडेक्स पूरा होने तक इसे लॉक कर दिया जाएगा। एक बहुत बड़ी टेबल पर इंडेक्स बनाने में कुछ समय लग सकता है, और आप डाउनटाइम से बचना चाहते हैं।

एक छोटे डेटासेट और बहुत कम कनेक्शन वाले स्थानीय विकास के माहौल में, यह माइग्रेशन तात्कालिक महसूस हो सकता है। हालांकि, कई समवर्ती कनेक्शन वाले बड़े डेटासेट पर, लॉक प्राप्त करने और इंडेक्स बनाने में कुछ समय लग सकता है।

अगले चरणों में, आप बिना किसी डाउनटाइम के इंडेक्स बनाने के लिए Django द्वारा बनाए गए माइग्रेशन को संशोधित करने जा रहे हैं।



नकली माइग्रेशन

पहला तरीका इंडेक्स को मैन्युअल रूप से बनाना है। आप माइग्रेशन जेनरेट करने जा रहे हैं, लेकिन आप वास्तव में Django को इसे लागू नहीं करने देंगे। इसके बजाय, आप डेटाबेस में मैन्युअल रूप से SQL चलाएंगे और फिर Django को माइग्रेशन पूर्ण होने के बारे में सोचेंगे।

सबसे पहले, माइग्रेशन जेनरेट करें:

$ python manage.py makemigrations --name add_index_fake
Migrations for 'app':
  app/migrations/0002_add_index_fake.py
    - Alter field sold_at on sale

sqlmigrate का उपयोग करें SQL देखने के लिए कमांड Django इस माइग्रेशन को निष्पादित करने के लिए उपयोग करेगा:

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

आप तालिका को लॉक किए बिना अनुक्रमणिका बनाना चाहते हैं, इसलिए आपको आदेश को संशोधित करने की आवश्यकता है। CONCURRENTLYजोड़ें डेटाबेस में कीवर्ड और एक्जीक्यूट करें:

app=# CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

CREATE INDEX

ध्यान दें कि आपने BEGIN . के बिना कमांड निष्पादित किया है और COMMIT भागों। इन खोजशब्दों को छोड़ने से डेटाबेस लेनदेन के बिना कमांड निष्पादित होंगे। हम लेख में बाद में डेटाबेस लेनदेन पर चर्चा करेंगे।

कमांड निष्पादित करने के बाद, यदि आप माइग्रेशन लागू करने का प्रयास करते हैं, तो आपको निम्न त्रुटि मिलेगी:

$ python manage.py migrate

Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake...Traceback (most recent call last):
  File "venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)

psycopg2.ProgrammingError: relation "app_sale_sold_at_b9438ae4" already exists

Django शिकायत करता है कि सूचकांक पहले से मौजूद है, इसलिए यह माइग्रेशन के साथ आगे नहीं बढ़ सकता है। आपने सीधे डेटाबेस में इंडेक्स बनाया है, इसलिए अब आपको Django को यह सोचने की ज़रूरत है कि माइग्रेशन पहले ही लागू हो चुका है।

माइग्रेशन को नकली कैसे बनाएं

Django वास्तव में उन्हें निष्पादित किए बिना, निष्पादित के रूप में माइग्रेशन को चिह्नित करने का एक अंतर्निहित तरीका प्रदान करता है। इस विकल्प का उपयोग करने के लिए, --fake सेट करें माइग्रेशन लागू करते समय फ़्लैग करें:

$ python manage.py migrate --fake
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake... FAKED

Django ने इस बार कोई त्रुटि नहीं उठाई। वास्तव में, Django ने वास्तव में कोई माइग्रेशन लागू नहीं किया। इसने इसे अभी निष्पादित के रूप में चिह्नित किया है (या FAKED )।

यहाँ कुछ मुद्दों पर ध्यान दिया जाना चाहिए जब नकली प्रवासन:

  • मैन्युअल कमांड Django द्वारा जेनरेट किए गए SQL के बराबर होना चाहिए: आपको यह सुनिश्चित करने की आवश्यकता है कि आपके द्वारा निष्पादित कमांड Django द्वारा उत्पन्न SQL के बराबर है। sqlmigrate का उपयोग करें SQL कमांड बनाने के लिए। यदि आदेश मेल नहीं खाते हैं, तो आप डेटाबेस और मॉडल स्थिति के बीच विसंगतियों के साथ समाप्त हो सकते हैं।

  • अन्य लागू न किए गए माइग्रेशन भी नकली होंगे: जब आपके पास कई लागू न किए गए माइग्रेशन होंगे, तो वे सभी नकली होंगे। इससे पहले कि आप माइग्रेशन लागू करें, यह सुनिश्चित करना महत्वपूर्ण है कि केवल वे माइग्रेशन जिन्हें आप नकली बनाना चाहते हैं, लागू नहीं हैं। अन्यथा, आप विसंगतियों के साथ समाप्त हो सकते हैं। एक अन्य विकल्प सटीक माइग्रेशन निर्दिष्ट करना है जिसे आप नकली बनाना चाहते हैं।

  • डेटाबेस तक सीधी पहुंच आवश्यक है: आपको डेटाबेस में SQL कमांड चलाने की आवश्यकता है। यह हमेशा एक विकल्प नहीं होता है। साथ ही, उत्पादन डेटाबेस में सीधे कमांड निष्पादित करना खतरनाक है और जब संभव हो तो इससे बचा जाना चाहिए।

  • स्वचालित परिनियोजन प्रक्रियाओं में समायोजन की आवश्यकता हो सकती है: यदि आपने परिनियोजन प्रक्रिया (CI, CD, या अन्य स्वचालन टूल का उपयोग करके) को स्वचालित किया है, तो आपको प्रक्रिया को नकली माइग्रेशन में बदलने की आवश्यकता हो सकती है। यह हमेशा वांछनीय नहीं होता है।

सफाई

अगले भाग पर जाने से पहले, आपको प्रारंभिक माइग्रेशन के ठीक बाद डेटाबेस को उसकी स्थिति में वापस लाने की आवश्यकता है। ऐसा करने के लिए, प्रारंभिक माइग्रेशन पर वापस माइग्रेट करें:

$ python manage.py migrate 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_fake... OK

Django ने दूसरे माइग्रेशन में किए गए परिवर्तनों को लागू नहीं किया, इसलिए अब फ़ाइल को हटाना भी सुरक्षित है:

$ rm app/migrations/0002_add_index_fake.py

यह सुनिश्चित करने के लिए कि आपने सब कुछ ठीक किया है, माइग्रेशन का निरीक्षण करें:

$ python manage.py showmigrations app
app
 [X] 0001_initial

प्रारंभिक माइग्रेशन लागू किया गया था, और कोई लागू नहीं किया गया माइग्रेशन नहीं है।



माइग्रेशन में रॉ SQL निष्पादित करें

पिछले खंड में, आपने सीधे डेटाबेस में SQL निष्पादित किया और माइग्रेशन को नकली बनाया। इससे काम हो जाता है, लेकिन एक बेहतर उपाय है।

Django RunSQL . का उपयोग करके माइग्रेशन में कच्चे SQL को निष्पादित करने का एक तरीका प्रदान करता है . आइए डेटाबेस में सीधे कमांड निष्पादित करने के बजाय इसका उपयोग करने का प्रयास करें।

सबसे पहले, एक नया खाली माइग्रेशन जेनरेट करें:

$ python manage.py makemigrations app --empty --name add_index_runsql
Migrations for 'app':
  app/migrations/0002_add_index_runsql.py

इसके बाद, माइग्रेशन फ़ाइल को संपादित करें और एक RunSQLजोड़ें ऑपरेशन:

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',
        ),
    ]

जब आप माइग्रेशन चलाते हैं, तो आपको निम्न आउटपुट मिलेगा:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_runsql... OK

यह अच्छा लग रहा है, लेकिन एक समस्या है। आइए फिर से माइग्रेशन जेनरेट करने का प्रयास करें:

$ python manage.py makemigrations --name leftover_migration
Migrations for 'app':
  app/migrations/0003_leftover_migration.py
    - Alter field sold_at on sale

Django ने फिर से वही माइग्रेशन जेनरेट किया। उसने ऐसा क्यों किया?

सफाई

इससे पहले कि हम उस प्रश्न का उत्तर दे सकें, आपको डेटाबेस में किए गए परिवर्तनों को साफ और पूर्ववत करना होगा। पिछले माइग्रेशन को हटाकर प्रारंभ करें। इसे लागू नहीं किया गया था, इसलिए इसे हटाना सुरक्षित है:

$ rm app/migrations/0003_leftover_migration.py

इसके बाद, app . के लिए माइग्रेशन सूचीबद्ध करें ऐप:

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [X] 0002_add_index_runsql

तीसरा माइग्रेशन चला गया है, लेकिन दूसरा लागू किया गया है। आप प्रारंभिक प्रवास के ठीक बाद राज्य में वापस जाना चाहते हैं। जैसा कि आपने पिछले अनुभाग में किया था, वापस प्रारंभिक माइग्रेशन में माइग्रेट करने का प्रयास करें:

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_runsql...Traceback (most recent call last):

NotImplementedError: You cannot reverse this operation

Django माइग्रेशन को उलटने में असमर्थ है।



रिवर्स माइग्रेशन ऑपरेशन

माइग्रेशन को उलटने के लिए, Django हर ऑपरेशन के लिए एक विपरीत क्रिया निष्पादित करता है। इस मामले में, एक इंडेक्स जोड़ने का उल्टा इसे छोड़ना है। जैसा कि आप पहले ही देख चुके हैं, जब कोई माइग्रेशन प्रतिवर्ती होता है, तो आप उसे लागू नहीं कर सकते। जैसे आप checkout . का उपयोग कर सकते हैं Git में, यदि आप migrate निष्पादित करते हैं, तो आप माइग्रेशन को उलट सकते हैं पहले के प्रवास के लिए।

कई बिल्ट-इन माइग्रेशन ऑपरेशन पहले से ही रिवर्स एक्शन को परिभाषित करते हैं। उदाहरण के लिए, किसी फ़ील्ड को जोड़ने के लिए रिवर्स एक्शन संबंधित कॉलम को छोड़ना है। एक मॉडल बनाने के लिए विपरीत क्रिया संबंधित तालिका को गिराना है।

कुछ माइग्रेशन ऑपरेशन प्रतिवर्ती नहीं हैं। उदाहरण के लिए, किसी फ़ील्ड को हटाने या मॉडल को हटाने के लिए कोई विपरीत कार्रवाई नहीं है, क्योंकि एक बार माइग्रेशन लागू करने के बाद, डेटा चला जाता है।

पिछले अनुभाग में, आपने RunSQL . का उपयोग किया था कार्यवाही। जब आपने माइग्रेशन को उलटने का प्रयास किया, तो आपको एक त्रुटि का सामना करना पड़ा। त्रुटि के अनुसार, माइग्रेशन में किसी एक ऑपरेशन को उलटा नहीं किया जा सकता है। Django डिफ़ॉल्ट रूप से कच्चे SQL को उलटने में असमर्थ है। क्योंकि Django को इस बात का कोई ज्ञान नहीं है कि ऑपरेशन द्वारा क्या निष्पादित किया गया था, यह स्वचालित रूप से एक विपरीत क्रिया उत्पन्न नहीं कर सकता है।

माइग्रेशन को प्रतिवर्ती कैसे बनाएं

एक प्रवासन प्रतिवर्ती होने के लिए, इसमें सभी संचालन प्रतिवर्ती होने चाहिए। माइग्रेशन के हिस्से को रिवर्स करना संभव नहीं है, इसलिए एक भी नॉन-रिवर्सिबल ऑपरेशन पूरे माइग्रेशन को नॉन-रिवर्सिबल बना देगा।

RunSQL बनाने के लिए ऑपरेशन रिवर्सिबल, ऑपरेशन रिवर्स होने पर आपको निष्पादित करने के लिए एसक्यूएल प्रदान करना होगा। रिवर्स SQL ​​reverse_sql . में दिया गया है तर्क।

किसी इंडेक्स को जोड़ने की विपरीत क्रिया उसे गिराना है। अपने माइग्रेशन को प्रतिवर्ती बनाने के लिए, reverse_sql प्रदान करें सूचकांक छोड़ने के लिए:

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',

            reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
        ),
    ]

अब माइग्रेशन को उलटने का प्रयास करें:

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [X] 0002_add_index_runsql

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
 Unapplying app.0002_add_index_runsql... OK

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [ ] 0002_add_index_runsql

दूसरा माइग्रेशन उलट गया था, और इंडेक्स को Django द्वारा हटा दिया गया था। अब माइग्रेशन फ़ाइल को हटाना सुरक्षित है:

$ rm app/migrations/0002_add_index_runsql.py

reverse_sql provide प्रदान करना हमेशा एक अच्छा विचार है . ऐसी स्थितियों में जहां कच्चे SQL ऑपरेशन को उलटने के लिए किसी कार्रवाई की आवश्यकता नहीं होती है, आप विशेष प्रहरी migrations.RunSQL.noop का उपयोग करके ऑपरेशन को प्रतिवर्ती के रूप में चिह्नित कर सकते हैं। :

migrations.RunSQL(
    sql='...',  # Your forward SQL here
    reverse_sql=migrations.RunSQL.noop,
),


मॉडल स्थिति और डेटाबेस स्थिति को समझें

RunSQL . का उपयोग करके मैन्युअल रूप से अनुक्रमणिका बनाने के आपके पिछले प्रयास में , Django ने एक ही माइग्रेशन को बार-बार जेनरेट किया, भले ही डेटाबेस में इंडेक्स बनाया गया था। यह समझने के लिए कि Django ने ऐसा क्यों किया, आपको सबसे पहले यह समझने की आवश्यकता है कि Django कैसे तय करता है कि कब नया माइग्रेशन जनरेट करना है।


जब Django एक नया माइग्रेशन जेनरेट करता है

माइग्रेशन उत्पन्न करने और लागू करने की प्रक्रिया में, Django डेटाबेस की स्थिति और मॉडल की स्थिति के बीच समन्वयित करता है। उदाहरण के लिए, जब आप किसी मॉडल में कोई फ़ील्ड जोड़ते हैं, तो Django तालिका में एक कॉलम जोड़ता है। जब आप मॉडल से कोई फ़ील्ड हटाते हैं, तो Django तालिका से कॉलम हटा देता है।

मॉडल और डेटाबेस के बीच सिंक करने के लिए, Django एक ऐसी स्थिति बनाए रखता है जो मॉडल का प्रतिनिधित्व करता है। मॉडल के साथ डेटाबेस को सिंक करने के लिए, Django माइग्रेशन ऑपरेशन उत्पन्न करता है। माइग्रेशन संचालन एक विक्रेता विशिष्ट SQL में अनुवाद करता है जिसे डेटाबेस में निष्पादित किया जा सकता है। जब सभी माइग्रेशन ऑपरेशन निष्पादित किए जाते हैं, तो डेटाबेस और मॉडलों के सुसंगत होने की उम्मीद की जाती है।

डेटाबेस की स्थिति प्राप्त करने के लिए, Django सभी पिछले माइग्रेशन से संचालन को एकत्रित करता है। जब माइग्रेशन की समग्र स्थिति मॉडल की स्थिति के अनुरूप नहीं होती है, तो Django एक नया माइग्रेशन उत्पन्न करता है।

पिछले उदाहरण में, आपने कच्चे SQL का उपयोग करके अनुक्रमणिका बनाई थी। Django को नहीं पता था कि आपने इंडेक्स बनाया है क्योंकि आपने एक परिचित माइग्रेशन ऑपरेशन का उपयोग नहीं किया है।

जब Django ने सभी माइग्रेशन को एकत्र किया और उनकी तुलना मॉडल की स्थिति से की, तो पाया कि एक इंडेक्स गायब था। यही कारण है कि, आपके द्वारा मैन्युअल रूप से इंडेक्स बनाने के बाद भी, Django को अभी भी लगा कि यह गायब है और इसके लिए एक नया माइग्रेशन जेनरेट किया।



डेटाबेस और राज्य को माइग्रेशन में कैसे अलग करें

चूंकि Django इंडेक्स को जिस तरह से आप चाहते हैं उसे बनाने में असमर्थ है, आप अपना स्वयं का एसक्यूएल प्रदान करना चाहते हैं लेकिन फिर भी Django को यह बताएं कि आपने इसे बनाया है।

दूसरे शब्दों में, आपको डेटाबेस में कुछ निष्पादित करने और इसकी आंतरिक स्थिति को सिंक करने के लिए Django को माइग्रेशन ऑपरेशन प्रदान करने की आवश्यकता है। ऐसा करने के लिए, Django हमें एक विशेष माइग्रेशन ऑपरेशन प्रदान करता है जिसे SeparateDatabaseAndState . कहा जाता है . यह ऑपरेशन अच्छी तरह से ज्ञात नहीं है और इस तरह के विशेष मामलों के लिए आरक्षित होना चाहिए।

माइग्रेशन को नए सिरे से लिखने की तुलना में संपादित करना बहुत आसान है, इसलिए माइग्रेशन को सामान्य तरीके से जनरेट करके प्रारंभ करें:

$ python manage.py makemigrations --name add_index_separate_database_and_state

Migrations for 'app':
  app/migrations/0002_add_index_separate_database_and_state.py
    - Alter field sold_at on sale

यह पहले की तरह ही Django द्वारा उत्पन्न माइग्रेशन की सामग्री है:

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='sale',
            name='sold_at',
            field=models.DateTimeField(
                auto_now_add=True,
                db_index=True,
            ),
        ),
    ]

Django ने एक AlterField उत्पन्न किया फील्ड पर ऑपरेशन sold_at . ऑपरेशन एक इंडेक्स बनाएगा और राज्य को अपडेट करेगा। हम इस ऑपरेशन को रखना चाहते हैं लेकिन डेटाबेस में निष्पादित करने के लिए एक अलग कमांड प्रदान करते हैं।

एक बार फिर, कमांड प्राप्त करने के लिए, Django द्वारा उत्पन्न SQL का उपयोग करें:

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

CONCURRENTLYजोड़ें उपयुक्त स्थान पर कीवर्ड:

CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

इसके बाद, माइग्रेशन फ़ाइल को संपादित करें और SeparateDatabaseAndState . का उपयोग करें निष्पादन के लिए अपना संशोधित SQL कमांड प्रदान करने के लिए:

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
                    ON "app_sale" ("sold_at");
                """, reverse_sql="""
                    DROP INDEX "app_sale_sold_at_b9438ae4";
                """),
            ],
        ),

    ],

माइग्रेशन ऑपरेशन SeparateDatabaseAndState संचालन की 2 सूचियां स्वीकार करता है:

  1. राज्य_संचालन आंतरिक मॉडल स्थिति पर लागू करने के लिए संचालन हैं। वे डेटाबेस को प्रभावित नहीं करते हैं।
  2. डेटाबेस_ऑपरेशंस डेटाबेस पर लागू करने के लिए संचालन हैं।

आपने Django द्वारा उत्पन्न मूल ऑपरेशन को state_operations . में रखा है . SeparateDatabaseAndState . का उपयोग करते समय , यह वही है जो आप आमतौर पर करना चाहेंगे। ध्यान दें कि db_index=True क्षेत्र में तर्क दिया जाता है। इस माइग्रेशन ऑपरेशन से Django को पता चल जाएगा कि फ़ील्ड पर एक इंडेक्स है।

आपने Django द्वारा उत्पन्न SQL का उपयोग किया और CONCURRENTLY . जोड़ा खोजशब्द। आपने विशेष क्रिया का उपयोग किया RunSQL माइग्रेशन में अपरिष्कृत SQL निष्पादित करने के लिए।

यदि आप माइग्रेशन चलाने का प्रयास करते हैं, तो आपको निम्न आउटपुट प्राप्त होगा:

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state...Traceback (most recent call last):
  File "/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 83, in _execute
    return self.cursor.execute(sql)
psycopg2.InternalError: CREATE INDEX CONCURRENTLY cannot run inside a transaction block



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

SQL में, CREATE , DROP , ALTER , और TRUNCATE संचालन को डेटा परिभाषा भाषा . के रूप में संदर्भित किया जाता है (डीडीएल)। डेटाबेस में जो लेनदेन संबंधी डीडीएल का समर्थन करते हैं, जैसे कि पोस्टग्रेएसक्यूएल, Django डिफ़ॉल्ट रूप से डेटाबेस लेनदेन के अंदर माइग्रेशन निष्पादित करता है। हालाँकि, उपरोक्त त्रुटि के अनुसार, PostgreSQL एक लेनदेन ब्लॉक के अंदर समवर्ती रूप से एक अनुक्रमणिका नहीं बना सकता है।

माइग्रेशन के भीतर समवर्ती रूप से एक इंडेक्स बनाने में सक्षम होने के लिए, आपको Django को डेटाबेस लेनदेन में माइग्रेशन निष्पादित नहीं करने के लिए कहना होगा। ऐसा करने के लिए, आप atomic . सेट करके माइग्रेशन को गैर-परमाणु के रूप में चिह्नित करते हैं करने के लिए False :

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
                    ON "app_sale" ("sold_at");
                """,
                reverse_sql="""
                    DROP INDEX "app_sale_sold_at_b9438ae4";
                """),
            ],
        ),

    ],

माइग्रेशन को गैर-परमाणु के रूप में चिह्नित करने के बाद, आप माइग्रेशन चला सकते हैं:

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state... OK

आपने बिना किसी डाउनटाइम के माइग्रेशन को अभी-अभी निष्पादित किया है।

जब आप SeparateDatabaseAndState . का उपयोग कर रहे हों, तब विचार करने के लिए यहां कुछ समस्याएं दी गई हैं :

  • डेटाबेस संचालन राज्य संचालन के बराबर होना चाहिए: डेटाबेस और मॉडल स्थिति के बीच विसंगतियाँ बहुत परेशानी का कारण बन सकती हैं। एक अच्छा प्रारंभिक बिंदु Django द्वारा उत्पन्न संचालन को state_operations . में रखना है और sqlmigrate . के आउटपुट को संपादित करें database_operations . में उपयोग करने के लिए ।

  • गैर-परमाणु प्रवासन त्रुटि के मामले में रोलबैक नहीं कर सकता: यदि माइग्रेशन के दौरान कोई त्रुटि होती है, तो आप रोलबैक नहीं कर पाएंगे। आपको या तो माइग्रेशन को रोलबैक करना होगा या इसे मैन्युअल रूप से पूरा करना होगा। गैर-परमाणु प्रवास के अंदर निष्पादित संचालन को न्यूनतम रखना एक अच्छा विचार है। यदि आपके पास माइग्रेशन में अतिरिक्त संचालन हैं, तो उन्हें एक नए माइग्रेशन में ले जाएं।

  • माइग्रेशन विक्रेता विशिष्ट हो सकता है: Django द्वारा उत्पन्न SQL परियोजना में उपयोग किए जाने वाले डेटाबेस बैकएंड के लिए विशिष्ट है। यह अन्य डेटाबेस बैकएंड के साथ काम कर सकता है, लेकिन इसकी गारंटी नहीं है। यदि आपको एकाधिक डेटाबेस बैकएंड का समर्थन करने की आवश्यकता है, तो आपको इस दृष्टिकोण में कुछ समायोजन करने की आवश्यकता है।



निष्कर्ष

आपने इस ट्यूटोरियल को एक बड़ी टेबल और एक समस्या के साथ शुरू किया है। आप अपने ऐप को अपने उपयोगकर्ताओं के लिए तेज़ बनाना चाहते थे, और आप उन्हें बिना किसी डाउनटाइम के ऐसा करना चाहते थे।

ट्यूटोरियल के अंत तक, आप इस लक्ष्य को प्राप्त करने के लिए Django माइग्रेशन को जेनरेट और सुरक्षित रूप से संशोधित करने में कामयाब रहे। आपने रास्ते में विभिन्न समस्याओं का सामना किया और माइग्रेशन ढांचे द्वारा प्रदान किए गए अंतर्निहित टूल का उपयोग करके उन्हें दूर करने में कामयाब रहे।

इस ट्यूटोरियल में, आपने निम्नलिखित सीखा:

  • मॉडल और डेटाबेस स्थिति का उपयोग करके Django माइग्रेशन आंतरिक रूप से कैसे काम करता है, और जब नए माइग्रेशन उत्पन्न होते हैं
  • RunSQL का उपयोग करके माइग्रेशन में कस्टम SQL कैसे निष्पादित करें? कार्रवाई
  • प्रतिवर्ती माइग्रेशन क्या होते हैं, और कैसे एक RunSQLबनाया जाता है क्रिया प्रतिवर्ती
  • परमाणु प्रवासन क्या हैं, और अपनी आवश्यकताओं के अनुसार डिफ़ॉल्ट व्यवहार को कैसे बदलें
  • Django में जटिल माइग्रेशन को सुरक्षित रूप से कैसे निष्पादित करें

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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. डेटाबेस क्या हैं?

  2. Amazon Aurora Cluster कैसे बनाएं

  3. प्लग करने योग्य डेटाबेस का नाम बदलना

  4. एक किराना वितरण डेटा मॉडल

  5. विशेष द्वीप चुनौती के लिए पाठक समाधान