Add beginnings of "New Table" feature
nowhere near complete yet, but skeleton is more or less in place
This commit is contained in:
parent
7e852c1836
commit
a061e362c3
|
@ -1135,6 +1135,8 @@ class Form(object):
|
||||||
if record:
|
if record:
|
||||||
try:
|
try:
|
||||||
return record[field_name]
|
return record[field_name]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return getattr(record, field_name, None)
|
return getattr(record, field_name, None)
|
||||||
|
|
||||||
|
|
214
tailbone/templates/tables/create.mako
Normal file
214
tailbone/templates/tables/create.mako
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/create.mako" />
|
||||||
|
|
||||||
|
<%def name="extra_styles()">
|
||||||
|
${parent.extra_styles()}
|
||||||
|
<style type="text/css">
|
||||||
|
.label {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_buefy_form()">
|
||||||
|
<b-steps v-model="activeStep"
|
||||||
|
:animated="false"
|
||||||
|
rounded
|
||||||
|
:has-navigation="false"
|
||||||
|
vertical
|
||||||
|
icon-pack="fas">
|
||||||
|
|
||||||
|
<b-step-item step="1"
|
||||||
|
value="enter-details"
|
||||||
|
label="Enter Details"
|
||||||
|
clickable>
|
||||||
|
<h3 class="is-size-3 block">
|
||||||
|
Enter Details
|
||||||
|
</h3>
|
||||||
|
${parent.render_buefy_form()}
|
||||||
|
</b-step-item>
|
||||||
|
|
||||||
|
<b-step-item step="2"
|
||||||
|
value="write-model"
|
||||||
|
label="Write Model"
|
||||||
|
clickable>
|
||||||
|
<h3 class="is-size-3 block">
|
||||||
|
Write Model
|
||||||
|
</h3>
|
||||||
|
<div class="form">
|
||||||
|
<b-field horizontal label="Table Name">
|
||||||
|
<span>TODO: poser_widget</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Model Class">
|
||||||
|
<span>TODO: PoserWidget</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="File">
|
||||||
|
<span>TODO: ~/src/poser/poser/db/model/widgets.py</span>
|
||||||
|
</b-field>
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="arrow-left"
|
||||||
|
@click="activeStep = 'enter-details'">
|
||||||
|
Back
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="save"
|
||||||
|
@click="activeStep = 'review-model'">
|
||||||
|
Write model class to file
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</b-step-item>
|
||||||
|
|
||||||
|
<b-step-item step="3"
|
||||||
|
value="review-model"
|
||||||
|
label="Review Model">
|
||||||
|
<h3 class="is-size-3 block">
|
||||||
|
Review Model
|
||||||
|
</h3>
|
||||||
|
<p class="block">TODO: review model class here</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="arrow-left"
|
||||||
|
@click="activeStep = 'write-model'">
|
||||||
|
Back
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="check"
|
||||||
|
@click="activeStep = 'write-revision'">
|
||||||
|
Model class looks good!
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</b-step-item>
|
||||||
|
|
||||||
|
<b-step-item step="4"
|
||||||
|
value="write-revision"
|
||||||
|
label="Write Revision">
|
||||||
|
<h3 class="is-size-3 block">
|
||||||
|
Write Revision
|
||||||
|
</h3>
|
||||||
|
<p class="block">
|
||||||
|
You said the model class looked good, so next we will generate
|
||||||
|
a revision script, used to modify DB schema.
|
||||||
|
</p>
|
||||||
|
<p class="block">
|
||||||
|
TODO: write revision script here
|
||||||
|
</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="arrow-left"
|
||||||
|
@click="activeStep = 'review-model'">
|
||||||
|
Back
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="save"
|
||||||
|
@click="activeStep = 'review-revision'">
|
||||||
|
Write revision script to file
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</b-step-item>
|
||||||
|
|
||||||
|
<b-step-item step="5"
|
||||||
|
value="review-revision"
|
||||||
|
label="Review Revision">
|
||||||
|
<h3 class="is-size-3 block">
|
||||||
|
Review Revision
|
||||||
|
</h3>
|
||||||
|
<p class="block">TODO: review revision script here</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="arrow-left"
|
||||||
|
@click="activeStep = 'write-revision'">
|
||||||
|
Back
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="check"
|
||||||
|
@click="activeStep = 'upgrade-db'">
|
||||||
|
Revision script looks good!
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</b-step-item>
|
||||||
|
|
||||||
|
<b-step-item step="6"
|
||||||
|
value="upgrade-db"
|
||||||
|
label="Upgrade DB">
|
||||||
|
<h3 class="is-size-3 block">
|
||||||
|
Upgrade DB
|
||||||
|
</h3>
|
||||||
|
<p class="block">TODO: upgrade DB here</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="arrow-left"
|
||||||
|
@click="activeStep = 'review-revision'">
|
||||||
|
Back
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="check"
|
||||||
|
@click="activeStep = 'review-db'">
|
||||||
|
Upgrade database
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</b-step-item>
|
||||||
|
|
||||||
|
<b-step-item step="7"
|
||||||
|
value="review-db"
|
||||||
|
label="Review DB">
|
||||||
|
<h3 class="is-size-3 block">
|
||||||
|
Review DB
|
||||||
|
</h3>
|
||||||
|
<p class="block">TODO: review DB here</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="arrow-left"
|
||||||
|
@click="activeStep = 'upgrade-db'">
|
||||||
|
Back
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="check"
|
||||||
|
@click="activeStep = 'commit-code'">
|
||||||
|
DB looks good!
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</b-step-item>
|
||||||
|
|
||||||
|
<b-step-item step="8"
|
||||||
|
value="commit-code"
|
||||||
|
label="Commit Code">
|
||||||
|
<h3 class="is-size-3 block">
|
||||||
|
Commit Code
|
||||||
|
</h3>
|
||||||
|
<p class="block">TODO: commit changes here</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="arrow-left"
|
||||||
|
@click="activeStep = 'review-db'">
|
||||||
|
Back
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="check"
|
||||||
|
@click="alert('TODO: redirect to table view')">
|
||||||
|
Code changes are committed!
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</b-step-item>
|
||||||
|
</b-steps>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_this_page_vars()">
|
||||||
|
${parent.modify_this_page_vars()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
ThisPageData.activeStep = null
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -676,7 +676,7 @@ class MasterView(View):
|
||||||
return self.render_to_response(template, context)
|
return self.render_to_response(template, context)
|
||||||
|
|
||||||
def make_create_form(self):
|
def make_create_form(self):
|
||||||
return self.make_form(self.get_model_class())
|
return self.make_form()
|
||||||
|
|
||||||
def save_create_form(self, form):
|
def save_create_form(self, form):
|
||||||
uploads = self.normalize_uploads(form)
|
uploads = self.normalize_uploads(form)
|
||||||
|
|
|
@ -26,6 +26,12 @@ Views with info about the underlying Rattail tables
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import colander
|
||||||
|
from deform import widget as dfwidget
|
||||||
|
|
||||||
from tailbone.views import MasterView
|
from tailbone.views import MasterView
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,20 +40,31 @@ class TableView(MasterView):
|
||||||
Master view for tables
|
Master view for tables
|
||||||
"""
|
"""
|
||||||
normalized_model_name = 'table'
|
normalized_model_name = 'table'
|
||||||
model_key = 'name'
|
model_key = 'table_name'
|
||||||
model_title = "Table"
|
model_title = "Table"
|
||||||
creatable = False
|
creatable = False
|
||||||
editable = False
|
editable = False
|
||||||
deletable = False
|
deletable = False
|
||||||
viewable = False
|
|
||||||
filterable = False
|
filterable = False
|
||||||
pageable = False
|
pageable = False
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
'branch_name': "Schema Branch",
|
||||||
|
'model_name': "Model Class",
|
||||||
|
'module_name': "Module",
|
||||||
|
'module_file': "File",
|
||||||
|
}
|
||||||
|
|
||||||
grid_columns = [
|
grid_columns = [
|
||||||
'name',
|
'table_name',
|
||||||
'row_count',
|
'row_count',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
super(TableView, self).__init__(request)
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
self.db_handler = app.get_db_handler()
|
||||||
|
|
||||||
def get_data(self, **kwargs):
|
def get_data(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Fetch existing table names and estimate row counts via PG SQL
|
Fetch existing table names and estimate row counts via PG SQL
|
||||||
|
@ -62,18 +79,166 @@ class TableView(MasterView):
|
||||||
order by n_live_tup desc;
|
order by n_live_tup desc;
|
||||||
"""
|
"""
|
||||||
result = self.Session.execute(sql)
|
result = self.Session.execute(sql)
|
||||||
return [dict(name=row['relname'], row_count=row['n_live_tup'])
|
return [dict(table_name=row['relname'], row_count=row['n_live_tup'])
|
||||||
for row in result]
|
for row in result]
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def configure_grid(self, g):
|
||||||
g.sorters['name'] = g.make_simple_sorter('name', foldcase=True)
|
super(TableView, self).configure_grid(g)
|
||||||
|
|
||||||
|
# table_name
|
||||||
|
g.sorters['table_name'] = g.make_simple_sorter('table_name', foldcase=True)
|
||||||
|
g.set_sort_defaults('table_name')
|
||||||
|
g.set_searchable('table_name')
|
||||||
|
g.set_link('table_name')
|
||||||
|
|
||||||
|
# row_count
|
||||||
g.sorters['row_count'] = g.make_simple_sorter('row_count')
|
g.sorters['row_count'] = g.make_simple_sorter('row_count')
|
||||||
g.set_sort_defaults('name')
|
|
||||||
|
|
||||||
g.set_searchable('name')
|
def get_instance(self):
|
||||||
|
from sqlalchemy_utils import get_mapper
|
||||||
|
|
||||||
# TODO: deprecate / remove this
|
model = self.model
|
||||||
TablesView = TableView
|
table_name = self.request.matchdict['table_name']
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
select n_live_tup
|
||||||
|
from pg_stat_user_tables
|
||||||
|
where schemaname = 'public' and relname = :table_name
|
||||||
|
order by n_live_tup desc;
|
||||||
|
"""
|
||||||
|
result = self.Session.execute(sql, {'table_name': table_name})
|
||||||
|
row = result.fetchone()
|
||||||
|
if not row:
|
||||||
|
raise self.notfound()
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'table_name': table_name,
|
||||||
|
'row_count': row['n_live_tup'],
|
||||||
|
}
|
||||||
|
|
||||||
|
table = model.Base.metadata.tables.get(table_name)
|
||||||
|
if table is not None:
|
||||||
|
try:
|
||||||
|
mapper = get_mapper(table)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
data['model_name'] = mapper.class_.__name__
|
||||||
|
data['model_title'] = mapper.class_.get_model_title()
|
||||||
|
data['model_title_plural'] = mapper.class_.get_model_title_plural()
|
||||||
|
data['description'] = mapper.class_.__doc__
|
||||||
|
|
||||||
|
# TODO: how to reliably get branch? must walk all revisions?
|
||||||
|
module_parts = mapper.class_.__module__.split('.')
|
||||||
|
data['branch_name'] = module_parts[0]
|
||||||
|
|
||||||
|
data['module_name'] = mapper.class_.__module__
|
||||||
|
data['module_file'] = sys.modules[mapper.class_.__module__].__file__
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_instance_title(self, table):
|
||||||
|
return table['table_name']
|
||||||
|
|
||||||
|
def make_form_schema(self):
|
||||||
|
return TableSchema()
|
||||||
|
|
||||||
|
def configure_form(self, f):
|
||||||
|
super(TableView, self).configure_form(f)
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
|
||||||
|
# exclude some fields when creating
|
||||||
|
if self.creating:
|
||||||
|
f.remove('row_count',
|
||||||
|
'module_name',
|
||||||
|
'module_file')
|
||||||
|
|
||||||
|
# branch_name
|
||||||
|
if self.creating:
|
||||||
|
|
||||||
|
# move this field to top of form, as it's more fundamental
|
||||||
|
# when creating new table
|
||||||
|
f.remove('branch_name')
|
||||||
|
f.insert(0, 'branch_name')
|
||||||
|
|
||||||
|
# define options for dropdown
|
||||||
|
branches = self.db_handler.get_alembic_branch_names()
|
||||||
|
values = [(branch, branch) for branch in branches]
|
||||||
|
f.set_widget('branch_name', dfwidget.SelectWidget(values=values))
|
||||||
|
|
||||||
|
# default to custom app branch, if applicable
|
||||||
|
table_prefix = app.get_table_prefix()
|
||||||
|
if table_prefix in branches:
|
||||||
|
f.set_default('branch_name', table_prefix)
|
||||||
|
f.set_helptext('branch_name', "Leave this set to your custom app branch, unless you know what you're doing.")
|
||||||
|
|
||||||
|
# table_name
|
||||||
|
if self.creating:
|
||||||
|
f.set_default('table_name', '{}_widget'.format(app.get_table_prefix()))
|
||||||
|
f.set_helptext('table_name', "Should be singular in nature, i.e. 'widget' not 'widgets'")
|
||||||
|
|
||||||
|
# model_name
|
||||||
|
if self.creating:
|
||||||
|
f.set_default('model_name', '{}Widget'.format(app.get_class_prefix()))
|
||||||
|
f.set_helptext('model_name', "Should be singular in nature, i.e. 'Widget' not 'Widgets'")
|
||||||
|
|
||||||
|
# model_title*
|
||||||
|
if self.creating:
|
||||||
|
f.set_default('model_title', 'Widget')
|
||||||
|
f.set_helptext('model_title', "Human-friendly singular model title.")
|
||||||
|
f.set_default('model_title_plural', 'Widgets')
|
||||||
|
f.set_helptext('model_title_plural', "Human-friendly plural model title.")
|
||||||
|
|
||||||
|
# description
|
||||||
|
if self.creating:
|
||||||
|
f.set_default('description', "Represents a cool widget.")
|
||||||
|
f.set_helptext('description', "Brief description of what a record in this table represents.")
|
||||||
|
|
||||||
|
# TODO: not sure yet how to handle "save" action
|
||||||
|
# def save_create_form(self, form):
|
||||||
|
# return form.validated
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def defaults(cls, config):
|
||||||
|
rattail_config = config.registry.settings.get('rattail_config')
|
||||||
|
|
||||||
|
# allow creating tables only if *not* production
|
||||||
|
if not rattail_config.production():
|
||||||
|
cls.creatable = True
|
||||||
|
|
||||||
|
cls._defaults(config)
|
||||||
|
|
||||||
|
|
||||||
|
class TablesView(TableView):
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
warnings.warn("TablesView is deprecated; please use TableView instead",
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
|
super(TablesView, self).__init__(request)
|
||||||
|
|
||||||
|
|
||||||
|
class TableSchema(colander.Schema):
|
||||||
|
|
||||||
|
table_name = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
row_count = colander.SchemaNode(colander.Integer(),
|
||||||
|
missing=colander.null)
|
||||||
|
|
||||||
|
model_name = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
model_title = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
model_title_plural = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
description = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
branch_name = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
module_name = colander.SchemaNode(colander.String(),
|
||||||
|
missing=colander.null)
|
||||||
|
|
||||||
|
module_file = colander.SchemaNode(colander.String(),
|
||||||
|
missing=colander.null)
|
||||||
|
|
||||||
|
|
||||||
def defaults(config, **kwargs):
|
def defaults(config, **kwargs):
|
||||||
|
|
Loading…
Reference in a new issue