2012-08-31 15 views
59

Chỉ cần một câu hỏi nhanh: SQLAlchemy talks about gọi sessionmaker() một lần nhưng gọi kết quả là Session() mỗi lần bạn cần nói chuyện với DB của mình. Đối với tôi đó là việc thứ hai tôi sẽ làm tôi đầu tiên session.add(x) hoặc một cái gì đó tương tự, lần đầu tiên tôi sẽ làmSQLAlchemy: Tạo so với sử dụng lại phiên

from project import Session 
session = Session() 

Những gì tôi đã làm cho đến nay là để thực hiện cuộc gọi session = Session() trong mô hình của tôi lần và sau đó luôn nhập cùng phiên bất cứ nơi nào trong ứng dụng của tôi. Vì đây là một ứng dụng web, điều này sẽ thường là có nghĩa là giống nhau (khi một lượt xem được thực thi).

Nhưng sự khác biệt ở đâu? Những bất lợi của việc sử dụng một phiên tất cả các thời gian chống lại bằng cách sử dụng nó cho công cụ cơ sở dữ liệu của tôi là gì cho đến khi chức năng của tôi được thực hiện và sau đó tạo ra một cái mới trong thời gian tới tôi muốn nói chuyện với DB của tôi?

Tôi hiểu rằng nếu tôi sử dụng nhiều chủ đề, mỗi chủ đề sẽ nhận được phiên của riêng họ. Nhưng sử dụng scoped_session(), tôi đã đảm bảo rằng vấn đề không tồn tại, phải không?

Hãy làm rõ nếu có bất kỳ giả định nào của tôi sai.

Trả lời

152

sessionmaker() là một nhà máy, ở đó để khuyến khích đặt các tùy chọn cấu hình để tạo các đối tượng Session mới chỉ ở một nơi. Nó là tùy chọn, trong đó bạn có thể dễ dàng gọi Session(bind=engine, expire_on_commit=False) bất cứ khi nào bạn cần một Session mới, ngoại trừ chi tiết và dư thừa của nó, và tôi muốn ngăn chặn sự gia tăng của "người trợ giúp" quy mô nhỏ mà mỗi người tiếp cận vấn đề dư thừa này một số cách mới mẻ và khó hiểu hơn.

Vì vậy, sessionmaker() chỉ là một công cụ giúp bạn tạo các đối tượng Session khi bạn cần chúng.

Phần tiếp theo. Tôi nghĩ rằng câu hỏi là, sự khác biệt giữa việc tạo ra một Session() mới tại các điểm khác nhau so với chỉ sử dụng một trong tất cả các cách thức thông qua. Câu trả lời, không phải rất nhiều. Session là một vùng chứa cho tất cả các đối tượng bạn đưa vào nó, và sau đó nó cũng theo dõi một giao dịch mở. Tại thời điểm bạn gọi rollback() hoặc commit(), giao dịch kết thúc và Session không có kết nối với cơ sở dữ liệu cho đến khi nó được gọi để phát ra lại SQL. Các liên kết mà nó nắm giữ đối với các đối tượng được ánh xạ của bạn là tham chiếu yếu, miễn là các đối tượng được xóa sạch các thay đổi đang chờ xử lý, vì vậy ngay cả trong trường hợp đó, Session sẽ tự dọn sạch trở lại trạng thái hoàn toàn mới khi ứng dụng của bạn mất tất cả các tham chiếu đến các đối tượng được ánh xạ. Nếu bạn để nó với thiết lập mặc định là "expire_on_commit", thì tất cả các đối tượng sẽ hết hạn sau một lần commit. Nếu điều đó Session treo trong khoảng năm hoặc hai mươi phút và tất cả mọi thứ đã thay đổi trong cơ sở dữ liệu trong lần sử dụng tiếp theo, nó sẽ tải tất cả trạng thái hoàn toàn mới vào lần tiếp theo bạn truy cập các đối tượng đó ngay cả khi chúng đang ngồi bộ nhớ trong hai mươi phút.

Trong các ứng dụng web, chúng tôi thường nói, tại sao bạn không tạo một thương hiệu mới Session trên mỗi yêu cầu, thay vì sử dụng cùng một lần lặp đi lặp lại. Thực hành này đảm bảo rằng yêu cầu mới bắt đầu "sạch". Nếu một số đối tượng từ yêu cầu trước đó chưa được thu thập rác và nếu có thể bạn đã tắt "expire_on_commit", có thể một số trạng thái từ yêu cầu trước đó vẫn bị treo xung quanh và trạng thái đó thậm chí có thể khá cũ. Nếu bạn cẩn thận để bật expire_on_commit bật và chắc chắn gọi commit() hoặc rollback() khi kết thúc yêu cầu, thì tốt, nhưng nếu bạn bắt đầu với một thương hiệu mới Session, thì thậm chí không có bất kỳ câu hỏi nào mà bạn bắt đầu sạch sẽ.Vì vậy, ý tưởng để bắt đầu mỗi yêu cầu với Session mới thực sự là cách đơn giản nhất để đảm bảo bạn đang bắt đầu mới và sử dụng expire_on_commit nhiều tùy chọn, vì cờ này có thể phải chịu rất nhiều SQL bổ sung cho một hoạt động gọi số commit() ở giữa một loạt các hoạt động. Không chắc chắn nếu điều này trả lời câu hỏi của bạn.

Vòng tiếp theo là những gì bạn đề cập đến về luồng. Nếu ứng dụng của bạn đa luồng, chúng tôi khuyên bạn nên đảm bảo sử dụng Session là cục bộ để ... một cái gì đó. scoped_session() theo mặc định làm cho địa phương trở thành chuỗi hiện tại. Trong một ứng dụng web, địa phương để yêu cầu là trong thực tế, thậm chí tốt hơn. Flask-SQLAlchemy thực sự gửi một "phạm vi chức năng" tùy chỉnh đến scoped_session() để bạn nhận được một phiên yêu cầu phạm vi. Ứng dụng Kim tự tháp trung bình sẽ gắn phiên vào trong sổ đăng ký "yêu cầu". Khi sử dụng các lược đồ như thế này, ý tưởng "tạo phiên mới khi bắt đầu yêu cầu" tiếp tục trông giống như cách đơn giản nhất để giữ mọi thứ thẳng thắn.

+9

Ồ, điều này trả lời tất cả câu hỏi của tôi về phần SQLAlchemy và thậm chí thêm một số thông tin về Flask a nd Kim tự tháp! Tiền thưởng thêm: nhà phát triển trả lời;) Tôi ước tôi có thể bỏ phiếu nhiều lần. Cảm ơn nhiều! – javex

+0

Một làm rõ, nếu có thể: bạn nói expire_on_commit "có thể phát sinh thêm nhiều SQL" ... bạn có thể cung cấp thêm chi tiết không? Tôi nghĩ expire_on_commit chỉ liên quan đến những gì xảy ra trong RAM, chứ không phải những gì xảy ra trong cơ sở dữ liệu. – Veky

+2

expire_on_commit có thể dẫn đến SQL nhiều hơn nếu bạn sử dụng lại cùng một phiên, và một số đối tượng vẫn còn treo trong phiên đó, khi bạn truy cập chúng, bạn sẽ nhận được một hàng SELECT cho mỗi một đối tượng. làm mới trạng thái của họ về giao dịch mới. – zzzeek

12

Ngoài câu trả lời của zzzeek tuyệt vời của, đây là một công thức đơn giản để nhanh chóng tạo throwaway, tự đóng kín phiên:

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() 

Cách sử dụng:

from mymodels import Foo 

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

Có lý do nào khiến bạn không chỉ tạo phiên mới mà còn là kết nối mới? – aforaudrey

+0

Không thực sự - đây là một ví dụ nhanh để hiển thị cơ chế, mặc dù nó có ý nghĩa để tạo ra mọi thứ mới mẻ trong thử nghiệm, nơi tôi sử dụng phương pháp này nhiều nhất. Nó sẽ dễ dàng mở rộng chức năng này với kết nối như một đối số tùy chọn. –

-1

Bạn có thể tạo phiên sử dụng db

db = SQLAlchemy(app) 
engine = db.engine 
Session = sessionmaker(engine) 
session = Session() 
+3

nó là 'Flask' ​​cụ thể, không phổ biến' SQLAlchemy' –

Các vấn đề liên quan