rewrite using pyramid and wtforms
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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})
|
||||
|
||||
Reference in New Issue
Block a user