आज की पोस्ट में, हम आपके रूबी ऑन रेल्स एप्लिकेशन में डिफ़ॉल्ट schema.rb स्कीमा फॉर्मेट बनाम structure.sql का उपयोग करने के महत्वपूर्ण अंतरों और लाभों को कवर करेंगे। डेटा-संचालित दुनिया में, अपने डेटाबेस की समृद्ध सुविधाओं का लाभ उठाना जानना एक सफल और असफल उद्यम के बीच का अंतर हो सकता है।
दोनों फॉर्मेट्स के बीच मुख्य अंतरों को स्पष्ट करने के बाद, हम structure.sql पर स्विच करने की रूपरेखा तैयार करेंगे और प्रदर्शित करेंगे कि यह डेटा अखंडता सुनिश्चित करने के साथ-साथ डेटाबेस कार्यक्षमता में कैसे मदद कर सकता है जिसे आप अन्यथा संरक्षित नहीं कर पाएंगे।
पोस्ट में, मैं एक रेल्स ऐप के उदाहरण दूंगा जो PostgreSQL डेटाबेस के साथ structure.sql का उपयोग करता है, लेकिन अंतर्निहित अवधारणाओं को अन्य डेटाबेस पर भी लागू किया जा सकता है। कोई भी वास्तविक-विश्व वेब एप्लिकेशन वास्तव में एक विश्वसनीय डेटाबेस के बिना पूरा नहीं होता जो इसका समर्थन करे।
बिना किसी देरी के, आइए सीधे इसमें गोता लगाएं!
schema.rb और structure.sql के बीच अंतर
रूबी ऑन रेल्स प्रोजेक्ट शुरू करते समय आपको सबसे पहले डेटाबेस माइग्रेशन चलाने की आवश्यकता होती है। यदि आप एक User मॉडल उत्पन्न करते हैं, उदाहरण के लिए, रेल्स अनिवार्य रूप से आपसे माइग्रेशन चलाने के लिए कहेगा, जो तदनुसार एक schema.rb फाइल बनाएगा:
rails g model User first_name:string last_name:string
रेल्स निम्न माइग्रेशन उत्पन्न करेगा:
class CreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| t.string :first_name t.string :last_name t.timestamps end end end
एक बार माइग्रेशन निष्पादित हो जाने पर, आप पाएंगे कि रेल्स ने आपके लिए एक schema.rb फाइल उत्पन्न की:
ActiveRecord::Schema.define(version: 2019_12_14_074018) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "users", force: :cascade do |t| t.string "first_name" t.string "last_name" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false end end
यह schema.rb फाइल अपेक्षाकृत बुनियादी एप्लिकेशन और उपयोग के मामलों के लिए शानदार है।
यहां नोटिस करने के लिए दो मुख्य चीजें हैं:
- यह आपके डेटाबेस का एक रूबी प्रतिनिधित्व है;
schema.rbडेटाबेस का निरीक्षण करके और इसकी संरचना को रूबी का उपयोग करके व्यक्त करके बनाया जाता है। - यह डेटाबेस-अज्ञेय है (यानी चाहे आप SQLite, PostgreSQL, MySQL या कोई अन्य डेटाबेस उपयोग करें जिसे रेल्स सपोर्ट करता है, सिंटैक्स और संरचना काफी हद तक समान रहेगी)
हालांकि, एक समय आ सकता है जब यह रणनीति आपके बढ़ते ऐप के लिए बहुत सीमित हो जाए।
मान लें, उदाहरण के लिए, आपके पास सैकड़ों या हजारों माइग्रेशन फाइलें हैं।
यदि आपको तेजी से एक नया प्रोडक्शन सिस्टम स्पिन अप करने की आवश्यकता है, तो आप एक ऐसे परिदृश्य का सामना कर सकते हैं जहां उन सभी को क्रम में चलाने में बहुत समय लगता है। या आप एक ऐसी स्थिति का सामना कर सकते हैं जहां कुछ माइग्रेशन में कोड होता है जो आपके डेटाबेस के पुराने वर्जन पर निष्पादित होने के लिए होता था, लेकिन जो वर्तमान वर्जन पर अब निष्पादन योग्य नहीं है। आपके पास एक ऐसी स्थिति हो सकती है जहां माइग्रेशन कुछ डेटा धारणाओं के साथ लिखे गए थे जो अब मान्य नहीं हैं, जिससे माइग्रेशन विफल हो जाएंगे।
ये सभी परिदृश्य एक साधारण rails db:create db:migrate कमांड के साथ आपके एप्लिकेशन के एक नए इंस्टेंस को कुशलतापूर्वक सेट अप करने से रोकते हैं — चाहे प्रोडक्शन में हो या नए टीम मेंबर के लिए। यदि ऐसा होता, तो आप सही डेटाबेस स्कीमा के साथ गति प्राप्त करने के बारे में कैसे जाएंगे?
निश्चित रूप से, एक तरीका यह होगा कि सभी टूटी हुई माइग्रेशन को वापस जाकर ठीक किया जाए। यह कभी बुरा विचार नहीं है!
यदि वापस जाकर कई माइग्रेशन को ठीक करना बहुत महंगा है, तो दूसरा तरीका rails db:setup टास्क चलाना होगा। यह टास्क आपके schema.rb फाइल से एक डेटाबेस स्कीमा उत्पन्न करेगा। हालांकि, क्या होगा यदि आपके डेटाबेस में जटिल तर्क था जो आपके डेटाबेस के schema.rb प्रतिनिधित्व में नहीं दर्शाया गया है?
सौभाग्य से, रेल्स एक विकल्प प्रदान करता है: structure.sql
structure.sql निम्न तरीकों से schema.rb से भिन्न है:
- यह डेटाबेस संरचना की एक सटीक प्रति की अनुमति देता है। यह टीम के साथ काम करते समय महत्वपूर्ण है, साथ ही यदि आपको
rails db:setupटास्क से प्रोडक्शन में तेजी से एक नया डेटाबेस उत्पन्न करने की आवश्यकता है। - यह उन्नत डेटाबेस सुविधाओं की जानकारी को संरक्षित करने की अनुमति देता है। उदाहरण के लिए, यदि आप PostgreSQL उपयोग कर रहे हैं, तो यह व्यूज़, मटेरियलाइज़्ड व्यूज़, फंक्शन्स, कंस्ट्रेंट्स आदि के उपयोग को सक्षम बनाता है।
एक बार जब एप्लिकेशन एक निश्चित परिपक्वता स्तर पर पहुंच जाता है, तो हमें दक्षता बढ़ाने, डेटा शुद्धता बनाए रखने, और धधकती-तेज प्रदर्शन सुनिश्चित करने के लिए किताब में हर तरकीब का उपयोग करना होगा। रेल्स डेटाबेस के व्यवहार को प्रबंधित करने के लिए structure.sql का उपयोग करना उपयोगकर्ताओं को ऐसा करने की अनुमति देता है।
schema.rb से structure.sql पर स्विच करना
schema.rb से structure.sql पर बदलाव करना अपेक्षाकृत सीधी प्रक्रिया है। आपको केवल config/application.rb में निम्न लाइन सेट करने की आवश्यकता है:
module YourApp class Application < Rails::Application config.load_defaults 6.0 # यह लाइन जोड़ें: config.active_record.schema_format = :sql end end
फिर, rails db:migrate चलाएं और आपको db/structure.sql में फाइल दिखाई देगी। वोइला! रेल्स आपके द्वारा उपयोग किए जा रहे डेटाबेस के लिए विशिष्ट टूल का उपयोग करके डेटाबेस संरचना को डंप करेगा (PostgreSQL के मामले में, वह टूल pg_dump है, MySQL या MariaDB के लिए, इसमें प्रत्येक टेबल के लिए SHOW CREATE TABLE का आउटपुट होगा, आदि)। यह सलाह दी जाती है कि यह फाइल वर्जन कंट्रोल में हो ताकि आपकी बाकी टीम के पास वही डेटाबेस संरचना हो।
उस फाइल पर पहली नज़र डालना कठिन हो सकता है: schema.rb फाइल केवल 25 लाइन्स थी, जबकि structure.sql फाइल पूरे 109 लाइन्स की है! इतनी बड़ी फाइल ऐप डेवलपमेंट में क्या लाभ जोड़ सकती है?
डेटाबेस-लेवल कंस्ट्रेंट्स जोड़ना
ActiveRecord रेल्स का उपयोग करने के मेरे पसंदीदा हिस्सों में से एक है। यह आपको डेटाबेस को इस तरह क्वेरी करने की अनुमति देता है जो प्राकृतिक लगता है, लगभग बोली जाने वाली भाषा की तरह। उदाहरण के लिए, यदि आप डैन नाम के किसी कंपनी के सभी यूजर्स को ढूंढना चाहते हैं, तो ActiveRecord आपको बस निम्न जैसी क्वेरी चलाने की अनुमति देता है:
company = Company.find(name: 'Some Company') # बिल्कुल प्राकृतिक भाषा की तरह पढ़ता है! company.users.where(first_name: 'Dan')
हालांकि, कुछ मामले हैं जिनमें ActiveRecord कम पड़ जाता है। उदाहरण के लिए, मान लें कि आपके User मॉडल पर निम्न वैलिडेशन है:
class User < ApplicationRecord validate :name_cannot_start_with_d private def name_cannot_start_with_d if first_name.present? && first_name[0].downcase == 'd' errors.add(:first_name, "cannot start with the letter 'D'") end end end
यदि आप 'Dan' नाम से एक यूजर बनाने का प्रयास करते हैं, तो जब वैलिडेशन चलता है तो आपको एक त्रुटि दिखाई देनी चाहिए:
User.create!(first_name: 'Dan') Traceback (most recent call last): ActiveRecord::RecordInvalid (Validation failed: First name cannot start with the letter 'D')
यह ठीक है, लेकिन मान लें कि आप या आपकी टीम के सदस्यों में से एक ने ActiveRecord की वैलिडेशन को बायपास करके डेटा बदल दिया:
u = User.create(first_name: 'Pan') # update_attribute मेथड ActiveRecord वैलिडेशन को बायपास करता है u.update_attribute :first_name, 'Dan' u.first_name => "Dan"
जैसा कि प्रदर्शित किया गया है, वैलिडेशन को बायपास करना बहुत आसान है।
इसका हमारे एप्लिकेशन के लिए विनाशकारी परिणाम हो सकते हैं। ActiveRecord एक आशीर्वाद के साथ-साथ एक अभिशाप भी हो सकता है — जबकि इसमें एक बहुत साफ और प्राकृतिक DSL है जो इसके साथ काम करना आनंददायक बनाता है, यह मॉडल-लेवल वैलिडेशन को लागू करने में अक्सर अत्यधिक अनुमतिवादी होता है। समाधान, जैसा कि आप पहले से ही जानते होंगे, डेटाबेस-लेवल कंस्ट्रेंट्स जोड़ना है।
rails g migration AddFirstNameConstraintToUser
यह एक फाइल उत्पन्न करेगा जिसे आप 'D' अक्षर से शुरू होने वाले पहले नामों को अस्वीकार करने के तर्क के साथ संपादित कर सकते हैं:
class AddFirstNameConstraintToUser < ActiveRecord::Migration[6.0] def up execute "ALTER TABLE users ADD CONSTRAINT name_cannot_start_with_d CHECK (first_name !~* '^d')" end def down execute "ALTER TABLE users DROP CONSTRAINT IF EXISTS name_cannot_start_with_d" end end
ध्यान दें कि माइग्रेशन को सफलतापूर्वक वापस करने वाला कोड जोड़ना बहुत महत्वपूर्ण है। उपरोक्त उदाहरण में, मेरे पास up और down डायरेक्टिव्स हैं। up मेथड माइग्रेशन चलने पर निष्पादित होता है, down माइग्रेशन रोल बैक होने पर निष्पादित होता है। अपने डेटाबेस संरचना को ठीक से वापस किए बिना, आपको बाद में कुछ मैन्युअल हाउसकीपिंग करनी पड़ सकती है। मैं हमेशा एक माइग्रेशन फाइल रखने की सलाह दूंगा जिसे up और down दोनों में निष्पादित किया जा सके ताकि भविष्य के सिरदर्द से बचा जा सके।
अब, माइग्रेशन चलाएं और जांचें कि क्या आप उस कंस्ट्रेंट को बायपास कर सकते हैं:
rails db:migrate
user = User.create first_name: 'Pan' user.update_attribute :first_name, 'Dan' ActiveRecord::StatementInvalid (PG::CheckViolation: ERROR: new row for relation "users" violates check constraint "name_cannot_start_with_d") DETAIL: Failing row contains (2, Dan, null, 2019-12-14 09:40:11.809358, 2019-12-14 09:40:41.658974).
बिल्कुल सही! हमारा कंस्ट्रेंट इरादे के अनुसार काम कर रहा है। भले ही, किसी भी कारण से, हम ActiveRecord की वैलिडेशन को बायपास करें, हम अभी भी डेटाबेस पर भरोसा कर सकते हैं — हमारे अंतिम गोलकीपर — हमारी डेटा अखंडता को संरक्षित करने के लिए।
इसका structure.sql से क्या लेना-देना है?
यदि आप उस पर एक नज़र डालते हैं, तो आप देखेंगे कि निम्न जोड़ा गया था:
CREATE TABLE public.users ( id bigint NOT NULL, first_name character varying, last_name character varying, created_at timestamp(6) without time zone NOT NULL, updated_at timestamp(6) without time zone NOT NULL, CONSTRAINT name_cannot_start_with_d CHECK (((first_name)::text !~* '^d'::text)));
आपका कंस्ट्रेंट स्कीमा के भीतर ही है!
जबकि schema.rb भी डेटाबेस-लेवल कंस्ट्रेंट्स का समर्थन करता है, यह याद रखना महत्वपूर्ण है कि यह वह सब कुछ व्यक्त नहीं करता जो आपका डेटाबेस सपोर्ट कर सकता है जैसे ट्रिगर्स, सीक्वेंसेज, स्टोर्ड प्रोसीजर्स या चेक कंस्ट्रेंट्स। उदाहरण के लिए, यदि आप केवल schema.rb का उपयोग कर रहे थे तो उसी सटीक माइग्रेशन (AddFirstNameConstraintToUser) के साथ आपकी स्कीमा फाइल के साथ क्या होगा:
ActiveRecord::Schema.define(version: 2019_12_14_074018) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "users", force: :cascade do |t| t.string "first_name" t.string "last_name" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false end end
फाइल नहीं बदली है! कंस्ट्रेंट नहीं जोड़ा गया था।
यदि आप अपने प्रोजेक्ट पर काम करने के लिए एक नए डेवलपर को ऑनबोर्ड करते हैं, तो आप संभावित रूप से विभिन्न डेटाबेस नियमों के तहत काम कर रहे होंगे।
structure.sql को वर्जन कंट्रोल में कमिट करने से यह सुनिश्चित करने में मदद मिलेगी कि आपकी टीम एक ही पेज पर है। यदि आप structure.sql फाइल होने पर rails db:setup चलाते हैं, तो आपके डेटाबेस की संरचना में उपरोक्त कंस्ट्रेंट होगा। schema.rb के साथ ऐसी कोई गारंटी नहीं है।
यही बात प्रोडक्शन सिस्टम के बारे में भी कही जा सकती है। यदि आपको एक नए एप्लिकेशन इंस्टेंस को एक नए डेटाबेस के साथ तेजी से स्पिन अप करने की आवश्यकता थी — और सभी माइग्रेशन को क्रमिक रूप से चलाने में लंबा समय लगता है — structure.sql फाइल से डेटाबेस सेट करना बहुत तेज होगा। हम निश्चिंत हो सकते हैं कि structure.sql हमारे डेटाबेस को अन्य इंस्टेंसेस के समान सटीक संरचना के साथ बनाएगा।
बढ़ते दर्द
संक्षिप्त schema.rb फाइल को एक टीम में प्रबंधित करना वर्बोज़ structure.sql फाइल को प्रबंधित करने की तुलना में कहीं आसान कार्य है।
structure.sql पर माइग्रेट करते समय सबसे बड़ी बढ़ती पीड़ा यह सुनिश्चित करना है कि केवल आवश्यक परिवर्तन उस फाइल में कमिट हों, जो कभी-कभी करना मुश्किल हो सकता है।
मान लें, उदाहरण के लिए, आप किसी की ब्रांच खींचते हैं और उस ब्रांच के लिए विशिष्ट माइग्रेशन चलाते हैं। आपका structure.sql अब कुछ परिवर्तन समाहित करेगा। फिर आप अपनी ब्रांच पर काम करने के लिए वापस जाते हैं और एक नई माइग्रेशन उत्पन्न करते हैं। आपका structure.sql फाइल अब आपकी ब्रांच और दूसरी ब्रांच दोनों के परिवर्तनों को समाहित करेगी। इससे निपटना थोड़ा झंझट भरा हो सकता है, और जब इन संघर्षों को प्रबंधित करने की बात आती है तो निस्संदेह एक सीखने की अवस्था होती है।
इस दृष्टिकोण का उपयोग करके, हम एक ट्रेडऑफ बना रहे हैं। हमें थोड़ी सी कोड जटिलता का सामना करना पड़ता है जो हमें अपने डेटाबेस की उन्नत कार्यक्षमता को संरक्षित करने की अनुमति देती है। बदले में, हमें एक सरल स्कीमा प्रतिनिधित्व के साथ-साथ डेटाबेस की पूरी शक्ति न होने से भी निपटना पड़ता है, उदाहरण के लिए यदि हम db:setup टास्क से बैकअप सेट करना चाहते हैं। मैं मानता हूं कि वर्जन-कंट्रोल की थोड़ी सी परेशानी सहन करना बेहतर है बजाय प्रोडक्शन सिस्टम में भ्रष्ट/गलत डेटा को ठीक करने के दर्द से गुजरने के, या अपने डेटाबेस द्वारा प्रदान की जाने वाली सभी उन्नत कार्यक्षमता का उपयोग नहीं कर पाने के।
आम तौर पर, मैंने अपने structure.sql फाइल को यह सुनिश्चित करने के लिए दो रणनीतियों का उपयोग किया है कि इसमें केवल एक विशिष्ट ब्रांच से संबंधित आवश्यक परिवर्तन हों:
- एक बार जब आप एक ब्रांच पर काम करना समाप्त कर लेते हैं जिसमें माइग्रेशन होते हैं, तो सुनिश्चित करें कि आप
rails db:rollback STEP=nचलाते हैं जहांnउस ब्रांच में माइग्रेशन की संख्या है। यह सुनिश्चित करेगा कि आपकी डेटाबेस संरचना अपनी मूल स्थिति में वापस आ जाए। - आप ब्रांच पर काम करने के बाद रोलबैक करना भूल सकते हैं। उस स्थिति में, एक नई ब्रांच पर काम करते समय, सुनिश्चित करें कि कोई भी नई माइग्रेशन बनाने से पहले आप मास्टर से एक प्राचीन
structure.sqlफाइल खींचते हैं।
एक नियम के रूप में, आपकी structure.sql फाइल में मास्टर में मर्ज होने से पहले केवल आपकी ब्रांच से संबंधित परिवर्तन होने चाहिए।
निष्कर्ष
आम तौर पर, जब रेल्स एप्लिकेशन छोटे होते हैं या डेटाबेस द्वारा प्रदान की जाने वाली कुछ अधिक उन्नत सुविधाओं की आवश्यकता नहीं होती है तो schema.rb का उपयोग करना सुरक्षित है, जो बहुत पठनीय, संक्षिप्त और प्रबंधन में आसान है।
हालांकि, जैसे-जैसे एप्लिकेशन आकार और जटिलता में बढ़ता है, डेटाबेस संरचना का सटीक प्रतिबिंब अत्यंत महत्वपूर्ण होता है। यह एक टीम को सही कंस्ट्रेंट्स, डेटाबेस मॉड्यूल्स, फंक्शन्स और ऑपरेटर्स को बनाए रखने की अनुमति देगा जो अन्यथा संभव नहीं होंगे। एक अच्छी तरह से बनाए रखा structure.sql फाइल के साथ रेल्स का उपयोग करना सीखना एक बढ़त प्रदान करेगा जो सरल schema.rb बस प्रदान नहीं कर सकता।
P.S. यदि आप रूबी मैजिक पोस्ट को प्रेस से बाहर आते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूज़लेटर की सदस्यता लें और कभी भी एक भी पोस्ट मिस न करें!