Node type editing, with FormField and FormList
This allows adding and removing custom fields from node types. Currently does not work if the Node Type does not have any initial custom field.
This commit is contained in:
parent
2ac2917645
commit
2badde4ff4
@ -11,6 +11,7 @@ from application import db
|
|||||||
|
|
||||||
from application.modules.nodes.models import Node, NodeType
|
from application.modules.nodes.models import Node, NodeType
|
||||||
from application.modules.nodes.forms import NodeTypeForm
|
from application.modules.nodes.forms import NodeTypeForm
|
||||||
|
from application.modules.nodes.forms import CustomFieldForm
|
||||||
from application.modules.nodes.forms import get_node_form
|
from application.modules.nodes.forms import get_node_form
|
||||||
from application.modules.nodes.forms import process_node_form
|
from application.modules.nodes.forms import process_node_form
|
||||||
|
|
||||||
@ -65,6 +66,41 @@ def add():
|
|||||||
return render_template('node_types/add.html', form=form)
|
return render_template('node_types/add.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@node_types.route("/<int:node_type_id>/edit", methods=['GET', 'POST'])
|
||||||
|
def edit(node_type_id):
|
||||||
|
node_type = NodeType.query.get_or_404(node_type_id)
|
||||||
|
|
||||||
|
form = NodeTypeForm(obj=node_type)
|
||||||
|
|
||||||
|
if form.validate_on_submit():
|
||||||
|
node_type.name = form.name.data
|
||||||
|
node_type.description = form.description.data
|
||||||
|
node_type.url = form.url.data
|
||||||
|
# Processing custom fields
|
||||||
|
for field in form.custom_fields:
|
||||||
|
print field.data['id']
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
else:
|
||||||
|
print form.errors
|
||||||
|
|
||||||
|
|
||||||
|
# if form.validate_on_submit():
|
||||||
|
# node_type = NodeType(
|
||||||
|
# name=form.name.data,
|
||||||
|
# description=form.description.data,
|
||||||
|
# url=form.url.data)
|
||||||
|
|
||||||
|
# db.session.add(node_type)
|
||||||
|
# db.session.commit()
|
||||||
|
|
||||||
|
# return redirect(url_for('node_types.index'))
|
||||||
|
return render_template('node_types/edit.html',
|
||||||
|
node_type=node_type,
|
||||||
|
form=form)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@nodes.route("/", methods=['GET', 'POST'])
|
@nodes.route("/", methods=['GET', 'POST'])
|
||||||
def index():
|
def index():
|
||||||
"""Generic function to list all nodes
|
"""Generic function to list all nodes
|
||||||
|
@ -5,6 +5,9 @@ from wtforms import SelectField
|
|||||||
from wtforms import TextAreaField
|
from wtforms import TextAreaField
|
||||||
from wtforms import IntegerField
|
from wtforms import IntegerField
|
||||||
from wtforms import HiddenField
|
from wtforms import HiddenField
|
||||||
|
from wtforms import FieldList
|
||||||
|
from wtforms import FormField
|
||||||
|
from wtforms import Form as BasicForm
|
||||||
|
|
||||||
from application.modules.nodes.models import CustomFields
|
from application.modules.nodes.models import CustomFields
|
||||||
from wtforms.validators import DataRequired
|
from wtforms.validators import DataRequired
|
||||||
@ -13,19 +16,51 @@ from application import db
|
|||||||
|
|
||||||
from application.modules.nodes.models import Node, NodeType, NodeProperties
|
from application.modules.nodes.models import Node, NodeType, NodeProperties
|
||||||
|
|
||||||
class CustomFieldForm(Form):
|
class CustomFieldForm(BasicForm):
|
||||||
|
id = HiddenField()
|
||||||
field_type = TextField('Field Type', validators=[DataRequired()])
|
field_type = TextField('Field Type', validators=[DataRequired()])
|
||||||
name = TextField('Name', validators=[DataRequired()])
|
name = TextField('Name', validators=[DataRequired()])
|
||||||
name_url = TextField('Url', validators=[DataRequired()])
|
name_url = TextField('Url', validators=[DataRequired()])
|
||||||
description = TextAreaField('Description', validators=[DataRequired()])
|
description = TextAreaField('Description')
|
||||||
is_required = BooleanField('Is extended')
|
is_required = BooleanField('Is extended')
|
||||||
|
def __init__(self, csrf_enabled=False, *args, **kwargs):
|
||||||
|
super(CustomFieldForm, self).__init__(csrf_enabled=False, *args, **kwargs)
|
||||||
|
|
||||||
|
class ModelFieldList(FieldList):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.model = kwargs.pop("model", None)
|
||||||
|
super(ModelFieldList, self).__init__(*args, **kwargs)
|
||||||
|
if not self.model:
|
||||||
|
raise ValueError("ModelFieldList requires model to be set")
|
||||||
|
|
||||||
|
def populate_obj(self, obj, name):
|
||||||
|
while len(getattr(obj, name)) < len(self.entries):
|
||||||
|
newModel = self.model()
|
||||||
|
db.session.add(newModel)
|
||||||
|
getattr(obj, name).append(newModel)
|
||||||
|
while len(getattr(obj, name)) > len(self.entries):
|
||||||
|
db.session.delete(getattr(obj, name).pop())
|
||||||
|
super(ModelFieldList, self).populate_obj(obj, name)
|
||||||
|
|
||||||
|
class ChildInline(Form):
|
||||||
|
title = TextField('Title',)
|
||||||
|
|
||||||
class NodeTypeForm(Form):
|
class NodeTypeForm(Form):
|
||||||
name = TextField('Name', validators=[DataRequired()])
|
name = TextField('Name', validators=[DataRequired()])
|
||||||
url = TextField('Url', validators=[DataRequired()])
|
url = TextField('Url', validators=[DataRequired()])
|
||||||
description = TextAreaField('Description', validators=[DataRequired()])
|
description = TextAreaField('Description', validators=[DataRequired()])
|
||||||
is_extended = BooleanField('Is extended')
|
is_extended = BooleanField('Is extended')
|
||||||
|
custom_fields = ModelFieldList(FormField(CustomFieldForm), model=CustomFields)
|
||||||
|
|
||||||
|
|
||||||
|
class IMForm(Form):
|
||||||
|
protocol = SelectField(choices=[('aim', 'AIM'), ('msn', 'MSN')])
|
||||||
|
username = TextField()
|
||||||
|
|
||||||
|
class ContactForm(Form):
|
||||||
|
first_name = TextField()
|
||||||
|
last_name = TextField()
|
||||||
|
im_accounts = FieldList(BooleanField('Is extended'),)
|
||||||
|
|
||||||
|
|
||||||
def get_node_form(node_type):
|
def get_node_form(node_type):
|
||||||
|
@ -37,6 +37,9 @@ class NodeType(db.Model):
|
|||||||
description = db.Column(db.Text)
|
description = db.Column(db.Text)
|
||||||
url = db.Column(db.String(120), nullable=False)
|
url = db.Column(db.String(120), nullable=False)
|
||||||
|
|
||||||
|
custom_fields = db.relationship('CustomFields', backref='NodeType',
|
||||||
|
cascade="all, delete, delete-orphan")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@ -74,7 +77,6 @@ class Node(db.Model):
|
|||||||
class CustomFields(db.Model):
|
class CustomFields(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key = True)
|
id = db.Column(db.Integer, primary_key = True)
|
||||||
node_type_id = db.Column(db.Integer(), db.ForeignKey(NodeType.id))
|
node_type_id = db.Column(db.Integer(), db.ForeignKey(NodeType.id))
|
||||||
node_type = db.relationship(NodeType, backref='CustomField')
|
|
||||||
|
|
||||||
field_type = db.Column(db.String(128))
|
field_type = db.Column(db.String(128))
|
||||||
order = db.Column(db.Integer())
|
order = db.Column(db.Integer())
|
||||||
|
@ -94,7 +94,8 @@
|
|||||||
"assets/js/jquery.attract.js" %}
|
"assets/js/jquery.attract.js" %}
|
||||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
{% endassets %}
|
{% endassets %}
|
||||||
|
{% block footer_scripts %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -23,9 +23,74 @@
|
|||||||
{{ form.is_extended.label }}
|
{{ form.is_extended.label }}
|
||||||
{{ form.is_extended(class='form-control') }}
|
{{ form.is_extended(class='form-control') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-toggle="fieldset" id="custom_field-fieldset">
|
||||||
|
{{ form.custom_fields.label }} <button type="button" id="add_another_button">+</button>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Url</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Is required</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
{% for custom_field in form.custom_fields %}
|
||||||
|
<tr data-toggle="fieldset-entry">
|
||||||
|
|
||||||
|
{% for field in custom_field %}
|
||||||
|
{% if field.type == 'HiddenField' %}
|
||||||
|
{{field}}
|
||||||
|
{% else %}
|
||||||
|
<td>{{field}}</td>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<td><button type="button" data-toggle="fieldset-remove-row" id="custom_fields-{{loop.index0}}-remove">-</button></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input class="btn btn-default" type="submit" value="Add Node Type">
|
<input class="btn btn-default" type="submit" value="Add Node Type">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block footer_scripts %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("div[data-toggle=fieldset]").each(function() {
|
||||||
|
var $this = $(this);
|
||||||
|
|
||||||
|
//Add new entry
|
||||||
|
$this.find("button[data-toggle=fieldset-add-row]").click(function() {
|
||||||
|
var target = $($(this).data("target"))
|
||||||
|
console.log(target);
|
||||||
|
var oldrow = target.find("tr[data-toggle=fieldset-entry]:last");
|
||||||
|
var row = oldrow.clone(true, true);
|
||||||
|
console.log(row.find(":input")[0]);
|
||||||
|
var elem_id = row.find(":input")[0].id;
|
||||||
|
var elem_num = parseInt(elem_id.replace(/.*-(\d{1,4})-.*/m, '$1')) + 1;
|
||||||
|
row.attr('data-id', elem_num);
|
||||||
|
row.find(":input").each(function() {
|
||||||
|
console.log(this);
|
||||||
|
var id = $(this).attr('id').replace('-' + (elem_num - 1) + '-', '-' + (elem_num) + '-');
|
||||||
|
$(this).attr('name', id).attr('id', id).val('').removeAttr("checked");
|
||||||
|
});
|
||||||
|
oldrow.after(row);
|
||||||
|
}); //End add new entry
|
||||||
|
|
||||||
|
//Remove row
|
||||||
|
$this.find("button[data-toggle=fieldset-remove-row]").click(function() {
|
||||||
|
if($this.find("tr[data-toggle=fieldset-entry]").length > 1) {
|
||||||
|
var thisRow = $(this).closest("tr[data-toggle=fieldset-entry]");
|
||||||
|
thisRow.remove();
|
||||||
|
}
|
||||||
|
}); //End remove row
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
100
attract/application/templates/node_types/edit.html
Normal file
100
attract/application/templates/node_types/edit.html
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
{% extends 'layout.html' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="col-md-9">
|
||||||
|
<h2>Edit Node type</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<form method="POST" action="{{url_for('node_types.edit', node_type_id=node_type.id)}}">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.name.label }}
|
||||||
|
{{ form.name(size=20, class='form-control') }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.description.label }}
|
||||||
|
{{ form.description(size=20, class='form-control') }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.url.label }}
|
||||||
|
{{ form.url(size=20, class='form-control') }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.is_extended.label }}
|
||||||
|
{{ form.is_extended(class='form-control') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-toggle="fieldset" id="custom_fields-fieldset">
|
||||||
|
{{ form.custom_fields.label }} <button type="button" data-toggle="fieldset-add-row" data-target="#custom_fields-fieldset">+</button>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Url</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Is required</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% for custom_field in form.custom_fields %}
|
||||||
|
<tr data-toggle="fieldset-entry">
|
||||||
|
|
||||||
|
{% for field in custom_field %}
|
||||||
|
{% if field.type == 'HiddenField' %}
|
||||||
|
{{field}}
|
||||||
|
{% else %}
|
||||||
|
<td>{{field}}</td>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<td><button type="button" data-toggle="fieldset-remove-row" id="custom_fields-{{loop.index0}}-remove">-</button></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input class="btn btn-default" type="submit" value="Edit Node Type">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block footer_scripts %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("div[data-toggle=fieldset]").each(function() {
|
||||||
|
var $this = $(this);
|
||||||
|
|
||||||
|
//Add new entry
|
||||||
|
$this.find("button[data-toggle=fieldset-add-row]").click(function() {
|
||||||
|
var target = $($(this).data("target"))
|
||||||
|
console.log(target);
|
||||||
|
var oldrow = target.find("tr[data-toggle=fieldset-entry]:last");
|
||||||
|
var row = oldrow.clone(true, true);
|
||||||
|
console.log(row.find(":input")[0]);
|
||||||
|
var elem_id = row.find(":input")[0].id;
|
||||||
|
var elem_num = parseInt(elem_id.replace(/.*-(\d{1,4})-.*/m, '$1')) + 1;
|
||||||
|
row.attr('data-id', elem_num);
|
||||||
|
row.find(":input").each(function() {
|
||||||
|
console.log(this);
|
||||||
|
var id = $(this).attr('id').replace('-' + (elem_num - 1) + '-', '-' + (elem_num) + '-');
|
||||||
|
$(this).attr('name', id).attr('id', id).val('').removeAttr("checked");
|
||||||
|
});
|
||||||
|
oldrow.after(row);
|
||||||
|
}); //End add new entry
|
||||||
|
|
||||||
|
//Remove row
|
||||||
|
$this.find("button[data-toggle=fieldset-remove-row]").click(function() {
|
||||||
|
if($this.find("tr[data-toggle=fieldset-entry]").length > 1) {
|
||||||
|
var thisRow = $(this).closest("tr[data-toggle=fieldset-entry]");
|
||||||
|
thisRow.remove();
|
||||||
|
}
|
||||||
|
}); //End remove row
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -24,7 +24,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a class="btn btn-default btn-xs" href="#"><i class="glyphicon glyphicon-edit"></i> Edit</a>
|
<a class="btn btn-default btn-xs" href="{{url_for('node_types.edit', node_type_id=node_type.id)}}"><i class="glyphicon glyphicon-edit"></i> Edit</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user