rewrite using pyramid and wtforms

This commit is contained in:
evilchili
2024-01-31 22:39:54 -08:00
parent 5faf5c97c1
commit 3444f83c91
15 changed files with 234 additions and 163 deletions
+55
View File
@@ -0,0 +1,55 @@
from itertools import chain
from pyramid_sqlalchemy import BaseObject
from wtforms import validators
class IterableMixin:
"""
Allows for iterating over Model objects' column names and values
"""
def __iter__(self):
values = vars(self)
for attr in self.__mapper__.columns.keys():
if attr in values:
yield attr, values[attr]
def __repr__(self):
return f"{self.__class__.__name__}: {str(dict(self))}"
class FormValidatorMixin:
"""
Add form validation capabilities using the .info attributes of columns.
"""
# column.info could contain any of these keywords. define the list of validators that should apply
# whenever we encounter one such keyword.
_validators_by_keyword = {
'min': [validators.NumberRange],
'max': [validators.NumberRange],
}
@classmethod
def validate(cls, form):
for name, column in cls.__mapper__.columns.items():
if name not in form._fields:
continue
# step through the info keywords and create a deduped list of validator classes that
# should apply to this form field. This prevents adding unnecessary copies of the same
# validator when two or more keywords map to the same one.
extras = set()
for key in column.info.keys():
for val in cls._validators_by_keyword.get(key, []):
extras.add(val)
# Add an instance of every unique validator for this column to the associated form field.
form._fields[name].validators.extend([v(**column.info) for v in extras])
# return the results of the form validation,.
return form.validate()
# class Table(*Bases):
Bases = [BaseObject, IterableMixin, FormValidatorMixin]
+12 -13
View File
@@ -5,6 +5,7 @@ from ttfrog.db.manager import db
from ttfrog.db import schema
from sqlalchemy.exc import IntegrityError
from sqlalchemy.inspection import inspect
# move this to json or whatever
data = {
@@ -28,16 +29,14 @@ def bootstrap():
model = getattr(schema, table)
for rec in records:
with transaction.manager as tx:
obj = model(**rec)
db.session.add(obj)
obj.slug = db.slugify(rec)
try:
tx.commit()
except IntegrityError as e:
tx.abort()
if 'UNIQUE constraint failed' in str(e):
logging.info(f"Skipping existing {table} {rec}")
continue
raise
logging.info(f"Created {table} {rec}")
obj = model(**rec)
try:
with db.transaction():
db.session.add(obj)
obj.slug = db.slugify(rec)
except IntegrityError as e:
if 'UNIQUE constraint failed' in str(e):
logging.info(f"Skipping existing {table} {obj}")
continue
raise
logging.info(f"Created {table} {obj}")
+13 -25
View File
@@ -1,8 +1,8 @@
import transaction
import base64
import hashlib
import logging
from contextlib import contextmanager
from functools import cached_property
from pyramid_sqlalchemy import Session
@@ -10,7 +10,7 @@ from pyramid_sqlalchemy import init_sqlalchemy
from pyramid_sqlalchemy import metadata as _metadata
from sqlalchemy import create_engine
from sqlalchemy.exc import IntegrityError
# from sqlalchemy.exc import IntegrityError
from ttfrog.path import database
import ttfrog.db.schema
@@ -30,7 +30,7 @@ class SQLDatabaseManager:
def engine(self):
return create_engine(self.url)
@cached_property
@property
def session(self):
return Session
@@ -42,31 +42,19 @@ class SQLDatabaseManager:
def tables(self):
return dict((t.name, t) for t in self.metadata.sorted_tables)
@contextmanager
def transaction(self):
with transaction.manager as tm:
yield tm
try:
tm.commit()
except Exception:
tm.abort()
raise
def query(self, *args, **kwargs):
return self.session.query(*args, **kwargs)
def execute(self, statement) -> tuple:
logging.info(statement)
result = None
error = None
try:
with transaction.manager as tx:
result = self.session.execute(statement)
tx.commit()
except IntegrityError as exc:
logging.error(exc)
error = "I AM ERROR."
return result, error
def insert(self, table, **kwargs) -> tuple:
stmt = table.insert().values(**kwargs)
return self.execute(stmt)
def update(self, table, **kwargs):
primary_key = kwargs.pop('id')
stmt = table.update().values(**kwargs).where(table.columns.id == primary_key)
return self.execute(stmt)
def slugify(self, rec: dict) -> str:
"""
Create a uniquish slug from a dictionary.
+13 -15
View File
@@ -1,17 +1,15 @@
from sqlalchemy import MetaData
from sqlalchemy import Table
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import UnicodeText
from sqlalchemy import ForeignKey
from sqlalchemy import CheckConstraint
# from sqlalchemy import PrimaryKeyConstraint
# from sqlalchemy import DateTime
from pyramid_sqlalchemy import BaseObject
from ttfrog.db.base import Bases
class Ancestry(BaseObject):
class Ancestry(*Bases):
__tablename__ = "ancestry"
id = Column(Integer, primary_key=True, autoincrement=True)
@@ -19,17 +17,17 @@ class Ancestry(BaseObject):
slug = Column(String, index=True, unique=True)
class Character(BaseObject):
class Character(*Bases):
__tablename__ = "character"
id = Column(Integer, primary_key=True, autoincrement=True)
slug = Column(String, index=True, unique=True)
ancestry = Column(String, ForeignKey("ancestry.name"))
name = Column(String)
level = Column(Integer, CheckConstraint('level > 0 AND level <= 20'))
str = Column(Integer, CheckConstraint('str >=0'))
dex = Column(Integer, CheckConstraint('dex >=0'))
con = Column(Integer, CheckConstraint('con >=0'))
int = Column(Integer, CheckConstraint('int >=0'))
wis = Column(Integer, CheckConstraint('wis >=0'))
cha = Column(Integer, CheckConstraint('cha >=0'))
ancestry = Column(String, ForeignKey("ancestry.name"), nullable=False)
name = Column(String(255), nullable=False)
level = Column(Integer, nullable=False, info={'min': 1, 'max': 20})
str = Column(Integer, info={'min': 1})
dex = Column(Integer, info={'min': 1})
con = Column(Integer, info={'min': 1})
int = Column(Integer, info={'min': 1})
wis = Column(Integer, info={'min': 1})
cha = Column(Integer, info={'min': 1})