2025, Nov 01 12:01

BaseModel.construct से नेस्टेड मॉडल के स्कीमा डिफ़ॉल्ट नियंत्रित करें

जानें कैसे Pydantic 2.11.7 में JSON Schema बनाते समय नेस्टेड मॉडल के डायनेमिक default_factory मान स्कीमा में स्थिर न हों। BaseModel.construct से स्थिर डिफ़ॉल्ट दिखाएँ.

Pydantic 2.11.7 में नेस्टेड मॉडल्स से JSON Schema बनाते समय कभी‑कभी डायनेमिक डिफ़ॉल्ट्स अनपेक्षित रूप से स्कीमा में “जुड़” जाते हैं। दिक्कत तब होती है जब किसी नेस्टेड मॉडल में समय-आधारित फ़ील्ड default_factory के साथ हो, लेकिन उसी मॉडल के किसी दूसरे फ़ील्ड के लिए आप स्थिर/पूर्वानुमेय डिफ़ॉल्ट दिखाना चाहते हों।

न्यूनतम उदाहरण

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

import json
import time
from pydantic import BaseModel, Field
class RandomSeedConfig(BaseModel):
    rand: int = Field(default_factory=lambda _: int(time.time() * 1000))
    use_safe_default: bool = False
class AppConfig(BaseModel):
    options: RandomSeedConfig = RandomSeedConfig()
schema_map = AppConfig.model_json_schema()
print(json.dumps(schema_map, indent=2))

इसे चलाने पर ऐसा स्कीमा बनता है जिसमें नेस्टेड फ़ील्ड का default एक पूरा बना हुआ ऑब्जेक्ट होता है—और उसमें समय-आधारित seed का एक ठोस integer मान भी शामिल हो जाता है। यानी नेस्टेड मॉडल का डिफ़ॉल्ट एक literal instance के रूप में कैप्चर होता है, इसलिए स्कीमा में स्थिर बूलियन के साथ seed की एक ठोस संख्या भी दर्ज हो जाती है।

स्कीमा seed को क्यों ‘स्थिर’ कर देता है

नेस्टेड फ़ील्ड का default सीधे नेस्टेड मॉडल का इंस्टैंस बनाकर तैयार किया जाता है। इसका मतलब, उस डिफ़ॉल्ट इंस्टैंस के लिए समय-आधारित फ़ील्ड का default_factory परिभाषा के समय ही चल जाता है। जब स्कीमा जेनरेट होता है, तो Pydantic वही default दिखा सकता है जो उसे दिख रहा है—यानी पूरा बना हुआ ऑब्जेक्ट—और इसी वजह से JSON Schema में seed एक स्थिर integer की तरह दर्ज हो जाता है। अगर आप चाहते हैं कि स्कीमा केवल स्थिर डिफ़ॉल्ट दिखाए और डायनेमिक मान अनिर्दिष्ट रहें, तो यह व्यवहार वांछनीय नहीं है।

समाधान

डायनेमिक फैक्ट्रियों को चलाए बिना स्कीमा में कौन‑सा default दिखे, इस पर नियंत्रण रखने के लिए नेस्टेड डिफ़ॉल्ट को BaseModel.construct के जरिए इनिशियलाइज़ करें और केवल वही फ़ील्ड पास करें जिन्हें आप स्कीमा में serialize कराना चाहते हैं। इससे स्थिर डिफ़ॉल्ट बना रहता है, जबकि स्कीमा के default हिस्से में समय-आधारित seed शामिल नहीं होता।

from pydantic import BaseModel, Field
import time
import json
class RandomSeedConfig(BaseModel):
    rand: int = Field(default_factory=lambda: int(time.time() * 1000))
    use_safe_default: bool = False
class AppConfig(BaseModel):
    options: RandomSeedConfig = RandomSeedConfig.construct(use_safe_default=False)
schema_map = AppConfig.model_json_schema()
print(json.dumps(schema_map, indent=2))

इस तरीके से नेस्टेड फ़ील्ड का स्कीमा-डिफ़ॉल्ट केवल स्थिर बूलियन तक सीमित रहता है। seed स्कीमा में साकार नहीं होता—और रनटाइम पर बनने वाले मान के लिए यही उपयुक्त है।

संकल्पना के स्तर पर यह समझना भी उपयोगी है: BaseModel.construct के जरिए मान सेट करने से आपको जेनरेट किए गए JSON Schema में डिफ़ॉल्ट्स के रूप में क्या शामिल होगा, इस पर स्पष्ट नियंत्रण मिलता है। इस तरीके का व्यावहारिक दुष्प्रभाव यह है कि construct रनटाइम वैलिडेशन नहीं करता—जिससे डायनेमिक फ़ैक्ट्री नहीं चलती, पर साथ ही उस विशेष जगह पर आप वैलिडेशन के बदले स्कीमा‑नियंत्रण चुन रहे होते हैं।

यह क्यों मायने रखता है

JSON Schema अकसर डॉक्यूमेंटेशन, कोड जेनरेशन और बाहरी वैलिडेशन का आधार बनता है। टाइमस्टैम्प-आधारित seed जैसे गैर‑नियत मान को स्कीमा में “पका” देना भ्रामक हो सकता है, नीचे की परत के डिफ्फ़ को शोरगुल वाला बना देता है, और यह संकेत देता है कि कोई खास संख्या ही मानक डिफ़ॉल्ट है। डायनेमिक मानों को डिफ़ॉल्ट से बाहर रखते हुए केवल स्थिर डिफ़ॉल्ट बनाए रखने से अनुबंध स्पष्ट रहता है और स्कीमा नियत (deterministic) बनता है।

निष्कर्ष

अगर किसी नेस्टेड मॉडल में डायनेमिक और स्थिर डिफ़ॉल्ट साथ‑साथ हों, तो पैरेंट का डिफ़ॉल्ट BaseModel.construct से सेट करने पर आप साफ‑सुथरा JSON Schema निर्यात कर पाते हैं: स्थिर डिफ़ॉल्ट बने रहते हैं, जबकि केवल रनटाइम पर बनने वाले मान बाहर रहते हैं। इसे वहीं अपनाएँ जहाँ डिफ़ॉल्ट इंस्टैंस की रनटाइम वैलिडेशन से अधिक स्कीमा की स्पष्टता अहम है—और डायनेमिक फ़ील्ड को सामान्य निष्पादन के दौरान मॉडल बनने पर ही तैयार होने दें।

यह लेख StackOverflow पर पूछे गए प्रश्न (लेखक: Tristan F.-R.) और Jone के उत्तर पर आधारित है।