Semi-finish logic for writing new table model class to file
definitely needs more polish and features, but the gist..
This commit is contained in:
parent
fb7368993c
commit
cac005f993
|
@ -10,7 +10,7 @@
|
||||||
</style>
|
</style>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_buefy_form()">
|
<%def name="render_this_page()">
|
||||||
<b-steps v-model="activeStep"
|
<b-steps v-model="activeStep"
|
||||||
:animated="false"
|
:animated="false"
|
||||||
rounded
|
rounded
|
||||||
|
@ -25,26 +25,257 @@
|
||||||
<h3 class="is-size-3 block">
|
<h3 class="is-size-3 block">
|
||||||
Enter Details
|
Enter Details
|
||||||
</h3>
|
</h3>
|
||||||
${parent.render_buefy_form()}
|
|
||||||
|
<b-field label="Schema Branch" horizontal
|
||||||
|
message="Leave this set to your custom app branch, unless you know what you're doing.">
|
||||||
|
<b-select v-model="tableBranch">
|
||||||
|
<option v-for="branch in branchOptions"
|
||||||
|
:key="branch"
|
||||||
|
:value="branch">
|
||||||
|
{{ branch }}
|
||||||
|
</option>
|
||||||
|
</b-select>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field grouped>
|
||||||
|
|
||||||
|
<b-field label="Table Name"
|
||||||
|
message="Should be singular in nature, i.e. 'widget' not 'widgets'">
|
||||||
|
<b-input v-model="tableName">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Model/Class Name"
|
||||||
|
message="Should be singular in nature, i.e. 'Widget' not 'Widgets'">
|
||||||
|
<b-input v-model="tableModelName">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field grouped>
|
||||||
|
|
||||||
|
<b-field label="Model Title"
|
||||||
|
message="Human-friendly singular model title.">
|
||||||
|
<b-input v-model="tableModelTitle">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Model Title Plural"
|
||||||
|
message="Human-friendly plural model title.">
|
||||||
|
<b-input v-model="tableModelTitlePlural">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Description"
|
||||||
|
message="Brief description of what a record in this table represents.">
|
||||||
|
<b-input v-model="tableDescription">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field>
|
||||||
|
<b-checkbox v-model="tableVersioned">
|
||||||
|
Record version data for this table
|
||||||
|
</b-checkbox>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div class="level-left">
|
||||||
|
<div class="level-item">
|
||||||
|
<h4 class="block is-size-4">Columns</h4>
|
||||||
|
</div>
|
||||||
|
<div class="level-item">
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="plus"
|
||||||
|
@click="tableAddColumn()">
|
||||||
|
New
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<b-table
|
||||||
|
:data="tableColumns">
|
||||||
|
% if buefy_0_8:
|
||||||
|
<template slot-scope="props">
|
||||||
|
% endif
|
||||||
|
|
||||||
|
<b-table-column field="name"
|
||||||
|
label="Name"
|
||||||
|
% if not buefy_0_8:
|
||||||
|
v-slot="props"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
{{ props.row.name }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="data_type"
|
||||||
|
label="Data Type"
|
||||||
|
% if not buefy_0_8:
|
||||||
|
v-slot="props"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
{{ props.row.data_type }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="nullable"
|
||||||
|
label="Nullable"
|
||||||
|
% if not buefy_0_8:
|
||||||
|
v-slot="props"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
{{ props.row.nullable ? "Yes" : "No" }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="versioned"
|
||||||
|
label="Versioned"
|
||||||
|
:visible="tableVersioned"
|
||||||
|
% if not buefy_0_8:
|
||||||
|
v-slot="props"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
{{ props.row.versioned ? "Yes" : "No" }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="description"
|
||||||
|
label="Description"
|
||||||
|
% if not buefy_0_8:
|
||||||
|
v-slot="props"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
{{ props.row.description }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="actions"
|
||||||
|
label="Actions"
|
||||||
|
% if not buefy_0_8:
|
||||||
|
v-slot="props"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
<a v-if="props.row.name != 'uuid'"
|
||||||
|
href="#"
|
||||||
|
@click.prevent="tableEditColumn(props.row)">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<a v-if="props.row.name != 'uuid'"
|
||||||
|
href="#"
|
||||||
|
class="has-text-danger"
|
||||||
|
@click.prevent="tableDeleteColumn(props.index)">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
Delete
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
% if buefy_0_8:
|
||||||
|
</template>
|
||||||
|
% endif
|
||||||
|
</b-table>
|
||||||
|
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="editingColumnShowDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">
|
||||||
|
{{ (editingColumn && editingColumn.name) ? "Edit" : "New" }} Column
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
|
||||||
|
<b-field label="Name">
|
||||||
|
<b-input v-model="editingColumnName"
|
||||||
|
ref="editingColumnName">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Data Type">
|
||||||
|
<b-input v-model="editingColumnDataType"></b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field grouped>
|
||||||
|
|
||||||
|
<b-field label="Nullable">
|
||||||
|
<b-checkbox v-model="editingColumnNullable"
|
||||||
|
native-value="true">
|
||||||
|
{{ editingColumnNullable }}
|
||||||
|
</b-checkbox>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Versioned"
|
||||||
|
v-if="tableVersioned">
|
||||||
|
<b-checkbox v-model="editingColumnVersioned"
|
||||||
|
native-value="true">
|
||||||
|
{{ editingColumnVersioned }}
|
||||||
|
</b-checkbox>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Description">
|
||||||
|
<b-input v-model="editingColumnDescription"></b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button @click="editingColumnShowDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="save"
|
||||||
|
@click="editingColumnSave()">
|
||||||
|
Save
|
||||||
|
</b-button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="check"
|
||||||
|
@click="activeStep = 'write-model'">
|
||||||
|
Details are complete
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</b-step-item>
|
</b-step-item>
|
||||||
|
|
||||||
<b-step-item step="2"
|
<b-step-item step="2"
|
||||||
value="write-model"
|
value="write-model"
|
||||||
label="Write Model"
|
label="Write Model">
|
||||||
clickable>
|
|
||||||
<h3 class="is-size-3 block">
|
<h3 class="is-size-3 block">
|
||||||
Write Model
|
Write Model
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<b-field label="Schema Branch" horizontal>
|
||||||
|
{{ tableBranch }}
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Table Name" horizontal>
|
||||||
|
{{ tableName }}
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Model Class" horizontal>
|
||||||
|
{{ tableModelName }}
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field horizontal label="File">
|
||||||
|
<b-input v-model="tableModelFile"></b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
<div class="form">
|
<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">
|
<div class="buttons">
|
||||||
<b-button icon-pack="fas"
|
<b-button icon-pack="fas"
|
||||||
icon-left="arrow-left"
|
icon-left="arrow-left"
|
||||||
|
@ -54,8 +285,9 @@
|
||||||
<b-button type="is-primary"
|
<b-button type="is-primary"
|
||||||
icon-pack="fas"
|
icon-pack="fas"
|
||||||
icon-left="save"
|
icon-left="save"
|
||||||
@click="activeStep = 'review-model'">
|
@click="writeModelFile()"
|
||||||
Write model class to file
|
:disabled="writingModelFile">
|
||||||
|
{{ writingModelFile ? "Working, please wait..." : "Write model class to file" }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -206,6 +438,107 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
ThisPageData.activeStep = null
|
ThisPageData.activeStep = null
|
||||||
|
ThisPageData.branchOptions = ${json.dumps(branch_name_options)|n}
|
||||||
|
|
||||||
|
ThisPageData.tableBranch = ${json.dumps(branch_name)|n}
|
||||||
|
ThisPageData.tableName = '${rattail_app.get_table_prefix()}_widget'
|
||||||
|
ThisPageData.tableModelName = '${rattail_app.get_class_prefix()}Widget'
|
||||||
|
ThisPageData.tableModelTitle = 'Widget'
|
||||||
|
ThisPageData.tableModelTitlePlural = 'Widgets'
|
||||||
|
ThisPageData.tableDescription = "Represents a cool widget."
|
||||||
|
ThisPageData.tableVersioned = true
|
||||||
|
|
||||||
|
ThisPageData.tableColumns = [{
|
||||||
|
name: 'uuid',
|
||||||
|
data_type: 'String(length=32)',
|
||||||
|
nullable: false,
|
||||||
|
description: "UUID primary key",
|
||||||
|
versioned: true,
|
||||||
|
}]
|
||||||
|
|
||||||
|
ThisPageData.editingColumnShowDialog = false
|
||||||
|
ThisPageData.editingColumn = null
|
||||||
|
ThisPageData.editingColumnName = null
|
||||||
|
ThisPageData.editingColumnDataType = null
|
||||||
|
ThisPageData.editingColumnNullable = true
|
||||||
|
ThisPageData.editingColumnDescription = null
|
||||||
|
ThisPageData.editingColumnVersioned = true
|
||||||
|
|
||||||
|
ThisPage.methods.tableAddColumn = function() {
|
||||||
|
this.editingColumn = null
|
||||||
|
this.editingColumnName = null
|
||||||
|
this.editingColumnDataType = null
|
||||||
|
this.editingColumnNullable = true
|
||||||
|
this.editingColumnDescription = null
|
||||||
|
this.editingColumnVersioned = true
|
||||||
|
this.editingColumnShowDialog = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.editingColumnName.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.tableEditColumn = function(column) {
|
||||||
|
this.editingColumn = column
|
||||||
|
this.editingColumnName = column.name
|
||||||
|
this.editingColumnDataType = column.data_type
|
||||||
|
this.editingColumnNullable = column.nullable
|
||||||
|
this.editingColumnDescription = column.description
|
||||||
|
this.editingColumnVersioned = column.versioned
|
||||||
|
this.editingColumnShowDialog = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.editingColumnName.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.editingColumnSave = function() {
|
||||||
|
let column
|
||||||
|
if (this.editingColumn) {
|
||||||
|
column = this.editingColumn
|
||||||
|
} else {
|
||||||
|
column = {}
|
||||||
|
this.tableColumns.push(column)
|
||||||
|
}
|
||||||
|
|
||||||
|
column.name = this.editingColumnName
|
||||||
|
column.data_type = this.editingColumnDataType
|
||||||
|
column.nullable = this.editingColumnNullable
|
||||||
|
column.description = this.editingColumnDescription
|
||||||
|
column.versioned = this.editingColumnVersioned
|
||||||
|
|
||||||
|
this.editingColumnShowDialog = false
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.tableDeleteColumn = function(index) {
|
||||||
|
if (confirm("Really delete this column?")) {
|
||||||
|
this.tableColumns.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPageData.tableModelFile = '${model_dir}widget.py'
|
||||||
|
ThisPageData.writingModelFile = false
|
||||||
|
|
||||||
|
ThisPage.methods.writeModelFile = function() {
|
||||||
|
this.writingModelFile = true
|
||||||
|
|
||||||
|
let url = '${url('{}.write_model_file'.format(route_prefix))}'
|
||||||
|
let params = {
|
||||||
|
branch_name: this.tableBranch,
|
||||||
|
table_name: this.tableName,
|
||||||
|
model_name: this.tableModelName,
|
||||||
|
model_title: this.tableModelTitle,
|
||||||
|
model_title_plural: this.tableModelTitlePlural,
|
||||||
|
description: this.tableDescription,
|
||||||
|
versioned: this.tableVersioned,
|
||||||
|
module_file: this.tableModelFile,
|
||||||
|
columns: this.tableColumns,
|
||||||
|
}
|
||||||
|
this.submitForm(url, params, response => {
|
||||||
|
this.writingModelFile = false
|
||||||
|
this.activeStep = 'review-model'
|
||||||
|
}, response => {
|
||||||
|
this.writingModelFile = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -26,6 +26,7 @@ Views with info about the underlying Rattail tables
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
@ -160,65 +161,36 @@ class TableView(MasterView):
|
||||||
def make_form_schema(self):
|
def make_form_schema(self):
|
||||||
return TableSchema()
|
return TableSchema()
|
||||||
|
|
||||||
def configure_form(self, f):
|
def template_kwargs_create(self, **kwargs):
|
||||||
super(TableView, self).configure_form(f)
|
kwargs = super(TableView, self).template_kwargs_create(**kwargs)
|
||||||
app = self.get_rattail_app()
|
app = self.get_rattail_app()
|
||||||
|
model = self.model
|
||||||
|
|
||||||
# exclude some fields when creating
|
kwargs['branch_name_options'] = self.db_handler.get_alembic_branch_names()
|
||||||
if self.creating:
|
|
||||||
f.remove('row_count',
|
|
||||||
'module_name',
|
|
||||||
'module_file')
|
|
||||||
|
|
||||||
# branch_name
|
branch_name = app.get_table_prefix()
|
||||||
if self.creating:
|
if branch_name not in kwargs['branch_name_options']:
|
||||||
|
branch_name = None
|
||||||
|
kwargs['branch_name'] = branch_name
|
||||||
|
|
||||||
# move this field to top of form, as it's more fundamental
|
kwargs['model_dir'] = (os.path.dirname(model.__file__)
|
||||||
# when creating new table
|
+ os.sep)
|
||||||
f.remove('branch_name')
|
|
||||||
f.insert(0, 'branch_name')
|
|
||||||
|
|
||||||
# define options for dropdown
|
return kwargs
|
||||||
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
|
def write_model_file(self):
|
||||||
table_prefix = app.get_table_prefix()
|
data = self.request.json_body
|
||||||
if table_prefix in branches:
|
path = data['module_file']
|
||||||
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 os.path.exists(path):
|
||||||
if self.creating:
|
return {'error': "File already exists"}
|
||||||
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
|
self.db_handler.write_table_model(data, path)
|
||||||
if self.creating:
|
return {'ok': True}
|
||||||
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
|
|
||||||
|
|
||||||
def get_row_data(self, table):
|
def get_row_data(self, table):
|
||||||
data = []
|
data = []
|
||||||
for i, column in enumerate(table['table'].columns, 1):
|
for i, column in enumerate(table['table'].columns, 1):
|
||||||
|
|
||||||
data.append({
|
data.append({
|
||||||
'column': column,
|
'column': column,
|
||||||
'sequence': i,
|
'sequence': i,
|
||||||
|
@ -269,8 +241,26 @@ class TableView(MasterView):
|
||||||
if not rattail_config.production():
|
if not rattail_config.production():
|
||||||
cls.creatable = True
|
cls.creatable = True
|
||||||
|
|
||||||
|
cls._table_defaults(config)
|
||||||
cls._defaults(config)
|
cls._defaults(config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _table_defaults(cls, config):
|
||||||
|
route_prefix = cls.get_route_prefix()
|
||||||
|
url_prefix = cls.get_url_prefix()
|
||||||
|
permission_prefix = cls.get_permission_prefix()
|
||||||
|
|
||||||
|
if cls.creatable:
|
||||||
|
|
||||||
|
# write model class to file
|
||||||
|
config.add_route('{}.write_model_file'.format(route_prefix),
|
||||||
|
'{}/write-model-file'.format(url_prefix),
|
||||||
|
request_method='POST')
|
||||||
|
config.add_view(cls, attr='write_model_file',
|
||||||
|
route_name='{}.write_model_file'.format(route_prefix),
|
||||||
|
renderer='json',
|
||||||
|
permission='{}.create'.format(permission_prefix))
|
||||||
|
|
||||||
|
|
||||||
class TablesView(TableView):
|
class TablesView(TableView):
|
||||||
|
|
||||||
|
@ -303,6 +293,8 @@ class TableSchema(colander.Schema):
|
||||||
module_file = colander.SchemaNode(colander.String(),
|
module_file = colander.SchemaNode(colander.String(),
|
||||||
missing=colander.null)
|
missing=colander.null)
|
||||||
|
|
||||||
|
versioned = colander.SchemaNode(colander.Bool())
|
||||||
|
|
||||||
|
|
||||||
def defaults(config, **kwargs):
|
def defaults(config, **kwargs):
|
||||||
base = globals()
|
base = globals()
|
||||||
|
|
Loading…
Reference in a new issue