fixing form handling for relationships
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
from ttfrog.db.schema import Ancestry
|
||||
from ttfrog.db.manager import db
|
||||
from wtforms_alchemy import ModelForm
|
||||
|
||||
|
||||
class AncestryForm(ModelForm):
|
||||
class Meta:
|
||||
model = Ancestry
|
||||
exclude = ['slug']
|
||||
|
||||
def get_session():
|
||||
return db.session
|
||||
@@ -1,19 +1,23 @@
|
||||
import logging
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
from wtforms_sqlalchemy.orm import model_form
|
||||
from collections import defaultdict
|
||||
|
||||
from pyramid.httpexceptions import HTTPFound
|
||||
from pyramid.interfaces import IRoutesMapper
|
||||
|
||||
from sqlalchemy.inspection import inspect
|
||||
|
||||
from ttfrog.attribute_map import AttributeMap
|
||||
from ttfrog.db.manager import db
|
||||
|
||||
|
||||
def get_all_routes(request):
|
||||
routes = {
|
||||
'static': '/static',
|
||||
}
|
||||
uri_pattern = re.compile(r"^([^\{\*]+)")
|
||||
mapper = request.registry.queryUtility(IRoutesMapper)
|
||||
routes = {}
|
||||
for route in mapper.get_routes():
|
||||
if route.name.startswith('__'):
|
||||
continue
|
||||
@@ -23,89 +27,93 @@ def get_all_routes(request):
|
||||
return routes
|
||||
|
||||
|
||||
def query_factory(model):
|
||||
return lambda: db.query(model).all()
|
||||
|
||||
|
||||
class BaseController:
|
||||
model = None
|
||||
model_form = None
|
||||
|
||||
def __init__(self, request):
|
||||
self.request = request
|
||||
self.attrs = defaultdict(str)
|
||||
self.record = None
|
||||
self.form = None
|
||||
self.model_form = None
|
||||
self._slug = None
|
||||
self._record = None
|
||||
self._form = None
|
||||
|
||||
self.config = {
|
||||
'static_url': '/static',
|
||||
'project_name': 'TTFROG'
|
||||
}
|
||||
self.configure_for_model()
|
||||
self.configure()
|
||||
|
||||
def configure_for_model(self):
|
||||
@property
|
||||
def slug(self):
|
||||
if not self._slug:
|
||||
parts = self.request.matchdict.get('uri', '').split('-')
|
||||
self._slug = parts[0].replace('/', '')
|
||||
return self._slug
|
||||
|
||||
@property
|
||||
def record(self):
|
||||
if not self._record and self.model:
|
||||
try:
|
||||
self._record = db.query(self.model).filter(self.model.slug == self.slug)[0]
|
||||
except IndexError:
|
||||
logging.warning(f"Could not load record with slug {self.slug}")
|
||||
self._record = self.model()
|
||||
return self._record
|
||||
|
||||
@property
|
||||
def form(self):
|
||||
if not self.model:
|
||||
return
|
||||
if not self.model_form:
|
||||
self.model_form = model_form(self.model, db_session=db.session)
|
||||
if not self.record:
|
||||
self.record = self.get_record_from_slug()
|
||||
if not self._form:
|
||||
if self.request.POST:
|
||||
self._form = self.model_form(self.request.POST, obj=self.record)
|
||||
else:
|
||||
self._form = self.model_form(obj=self.record)
|
||||
return self._form
|
||||
|
||||
def configure_for_model(self):
|
||||
if 'all_records' not in self.attrs:
|
||||
self.attrs['all_records'] = db.query(self.model).all()
|
||||
|
||||
def configure(self):
|
||||
pass
|
||||
def coerce_foreign_keys(self):
|
||||
inspector = inspect(db.engine)
|
||||
foreign_keys = inspector.get_foreign_keys(table_name=self.record.__class__.__tablename__)
|
||||
for foreign_key in foreign_keys:
|
||||
for col in inspector.get_columns(foreign_key['referred_table']):
|
||||
if col['name'] == foreign_key['referred_columns'][0]:
|
||||
col_name = foreign_key['constrained_columns'][0]
|
||||
col_type = col['type'].python_type
|
||||
col_value = col_type(getattr(self.record, col_name))
|
||||
setattr(self.record, col_name, col_value)
|
||||
|
||||
def get_record_from_slug(self):
|
||||
if not self.model:
|
||||
return
|
||||
parts = self.request.matchdict.get('uri', '').split('-')
|
||||
if not parts:
|
||||
return
|
||||
slug = parts[0].replace('/', '')
|
||||
if not slug:
|
||||
return
|
||||
try:
|
||||
return db.query(self.model).filter(self.model.slug == slug)[0]
|
||||
except IndexError:
|
||||
logging.warning(f"Could not load record with slug {slug}")
|
||||
|
||||
def process_form(self):
|
||||
if not self.model:
|
||||
return False
|
||||
|
||||
if self.request.method == 'POST':
|
||||
|
||||
# if we haven't loaded a record, we're creating a new one
|
||||
if not self.record:
|
||||
self.record = self.model()
|
||||
|
||||
# generate a form object using the POST form data and the db record
|
||||
self.form = self.model_form(self.request.POST, obj=self.record)
|
||||
if self.model.validate(self.form):
|
||||
# update the record. If it's a record bound to the session
|
||||
# updates will be commited automatically. Otherwise we must
|
||||
# add and commit the record.
|
||||
self.form.populate_obj(self.record)
|
||||
if not self.record.id:
|
||||
with db.transaction():
|
||||
db.session.add(self.record)
|
||||
logging.debug(f"Added {self.record = }")
|
||||
return True
|
||||
return False
|
||||
self.form = self.model_form(obj=self.record)
|
||||
return False
|
||||
|
||||
def output(self, **kwargs) -> dict:
|
||||
return dict(c=dict(
|
||||
config=self.config,
|
||||
request=self.request,
|
||||
form=self.form,
|
||||
record=self.record,
|
||||
routes=get_all_routes(self.request),
|
||||
**self.attrs,
|
||||
**kwargs,
|
||||
))
|
||||
def template_context(self, **kwargs) -> dict:
|
||||
return AttributeMap.from_dict({
|
||||
'c': dict(
|
||||
config=self.config,
|
||||
request=self.request,
|
||||
form=self.form,
|
||||
record=self.record,
|
||||
routes=get_all_routes(self.request),
|
||||
**self.attrs,
|
||||
**kwargs,
|
||||
)
|
||||
})
|
||||
|
||||
def response(self):
|
||||
if self.process_form():
|
||||
return HTTPFound(location=f"{self.request.current_route_path}/{self.record.uri}")
|
||||
return self.output()
|
||||
if not (self.request.POST and self.form):
|
||||
return
|
||||
if self.form.validate():
|
||||
self.form.populate_obj(self.record)
|
||||
self.coerce_foreign_keys()
|
||||
if not self.record.id:
|
||||
with db.transaction():
|
||||
db.session.add(self.record)
|
||||
db.session.flush()
|
||||
logging.debug(f"Added {self.record = }")
|
||||
location = f"{self.request.current_route_path()}/{self.record.uri}"
|
||||
return HTTPFound(location=location)
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
from ttfrog.webserver.controllers import BaseController
|
||||
from ttfrog.db.schema import Character
|
||||
from ttfrog.webserver.controllers.base import BaseController, query_factory
|
||||
from ttfrog.db.schema import Character, Ancestry
|
||||
from ttfrog.db.manager import db
|
||||
from wtforms_alchemy import ModelForm, QuerySelectField
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
|
||||
class CharacterForm(ModelForm):
|
||||
class Meta:
|
||||
model = Character
|
||||
exclude = ['slug']
|
||||
|
||||
def get_session():
|
||||
return db.session
|
||||
|
||||
ancestry = QuerySelectField('Ancestry', validators=[InputRequired()],
|
||||
query_factory=query_factory(Ancestry), get_label='name')
|
||||
|
||||
|
||||
class CharacterSheet(BaseController):
|
||||
model = Character
|
||||
model = CharacterForm.Meta.model
|
||||
model_form = CharacterForm
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from wtforms_alchemy import ModelForm
|
||||
from db.schema import Character
|
||||
|
||||
|
||||
class CharacterForm(ModelForm):
|
||||
class Meta:
|
||||
model = Character
|
||||
@@ -12,5 +12,5 @@ def index(request):
|
||||
|
||||
@view_config(route_name='sheet', renderer='character_sheet.html')
|
||||
def sheet(request):
|
||||
sheet = request.context
|
||||
return sheet.response()
|
||||
controller = request.context
|
||||
return controller.response() or controller.template_context()
|
||||
|
||||
Reference in New Issue
Block a user