SQLAlchemy: Tworzenie vs. ponowne wykorzystanie sesji

Krótkie pytanie: SQLAlchemymówi o wywołaniu sessionmaker() raz, ale wywołaniu wynikowej Session() klasy za każdym razem, gdy musisz porozmawiać z DB. Dla mnie oznacza to, że w momencie gdy zrobię pierwszy session.add(x) lub coś podobnego, najpierw zrobię

from project import Session
session = Session()

Do tej pory wykonałem połączenie session = Session() w moim modelu raz, a następnie zawsze zaimportowałem tę samą sesję w dowolnym miejscu mojej aplikacji. Ponieważ jest to aplikacja internetowa, to zazwyczaj oznacza to samo (jako jeden widok jest wykonywany).

Ale gdzie jest różnica? Jaka jest wada używania jednej sesji Przez cały czas przeciwko używaniu jej do moich rzeczy bazodanowych, dopóki moja funkcja nie zostanie zakończona, a następnie utworzenie nowej następnym razem, gdy chcę porozmawiać z moim DB?

Rozumiem, że jeśli używam wielu wątków, każdy z nich powinien mieć własną sesję. Ale używając scoped_session(), już upewniam się, że problem nie istnieje, prawda?

Proszę wyjaśnić, czy moje założenia są błędne.
Author: javex, 2012-09-01

3 answers

sessionmaker() jest fabryką, ma zachęcać do umieszczania opcji konfiguracyjnych do tworzenia nowych obiektów Session w jednym miejscu. Jest to opcjonalne, w tym, że można równie łatwo zadzwonić Session(bind=engine, expire_on_commit=False) w każdej chwili, kiedy potrzebujesz nowego Session, z wyjątkiem tego, że jest gadatliwy i zbędny, i chciałem powstrzymać rozprzestrzenianie się małych "pomocników", że każdy podszedł do kwestii tej redundancji w jakiś nowy i bardziej mylący sposób.

Więc sessionmaker() jest tylko narzędziem, które pomoże Ci tworzyć Session obiekty, gdy potrzebujesz oni.

Następna część. Myślę, że pytanie brzmi, jaka jest różnica między tworzeniem nowego Session() w różnych punktach, a używaniem jednego do końca. Odpowiedź, nie bardzo. Session jest kontenerem dla wszystkich umieszczonych w nim obiektów, a następnie śledzi również otwartą transakcję. W momencie wywołania rollback() lub commit(), transakcja jest zakończona, a Session nie ma połączenia z bazą danych, dopóki nie zostanie ponownie wywołany SQL. Linki, które posiada do twojego zmapowane obiekty są słabym odniesieniem, pod warunkiem, że obiekty są czyste od oczekujących zmian, więc nawet w tym zakresie Session opróżni się z powrotem do zupełnie nowego stanu, gdy aplikacja straci wszystkie odniesienia do zmapowanych obiektów. Jeśli pozostawisz go z domyślnym ustawieniem "expire_on_commit", to wszystkie obiekty wygasną po zatwierdzeniu. Jeśli Session będzie wisiał przez pięć lub dwadzieścia minut, a wszystkie rodzaje rzeczy zmienią się w bazie danych następnym razem, gdy jej użyjesz, załaduje się cały nowy stan następnym razem, gdy uzyskasz dostęp do tych obiektów, mimo że są w pamięci od 20 minut.

W aplikacjach internetowych zazwyczaj mówimy: hej, dlaczego nie tworzysz nowego Session na każde żądanie, zamiast używać tego samego w kółko. Ta praktyka zapewnia, że nowe żądanie zaczyna się "czyste". Jeśli niektóre obiekty z poprzedniego żądania nie zostały jeszcze pobrane i jeśli być może wyłączyłeś "expire_on_commit", może jakiś stan z poprzedniego żądania jest nadal a ten stan może być nawet dość stary. Jeśli jesteś ostrożny, aby zostawić expire_on_commit włączone i zdecydowanie zadzwonić commit() lub rollback() na końcu żądania, to jest w porządku, ale jeśli zaczniesz od zupełnie nowego Session, to nie ma nawet pytania, że zaczynasz czysty. Tak więc pomysł, aby rozpocząć każde żądanie od nowego Session jest tak naprawdę najprostszym sposobem, aby upewnić się, że zaczynasz od nowa, i aby użycie expire_on_commit było praktycznie opcjonalne, ponieważ ta flaga może spowodować wiele dodatkowych SQL dla operacja wywołująca commit() w środku serii operacji. Nie jestem pewien, czy to odpowiada na twoje pytanie.

Następna runda jest tym, co wspominasz o gwintowaniu. Jeśli aplikacja jest wielowątkowa, zalecamy upewnienie się, że Session w użyciu jest lokalny do...coś. scoped_session() domyślnie czyni go lokalnym dla bieżącego wątku. W aplikacji internetowej, lokalne do żądania jest w rzeczywistości jeszcze lepiej. Flask-SQLAlchemy wysyła niestandardową "funkcję scope" do scoped_session(), aby otrzymać request-scoped sesja. Przeciętna aplikacja piramidy umieszcza sesję w rejestrze "request". Podczas korzystania z takich schematów idea "Utwórz nową sesję na żądanie start" nadal wygląda jak najprostszy sposób na utrzymanie porządku.

 161
Author: zzzeek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-19 13:02:59

Oprócz doskonałej odpowiedzi zzzeek, Oto prosty przepis na szybkie tworzenie wyrzucanych, zamkniętych sesji:

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

Użycie:

from mymodels import Foo

with db_session("sqlite://") as db:
    foos = db.query(Foo).all()
 15
Author: Berislav Lopac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-11 10:22:36

Możesz utworzyć sesję używając db

db = SQLAlchemy(app)
engine = db.engine
Session = sessionmaker(engine)
session = Session()
 -1
Author: mcolak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-23 04:05:31