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>
|
||||
</%def>
|
||||
|
||||
<%def name="render_buefy_form()">
|
||||
<%def name="render_this_page()">
|
||||
<b-steps v-model="activeStep"
|
||||
:animated="false"
|
||||
rounded
|
||||
|
@ -25,26 +25,257 @@
|
|||
<h3 class="is-size-3 block">
|
||||
Enter Details
|
||||
</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 step="2"
|
||||
value="write-model"
|
||||
label="Write Model"
|
||||
clickable>
|
||||
label="Write Model">
|
||||
<h3 class="is-size-3 block">
|
||||
Write Model
|
||||
</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">
|
||||
<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"
|
||||
|
@ -54,8 +285,9 @@
|
|||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
icon-left="save"
|
||||
@click="activeStep = 'review-model'">
|
||||
Write model class to file
|
||||
@click="writeModelFile()"
|
||||
:disabled="writingModelFile">
|
||||
{{ writingModelFile ? "Working, please wait..." : "Write model class to file" }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -206,6 +438,107 @@
|
|||
<script type="text/javascript">
|
||||
|
||||
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>
|
||||
</%def>
|
||||
|
|
|
@ -26,6 +26,7 @@ Views with info about the underlying Rattail tables
|
|||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
|
@ -160,65 +161,36 @@ class TableView(MasterView):
|
|||
def make_form_schema(self):
|
||||
return TableSchema()
|
||||
|
||||
def configure_form(self, f):
|
||||
super(TableView, self).configure_form(f)
|
||||
def template_kwargs_create(self, **kwargs):
|
||||
kwargs = super(TableView, self).template_kwargs_create(**kwargs)
|
||||
app = self.get_rattail_app()
|
||||
model = self.model
|
||||
|
||||
# exclude some fields when creating
|
||||
if self.creating:
|
||||
f.remove('row_count',
|
||||
'module_name',
|
||||
'module_file')
|
||||
kwargs['branch_name_options'] = self.db_handler.get_alembic_branch_names()
|
||||
|
||||
# branch_name
|
||||
if self.creating:
|
||||
branch_name = app.get_table_prefix()
|
||||
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
|
||||
# when creating new table
|
||||
f.remove('branch_name')
|
||||
f.insert(0, 'branch_name')
|
||||
kwargs['model_dir'] = (os.path.dirname(model.__file__)
|
||||
+ os.sep)
|
||||
|
||||
# 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))
|
||||
return kwargs
|
||||
|
||||
# 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.")
|
||||
def write_model_file(self):
|
||||
data = self.request.json_body
|
||||
path = data['module_file']
|
||||
|
||||
# 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'")
|
||||
if os.path.exists(path):
|
||||
return {'error': "File already exists"}
|
||||
|
||||
# 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
|
||||
self.db_handler.write_table_model(data, path)
|
||||
return {'ok': True}
|
||||
|
||||
def get_row_data(self, table):
|
||||
data = []
|
||||
for i, column in enumerate(table['table'].columns, 1):
|
||||
|
||||
data.append({
|
||||
'column': column,
|
||||
'sequence': i,
|
||||
|
@ -269,8 +241,26 @@ class TableView(MasterView):
|
|||
if not rattail_config.production():
|
||||
cls.creatable = True
|
||||
|
||||
cls._table_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):
|
||||
|
||||
|
@ -303,6 +293,8 @@ class TableSchema(colander.Schema):
|
|||
module_file = colander.SchemaNode(colander.String(),
|
||||
missing=colander.null)
|
||||
|
||||
versioned = colander.SchemaNode(colander.Bool())
|
||||
|
||||
|
||||
def defaults(config, **kwargs):
|
||||
base = globals()
|
||||
|
|
Loading…
Reference in a new issue