2012-02-15 7 views
32

हम स्क्लेक्लेमी और पोस्टग्रेस के साथ एक बहुआयामी ऐप होस्ट करते हैं। मैं प्रत्येक किरायेदार के लिए एकाधिक डेटाबेस के साथ एक डेटाबेस में अलग डेटाबेस रखने से आगे बढ़ रहा हूं। क्या SQLAlchemy इस मूल रूप से समर्थन करता है? मैं मूल रूप से बस प्रत्येक जिज्ञासा है कि बाहर आता है एक पूर्व निर्धारित स्कीमा के साथ उपसर्ग होना चाहते हैं ... जैसेपोस्टग्रेस स्कीमा के SQLAlchemy समर्थन

select * from client1.users 

के बजाय सिर्फ

select * from users 

नोट है कि मैं एक में सभी तालिकाओं के लिए स्कीमा स्विच करना चाहते हैं विशेष अनुरोध/अनुरोधों का सेट, यहां और वहां केवल एक ही टेबल नहीं।

मुझे कल्पना है कि यह एक कस्टम क्वेरी क्लास के साथ भी पूरा किया जा सकता है लेकिन मैं कल्पना नहीं कर सकता कि इस नस में पहले से कुछ नहीं किया गया है।

उत्तर

35

अच्छी तरह से इस पर जाने के लिए कुछ तरीके है और उस पर कैसे निर्भर करता है आपके ऐप संरचित है।

meta = MetaData(schema="client1") 

तरह से अपने अनुप्रयोग चलाता पूरे आवेदन के भीतर एक समय में एक "ग्राहक" है, तो आप काम हो गया: यहाँ सबसे बुनियादी तरीका है।

लेकिन इसके साथ क्या गलत हो सकता है, उस मेटाडेटा की प्रत्येक तालिका उस स्कीमा पर है। यदि आप एक आवेदन को एक साथ कई ग्राहकों का समर्थन करना चाहते हैं (आमतौर पर "बहुमुखी" का अर्थ क्या है), यह अनावश्यक होगा क्योंकि आपको मेटाडेटा की प्रतिलिपि बनाना होगा और प्रत्येक क्लाइंट के लिए सभी मैपिंग को डुप्लिकेट करना होगा।

client1_foo = Client1Foo() 

और उस मामले में आप "इकाई के साथ काम करेंगे: इस दृष्टिकोण से किया जा सकता है, अगर तुम सच में करना चाहते हैं, जिस तरह से यह काम करता है आप की तरह एक विशेष मैप किया वर्ग के साथ प्रत्येक ग्राहक का उपयोग होता है sometable.tometadata() के साथ http://www.sqlalchemy.org/trac/wiki/UsageRecipes/EntityName पर नुस्खा "(http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Table.tometadata देखें)।

तो मान लें कि जिस तरह से यह वास्तव में काम करता है वह ऐप के भीतर कई क्लाइंट है, लेकिन प्रति थ्रेड में केवल एक ही समय में।

# start request 

# new session 
sess = Session() 

# set the search path 
sess.execute("SET search_path TO client1") 

# do stuff with session 

# close it. if you're using connection pooling, the 
# search path is still set up there, so you might want to 
# revert it first 
sess.close() 

अंतिम दृष्टिकोण @compiles एक्सटेंशन का उपयोग कर रहना संकलक ओवरराइड करने के लिए किया जाएगा: ठीक है वास्तव में, ऐसा करने के लिए है कि PostgreSQL में सबसे आसान तरीका खोज पथ सेट करने के लिए जब आप एक कनेक्शन के साथ काम शुरू किया जाएगा बयानों के भीतर "स्कीमा" नाम। यह करने योग्य है, लेकिन मुश्किल होगा क्योंकि हर जगह "टेबल" उत्पन्न होने के लिए लगातार हुक नहीं है। आपकी सबसे अच्छी शर्त शायद प्रत्येक अनुरोध पर खोज पथ सेट कर रही है।

+0

धन्यवाद! मैं कुछ चीजों की कोशिश करूंगा और फिर देखें कि कौन सा सबसे अच्छा काम करता है और वापस रिपोर्ट करता है लेकिन मुझे लगता है कि रास्ता जाने का रास्ता है। – eleddy

+0

@zzzeek मेरे पास इसका दर्पण प्रश्न है, लेकिन अलेम्बिक के लिए, वास्तव में आपके इनपुट का उपयोग कर सकता है: http://stackoverflow.com/questions/21109218/alembic-support-for-multiple-postgres-schemas – dtheodor

+4

संयोग से, मैं करने में कामयाब रहा यह घोषणात्मक वाक्यविन्यास के लिए: '' base = declarative_base(); Base.metadata.schema = 'ebay'''। हालांकि, शायद एक बेहतर तरीका है। –

2

Table definitions

में एक स्कीमा संपत्ति मुझे यकीन है कि नहीं कर रहा हूँ वहाँ है अगर यह काम करता है, लेकिन आप की कोशिश कर सकते हैं:

Table(CP.get('users', metadata, schema='client1',....) 
+0

मैं एक और वैश्विक स्तर पर कुछ ढूंढ रहा हूं ताकि मैं एक ही अनुरोध के लिए सभी तालिकाओं में सभी प्रश्नों को स्विच कर सकूं। मैं इसे प्रतिबिंबित करने के लिए प्रश्न अपडेट करूंगा। – eleddy

0

आप बस अपनी खोज_पथ बदल सकते हैं। समस्या

set search_path=client9; 

अपने सत्र की शुरुआत में और फिर अपनी तालिकाओं को अयोग्य बनाए रखें।

आप प्रति-डेटाबेस या प्रति-उपयोगकर्ता स्तर पर डिफ़ॉल्ट खोज_पथ भी सेट कर सकते हैं। मैं डिफ़ॉल्ट रूप से इसे एक खाली स्कीमा पर सेट करने के लिए लुभाना चाहता हूं ताकि आप इसे सेट करने में किसी भी विफलता को आसानी से पकड़ सकें।

+0

जो ... एक अच्छा विचार है। दो ताली! – eleddy

+0

और याद रखें कि session.commit() के बाद एक नया लेनदेन शुरू हो गया है, इसलिए search_path रीसेट हो जाएगा। एसए सत्र कार्यक्रम प्रत्येक नए लेनदेन के लिए search_path सेट करने के लिए बहुत अच्छा काम करते हैं। – Brett

5

आप SQLAlchemy घटना इंटरफ़ेस का उपयोग कर इस प्रबंधन करने में सक्षम हो सकता है। तो इससे पहले कि आप पहली बार कनेक्शन बनाने,

from sqlalchemy import event 
from sqlalchemy.pool import Pool 

def set_search_path(db_conn, conn_proxy): 
    print "Setting search path..." 
    db_conn.cursor().execute('set search_path=client9, public') 

event.listen(Pool,'connect', set_search_path) 

की तर्ज पर एक श्रोता की स्थापना जाहिर है इस से पहले पहले कनेक्शन बनाई गई है (आवेदन initiallization में जैसे)

समस्या मैं देख रहा हूँ

निष्पादित करने की आवश्यकता सत्र.execute (...) समाधान के साथ यह है कि यह सत्र द्वारा उपयोग किए गए एक विशिष्ट कनेक्शन पर निष्पादित करता है। हालांकि मैं sqlalchemy में कुछ भी नहीं देख सकता जो गारंटी देता है कि सत्र अनिश्चित काल तक उसी कनेक्शन का उपयोग जारी रखेगा। यदि यह कनेक्शन पूल से नया कनेक्शन उठाता है, तो यह खोज पथ सेटिंग खो देगा।

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

दूसरी ओर, यदि आप किसी एप्लिकेशन के भीतर एकाधिक क्लाइंट को संभालना चाहते हैं, तो यह काम नहीं करेगा - और मुझे लगता है कि session.execute (...) दृष्टिकोण सबसे अच्छा तरीका हो सकता है।

+1

क्या आपके पास 'क्लाइंट 9' को हार्डकोडिंग के बजाय तर्क के रूप में पास करने का एक शानदार तरीका है? मेरा वर्तमान (हैकी) वर्कअराउंड डीबी-यूआरएल ('? Application_name = bla') पर' application_name' क्वेरी arg को पास करना है और फिर इसे 'set_search_path' में' db_conn.dsn.split ('application_name =') के साथ जांचना है [ 1]) '। – rkrzr

0

मुझे उपरोक्त उत्तरों में से कोई भी SqlAlchmeny 1.2.4 के साथ काम नहीं मिला। यह वह समाधान है जो मेरे लिए काम करता है।

from sqlalchemy import MetaData, Table 
from sqlalchemy import create_engine  

def table_schemato_psql(schema_name, table_name): 

     conn_str = 'postgresql://{username}:{password}@localhost:5432/{database}'.format(
      username='<username>', 
      password='<password>', 
      database='<database name>' 
     ) 

     engine = create_engine(conn_str) 

     with engine.connect() as conn: 
      conn.execute('SET search_path TO {schema}'.format(schema=schema_name)) 

      meta = MetaData() 

      table_data = Table(table_name, meta, 
           autoload=True, 
           autoload_with=conn, 
           postgresql_ignore_search_path=True) 

      for column in table_data.columns: 
       print column.name