mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
[FastAPI] Refactor db modifications
For SQLAlchemy to automatically understand updates from the external world, it must use an `autocommit=True` in its session. This change breaks how we were using commit previously, as `autocommit=True` causes SQLAlchemy to commit when a SessionTransaction context hits __exit__. So, a refactoring was required of our tests: All usage of any `db.{create,delete}` must be called **within** a SessionTransaction context, created via new `db.begin()`. From this point forward, we're going to require: ``` with db.begin(): db.create(...) db.delete(...) db.session.delete(object) ``` With this, we now get external DB modifications automatically without reloading or restarting the FastAPI server, which we absolutely need for production. Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
b52059d437
commit
a5943bf2ad
37 changed files with 998 additions and 902 deletions
44
aurweb/db.py
44
aurweb/db.py
|
@ -59,20 +59,15 @@ def query(model, *args, **kwargs):
|
|||
return session.query(model).filter(*args, **kwargs)
|
||||
|
||||
|
||||
def create(model, autocommit: bool = True, *args, **kwargs):
|
||||
def create(model, *args, **kwargs):
|
||||
instance = model(*args, **kwargs)
|
||||
add(instance)
|
||||
if autocommit is True:
|
||||
commit()
|
||||
return instance
|
||||
return add(instance)
|
||||
|
||||
|
||||
def delete(model, *args, autocommit: bool = True, **kwargs):
|
||||
def delete(model, *args, **kwargs):
|
||||
instance = session.query(model).filter(*args, **kwargs)
|
||||
for record in instance:
|
||||
session.delete(record)
|
||||
if autocommit is True:
|
||||
commit()
|
||||
|
||||
|
||||
def rollback():
|
||||
|
@ -84,8 +79,25 @@ def add(model):
|
|||
return model
|
||||
|
||||
|
||||
def commit():
|
||||
session.commit()
|
||||
def begin():
|
||||
""" Begin an SQLAlchemy SessionTransaction.
|
||||
|
||||
This context is **required** to perform an modifications to the
|
||||
database.
|
||||
|
||||
Example:
|
||||
|
||||
with db.begin():
|
||||
object = db.create(...)
|
||||
# On __exit__, db.commit() is run.
|
||||
|
||||
with db.begin():
|
||||
object = db.delete(...)
|
||||
# On __exit__, db.commit() is run.
|
||||
|
||||
:return: A new SessionTransaction based on session
|
||||
"""
|
||||
return session.begin()
|
||||
|
||||
|
||||
def get_sqlalchemy_url():
|
||||
|
@ -155,23 +167,23 @@ def get_engine(echo: bool = False):
|
|||
connect_args=connect_args,
|
||||
echo=echo)
|
||||
|
||||
Session = sessionmaker(autocommit=True, autoflush=False, bind=engine)
|
||||
session = Session()
|
||||
|
||||
if db_backend == "sqlite":
|
||||
# For SQLite, we need to add some custom functions as
|
||||
# they are used in the reference graph method.
|
||||
def regexp(regex, item):
|
||||
return bool(re.search(regex, str(item)))
|
||||
|
||||
@event.listens_for(engine, "begin")
|
||||
def do_begin(conn):
|
||||
@event.listens_for(engine, "connect")
|
||||
def do_begin(conn, record):
|
||||
create_deterministic_function = functools.partial(
|
||||
conn.connection.create_function,
|
||||
conn.create_function,
|
||||
deterministic=True
|
||||
)
|
||||
create_deterministic_function("REGEXP", 2, regexp)
|
||||
|
||||
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
session = Session()
|
||||
|
||||
return engine
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue