SQLAlchemy: एक सत्र का पुन: उपयोग करना


98

बस एक त्वरित प्रश्न: SQLAlchemy एक बार कॉल करने के बारे में बात करता हैsessionmaker() लेकिन परिणामी Session()कक्षा को हर बार कॉल करने पर आपको अपने DB से बात करने की आवश्यकता होती है। मेरे लिए इसका मतलब है कि दूसरा मैं अपना पहला session.add(x)या ऐसा ही कुछ करूंगा, मैं पहले करूंगा

from project import Session
session = Session()

अब तक मैंने जो किया वह था कि मैं एक बारsession = Session() अपने मॉडल में कॉल करूं और फिर हमेशा अपने आवेदन में कहीं भी उसी सत्र को आयात करूं। चूंकि यह एक वेब-अनुप्रयोग है, इसका मतलब आमतौर पर एक ही होगा (जैसा कि एक दृश्य निष्पादित होता है)।

लेकिन फर्क कहां है? जब तक मेरा कार्य पूरा नहीं हो जाता है तब तक अपने डेटाबेस सामान के लिए इसका उपयोग करने के खिलाफ हर समय एक सत्र का उपयोग करने का क्या नुकसान है और फिर अगली बार जब मैं अपने डीबी से बात करना चाहता हूं तो एक नया बनाना?

मुझे लगता है कि अगर मैं कई थ्रेड्स का उपयोग करता हूं, तो प्रत्येक को अपना स्वयं का सत्र मिलना चाहिए। लेकिन उपयोग करते हुए scoped_session(), मैं पहले से ही सुनिश्चित करता हूं कि समस्या मौजूद नहीं है, क्या मैं?

कृपया स्पष्ट करें कि क्या मेरी कोई धारणा गलत है।

जवाबों:


223

sessionmaker()एक कारखाना है, यह Sessionसिर्फ एक ही स्थान पर नई वस्तुओं को बनाने के लिए कॉन्फ़िगरेशन विकल्पों को प्रोत्साहित करने के लिए है। यह वैकल्पिक है, जिसमें आप आसानी से Session(bind=engine, expire_on_commit=False)कभी भी कॉल कर सकते हैं जब आपको किसी नए की आवश्यकता होती है Session, सिवाय इसके कि क्रिया और अतिरेक, और मैं छोटे पैमाने पर "सहायकों" के प्रसार को रोकना चाहता था कि प्रत्येक ने कुछ नए में इस अतिरेक के मुद्दे पर संपर्क किया और अधिक भ्रमित तरीका।

तो sessionmaker()बस एक उपकरण है जो आपको Sessionवस्तुओं को बनाने में मदद करता है जब आपको उनकी आवश्यकता होती है।

अगला भाग मुझे लगता है कि सवाल यह है कि Session()विभिन्न बिंदुओं पर एक नया बनाने में क्या अंतर है ? जवाब, बहुत ज्यादा नहीं। Sessionआपके द्वारा इसमें डाली गई सभी वस्तुओं के लिए एक कंटेनर है, और फिर यह एक खुले लेनदेन का भी ट्रैक रखता है। जिस समय आप कॉल करते हैं rollback()या commit()लेन-देन खत्म हो जाता है, और Sessionतब तक डेटाबेस से कोई संबंध नहीं होता है जब तक कि उसे फिर से एसक्यूएल का उत्सर्जन करने के लिए नहीं बुलाया जाता है। आपके मैप्ड ऑब्जेक्ट्स के लिए यह लिंक कमजोर रेफ़रेंसिंग है, बशर्ते ऑब्जेक्ट्स लंबित परिवर्तनों से साफ़ हों, तो इस संबंध में भी Sessionजब कोई एप्लिकेशन मैप की गई सभी चीज़ों को खो देता है , तो वह खुद को एक नए राज्य में वापस खाली कर देगा। यदि आप इसे अपने डिफ़ॉल्ट के साथ छोड़ देते हैं"expire_on_commit"सेटिंग, फिर सभी ऑब्जेक्ट्स एक कमिट के बाद समाप्त हो जाते हैं। अगर वह Sessionपांच या बीस मिनट के लिए घूमता है, और अगली बार जब आप इसका उपयोग करते हैं, तो डेटाबेस में सभी प्रकार की चीजें बदल गई हैं, यह अगली बार जब आप उन वस्तुओं तक पहुंचते हैं, तो वे सभी ब्रांड नई स्थिति को लोड करेंगे, भले ही वे स्मृति में बैठे हों बीस मिनट के लिए।

वेब अनुप्रयोगों में, हम आम तौर पर कहते हैं, हे, आप एक Sessionही बार-बार उपयोग करने के बजाय प्रत्येक अनुरोध पर एक नया ब्रांड क्यों नहीं बनाते हैं । यह अभ्यास सुनिश्चित करता है कि नया अनुरोध "स्वच्छ" शुरू हो। यदि पिछले अनुरोध की कुछ वस्तुओं को अभी तक एकत्र नहीं किया गया है, और हो सकता है कि आप बंद हो गए हों "expire_on_commit", हो सकता है कि पिछले अनुरोध से कुछ स्थिति अभी भी घूम रही हो, और वह स्थिति बहुत पुरानी हो सकती है। यदि आप सावधान रहना छोड़ देते हैं expire_on_commitऔर निश्चित रूप से कॉल करने के लिए commit()या rollback()अनुरोध समाप्त होने पर, तो यह ठीक है, लेकिन अगर आप एक नए ब्रांड के साथ शुरुआत करते हैं Session, तो कोई भी सवाल नहीं है जिसे आप साफ करना शुरू कर रहे हैं। तो एक नए के साथ प्रत्येक अनुरोध शुरू करने का विचार हैSessionयह सुनिश्चित करने के लिए कि आप नए सिरे से शुरू कर रहे हैं, और expire_on_commitइस विकल्प को बहुत अधिक वैकल्पिक बनाने के लिए वास्तव में सिर्फ सबसे सरल तरीका है , क्योंकि यह ध्वज एक ऑपरेशन के लिए बहुत अधिक अतिरिक्त एसक्यूएल का उपयोग कर सकता है, जो ऑपरेशन commit()की एक श्रृंखला के बीच में कॉल करता है। यकीन नहीं होता अगर यह आपके सवाल का जवाब देता है।

अगले दौर में आप थ्रेडिंग के बारे में उल्लेख करते हैं। यदि आपका एप्लिकेशन मल्टीथ्रेडेड है, तो हम अनुशंसा करते हैं कि Sessionउपयोग में स्थानीय हो ... कुछ। scoped_session()डिफ़ॉल्ट रूप से यह वर्तमान थ्रेड के लिए स्थानीय बनाता है। एक वेब ऐप में, अनुरोध के लिए स्थानीय वास्तव में और भी बेहतर है। फ्लास्क-SQLAlchemy वास्तव में एक कस्टम "स्कोप फंक्शन" भेजता है scoped_session()ताकि आपको एक अनुरोधित स्कॉप्ड सत्र मिल सके। औसत पिरामिड एप्लिकेशन सत्र को "अनुरोध" रजिस्ट्री में चिपका देता है। इस तरह की योजनाओं का उपयोग करते समय, "नया सत्र अनुरोध पर शुरू करें" विचार चीजों को सीधा रखने के लिए सबसे सरल तरीके की तरह दिखता है।


17
वाह, यह SQLAlchemy भाग पर मेरे सभी सवालों के जवाब देता है और यहां तक ​​कि फ्लास्क और पिरामिड के बारे में कुछ जानकारी जोड़ता है! जोड़ा गया बोनस: डेवलपर्स जवाब;) काश मैं एक से अधिक बार वोट कर सकता। आपका बहुत बहुत धन्यवाद!
javex

एक स्पष्टीकरण, यदि संभव हो तो: आप कहते हैं कि expire_on_commit "बहुत अधिक अतिरिक्त एसक्यूएल कर सकता है" ... क्या आप अधिक विवरण दे सकते हैं? मुझे लगा कि expire_on_commit केवल चिंतित है कि RAM में क्या होता है, न कि डेटाबेस में क्या होता है।
वेकी

3
expire_on_commit के परिणामस्वरूप अधिक SQL हो सकता है यदि आप एक ही सत्र को फिर से उपयोग करते हैं, और कुछ वस्तुएँ अभी भी उस सत्र में घूम रही हैं, जब आप उन्हें एक्सेस करते हैं, तो आप उनमें से प्रत्येक के लिए एकल-पंक्ति का चयन करेंगे, क्योंकि वे प्रत्येक व्यक्तिगत रूप से ताज़ा करते हैं। नए लेन-देन के संदर्भ में उनका राज्य।
zzzeek

1
हाय, @zzzeek। उत्कृष्ट उत्तर के लिए धन्यवाद। मैं अजगर में बहुत नया हूं और कई चीजें जो मैं स्पष्ट करना चाहता हूं: 1) क्या मैं सही समझता हूं जब मैं सत्र () विधि से नया "सत्र" बनाता हूं, तो यह SQL लेन-देन बनाएगा, तब तक लेन-देन तब तक खोला जाएगा जब तक कि मैं प्रतिबद्ध / रोलबैक सत्र नहीं लेता ? 2) क्या सत्र () किसी तरह के कनेक्शन पूल का उपयोग करता है या हर बार sql से नया कनेक्शन बनाता है?
एलेक्स गर्सकी

27

उत्कृष्ट zzzeek के जवाब के अलावा, यहाँ एक सरल नुस्खा जल्दी से भगोड़ा, स्व-संलग्न सत्र बनाने के लिए है:

from contextlib import contextmanager

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

@contextmanager
def db_session(db_url):
    """ Creates a context with an open SQLAlchemy session.
    """
    engine = create_engine(db_url, convert_unicode=True)
    connection = engine.connect()
    db_session = scoped_session(sessionmaker(autocommit=False, autoflush=True, bind=engine))
    yield db_session
    db_session.close()
    connection.close()

उपयोग:

from mymodels import Foo

with db_session("sqlite://") as db:
    foos = db.query(Foo).all()

3
क्या कोई कारण है कि आप न केवल एक नया सत्र बनाते हैं, बल्कि एक ताजा कनेक्शन भी बनाते हैं?
डेन्किंग

वास्तव में नहीं - यह तंत्र को दिखाने के लिए एक त्वरित उदाहरण है, हालांकि यह परीक्षण में सब कुछ नया बनाने के लिए समझ में आता है, जहां मैं इस दृष्टिकोण का सबसे अधिक उपयोग करता हूं। वैकल्पिक तर्क के रूप में कनेक्शन के साथ इस फ़ंक्शन का विस्तार करना आसान होना चाहिए।
बेरीस्लाव लोपैक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.