fixed most deprecations from flask and sqlalchemy

This commit is contained in:
Tim Gröger 2023-04-09 20:57:15 +02:00
parent cfbb557539
commit af2c674ce4
11 changed files with 56 additions and 22 deletions

View File

@ -1,9 +1,12 @@
import enum
import json
from flask import Flask
from flask_cors import CORS
from datetime import datetime, date
from flask.json import JSONEncoder, jsonify
from flask.json import jsonify
from json import JSONEncoder
from flask.json.provider import JSONProvider
from sqlalchemy.exc import OperationalError
from werkzeug.exceptions import HTTPException
@ -11,6 +14,9 @@ from flaschengeist import logger
from flaschengeist.controller import pluginController
from flaschengeist.utils.hook import Hook
from flaschengeist.config import configure_app
from flaschengeist.plugins import Plugin
from flaschengeist.database import db
class CustomJSONEncoder(JSONEncoder):
@ -33,6 +39,19 @@ class CustomJSONEncoder(JSONEncoder):
return list(iterable)
return JSONEncoder.default(self, o)
class CustomJSONProvider(JSONProvider):
ensure_ascii: bool = True
sort_keys: bool = True
def dumps(self, obj, **kwargs):
kwargs.setdefault("ensure_ascii", self.ensure_ascii)
kwargs.setdefault("sort_keys", self.sort_keys)
return json.dumps(obj, **kwargs, cls=CustomJSONEncoder)
def loads(self, s: str | bytes, **kwargs):
return json.loads(s, **kwargs)
@Hook("plugins.loaded")
def load_plugins(app: Flask):
@ -43,7 +62,9 @@ def load_plugins(app: Flask):
try:
# Load class
cls = plugin.entry_point.load()
plugin = cls.query.get(plugin.id) if plugin.id is not None else plugin
#plugin = cls.query.get(plugin.id) if plugin.id is not None else plugin
#plugin = db.session.query(cls).get(plugin.id) if plugin.id is not None else plugin
plugin = db.session.get(cls, plugin.id) if plugin.id is not None else plugin
# Custom loading tasks
plugin.load()
# Register blueprint
@ -60,7 +81,9 @@ def load_plugins(app: Flask):
def create_app(test_config=None, cli=False):
app = Flask("flaschengeist")
app.json_encoder = CustomJSONEncoder
# app.json_encoder = CustomJSONEncoder
app.json_provider_class = CustomJSONProvider
app.json = CustomJSONProvider(app)
CORS(app)
with app.app_context():

View File

@ -28,7 +28,7 @@ def read_configuration(test_config):
if not test_config:
paths.append(Path.home() / ".config")
if "FLASCHENGEIST_CONF" in os.environ:
paths.append(Path(os.environ.get("FLASCHENGEIST_CONF")))
paths.append(Path(str(os.environ.get("FLASCHENGEIST_CONF"))))
for loc in paths:
try:

View File

@ -8,6 +8,7 @@ from ..database.types import ModelSerializeMixin, Serial
class Image(db.Model, ModelSerializeMixin):
__allow_unmapped__ = True
__tablename__ = "image"
id: int = db.Column(Serial, primary_key=True)
filename_: str = db.Column("filename", db.String(255), nullable=False)

View File

@ -8,6 +8,7 @@ from ..database.types import Serial, UtcDateTime, ModelSerializeMixin
class Notification(db.Model, ModelSerializeMixin):
__allow_unmapped__ = True
__tablename__ = "notification"
id: int = db.Column("id", Serial, primary_key=True)
text: str = db.Column(db.Text)

View File

@ -1,6 +1,6 @@
from __future__ import annotations # TODO: Remove if python requirement is >= 3.12 (? PEP 563 is defered)
from typing import Any
from typing import Any, List, Dict
from sqlalchemy.orm.collections import attribute_mapped_collection
from ..database import db
@ -8,6 +8,7 @@ from ..database.types import Serial
class PluginSetting(db.Model):
__allow_unmapped__ = True
__tablename__ = "plugin_setting"
id = db.Column("id", Serial, primary_key=True)
plugin_id: int = db.Column("plugin", Serial, db.ForeignKey("plugin.id"))
@ -16,6 +17,7 @@ class PluginSetting(db.Model):
class BasePlugin(db.Model):
__allow_unmapped__ = True
__tablename__ = "plugin"
id: int = db.Column("id", Serial, primary_key=True)
name: str = db.Column(db.String(127), nullable=False)
@ -24,7 +26,7 @@ class BasePlugin(db.Model):
"""The latest installed version"""
enabled: bool = db.Column(db.Boolean, default=False)
"""Enabled state of the plugin"""
permissions: list = db.relationship(
permissions: List["Permission"] = db.relationship(
"Permission", cascade="all, delete, delete-orphan", back_populates="plugin_", lazy="select"
)
"""Optional list of custom permissions used by the plugin
@ -33,11 +35,11 @@ class BasePlugin(db.Model):
to prevent clashes with other plugins. E. g. instead of *delete* use *plugin_delete*.
"""
__settings: dict[str, "PluginSetting"] = db.relationship(
__settings: Dict[str, "PluginSetting"] = db.relationship(
"PluginSetting",
collection_class=attribute_mapped_collection("name"),
cascade="all, delete, delete-orphan",
lazy="select",
lazy="subquery",
)
def get_setting(self, name: str, **kwargs):

View File

@ -17,6 +17,7 @@ class Session(db.Model, ModelSerializeMixin):
token: String to verify access later.
"""
__allow_unmapped__ = True
__tablename__ = "session"
expires: datetime = db.Column(UtcDateTime)
token: str = db.Column(db.String(32), unique=True)

View File

@ -1,6 +1,6 @@
from __future__ import annotations # TODO: Remove if python requirement is >= 3.12 (? PEP 563 is defered)
from typing import Optional
from typing import Optional, Union, List
from datetime import date, datetime
from sqlalchemy.orm.collections import attribute_mapped_collection
@ -21,6 +21,7 @@ role_permission_association_table = db.Table(
class Permission(db.Model, ModelSerializeMixin):
__allow_unmapped__ = True
__tablename__ = "permission"
name: str = db.Column(db.String(30), unique=True)
@ -30,10 +31,11 @@ class Permission(db.Model, ModelSerializeMixin):
class Role(db.Model, ModelSerializeMixin):
__allow_unmapped__ = True
__tablename__ = "role"
id: int = db.Column(Serial, primary_key=True)
name: str = db.Column(db.String(30), unique=True)
permissions: list[Permission] = db.relationship("Permission", secondary=role_permission_association_table)
permissions: List[Permission] = db.relationship("Permission", secondary=role_permission_association_table)
class User(db.Model, ModelSerializeMixin):
@ -51,6 +53,7 @@ class User(db.Model, ModelSerializeMixin):
birthday: Birthday of the user
"""
__allow_unmapped__ = True
__tablename__ = "user"
userid: str = db.Column(db.String(30), unique=True, nullable=False)
display_name: str = db.Column(db.String(30))
@ -59,15 +62,15 @@ class User(db.Model, ModelSerializeMixin):
deleted: bool = db.Column(db.Boolean(), default=False)
birthday: Optional[date] = db.Column(db.Date)
mail: str = db.Column(db.String(60))
roles: list[str] = []
permissions: Optional[list[str]] = None
roles: List[str] = []
# Protected stuff for backend use only
id_ = db.Column("id", Serial, primary_key=True)
roles_: list[Role] = db.relationship("Role", secondary=association_table, cascade="save-update, merge")
sessions_: list[Session] = db.relationship("Session", back_populates="user_", cascade="all, delete, delete-orphan")
roles_: List[Role] = db.relationship("Role", secondary=association_table, cascade="save-update, merge")
sessions_: List[Session] = db.relationship("Session", back_populates="user_", cascade="all, delete, delete-orphan")
avatar_: Optional[Image] = db.relationship("Image", cascade="all, delete, delete-orphan", single_parent=True)
reset_requests_: list["_PasswordReset"] = db.relationship("_PasswordReset", cascade="all, delete, delete-orphan")
reset_requests_: List["_PasswordReset"] = db.relationship("_PasswordReset", cascade="all, delete, delete-orphan")
# Private stuff for internal use
_avatar_id = db.Column("avatar", Serial, db.ForeignKey("image.id"))
@ -107,6 +110,7 @@ class User(db.Model, ModelSerializeMixin):
class _UserAttribute(db.Model, ModelSerializeMixin):
__allow_unmapped__ = True
__tablename__ = "user_attribute"
id = db.Column("id", Serial, primary_key=True)
user: User = db.Column("user", Serial, db.ForeignKey("user.id"), nullable=False)
@ -117,6 +121,7 @@ class _UserAttribute(db.Model, ModelSerializeMixin):
class _PasswordReset(db.Model):
"""Table containing password reset requests"""
__allow_unmapped__ = True
__tablename__ = "password_reset"
_user_id: User = db.Column("user", Serial, db.ForeignKey("user.id"), primary_key=True)
user: User = db.relationship("User", back_populates="reset_requests_", foreign_keys=[_user_id])

View File

@ -100,7 +100,7 @@ class Plugin(BasePlugin):
@property
def entry_point(self):
ep = entry_points(group="flaschengeist.plugins", name=self.name)
ep = tuple(entry_points(group="flaschengeist.plugins", name=self.name))
return ep[0]
def load(self):
@ -158,14 +158,13 @@ class Plugin(BasePlugin):
Args:
permissions: List of permissions to install
"""
cur_perm = set(x.name for x in self.permissions)
cur_perm = set(x.name for x in self.permissions or [])
all_perm = set(permissions)
new_perms = all_perm - cur_perm
self.permissions = list(filter(lambda x: x.name in permissions, self.permissions)) + [
Permission(name=x, plugin_=self) for x in new_perms
]
_perms = [ Permission(name=x, plugin_=self) for x in new_perms ]
#self.permissions = list(filter(lambda x: x.name in permissions, self.permissions and isinstance(self.permissions, list) or []))
self.permissions.extend(_perms)
class AuthPlugin(Plugin):
"""Base class for all authentification plugins

View File

@ -56,6 +56,7 @@ def service_debit():
class BalancePlugin(Plugin):
#id = "dev.flaschengeist.balance"
models = models
def install(self):
@ -71,7 +72,7 @@ class BalancePlugin(Plugin):
add_scheduled(f"{id}.service_debit", service_debit, minutes=1)
@before_update_user
def set_default_limit(user):
def set_default_limit(user, *args):
from . import balance_controller
try:

View File

@ -8,6 +8,7 @@ from flaschengeist.models import ModelSerializeMixin, UtcDateTime, Serial
class Transaction(db.Model, ModelSerializeMixin):
__allow_unmapped__ = True
__tablename__ = "balance_transaction"
# Protected foreign key properties
_receiver_id = db.Column("receiver_id", Serial, db.ForeignKey("user.id"))

View File

@ -30,7 +30,7 @@ install_requires =
sqlalchemy_utils>=0.38.3
# Importlib requirement can be dropped when python requirement is >= 3.10
importlib_metadata>=4.3
sqlalchemy>=1.4.40, <2.0
sqlalchemy >= 2.0
toml
werkzeug>=2.2.2