Commit 6021a848 authored by Serge S. Koval's avatar Serge S. Koval

Fixed #278. Form rendering rules are now there.

parent c8102977
...@@ -8,6 +8,7 @@ from sqlalchemy.event import listens_for ...@@ -8,6 +8,7 @@ from sqlalchemy.event import listens_for
from jinja2 import Markup from jinja2 import Markup
from flask.ext.admin import Admin, form from flask.ext.admin import Admin, form
from flask.ext.admin.form import rules
from flask.ext.admin.contrib import sqla from flask.ext.admin.contrib import sqla
...@@ -49,6 +50,20 @@ class Image(db.Model): ...@@ -49,6 +50,20 @@ class Image(db.Model):
return self.name return self.name
class RuleSample(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.Unicode(64))
last_name = db.Column(db.Unicode(64))
email = db.Column(db.Unicode(128))
phone = db.Column(db.Unicode(32))
address = db.Column(db.Unicode(128))
city = db.Column(db.Unicode(128))
zip = db.Column(db.Unicode(8))
notes = db.Column(db.UnicodeText)
# Delete hooks for models, delete files if models are getting deleted # Delete hooks for models, delete files if models are getting deleted
@listens_for(File, 'after_delete') @listens_for(File, 'after_delete')
def del_file(mapper, connection, target): def del_file(mapper, connection, target):
...@@ -114,6 +129,27 @@ class ImageView(sqla.ModelView): ...@@ -114,6 +129,27 @@ class ImageView(sqla.ModelView):
} }
class RuleView(sqla.ModelView):
form_create_rules = [
# Header and four fields. Email field will go above phone field.
rules.FieldSet(('first_name', 'last_name', 'email', 'phone'), 'Personal'),
# Separate header and few fields
rules.Header('Address'),
rules.Field('address'),
# String is resolved to form field, so there's no need to explicitly use `rules.Field`
'city',
'zip',
# Show macro from Flask-Admin lib.html (it is included with 'lib' prefix)
rules.Container('rule_demo.wrap', rules.Field('notes'))
]
# Use same rule set for edit page
form_edit_rules = form_create_rules
create_template = 'rule_create.html'
edit_template = 'rule_edit.html'
# Flask views # Flask views
@app.route('/') @app.route('/')
def index(): def index():
...@@ -127,6 +163,7 @@ if __name__ == '__main__': ...@@ -127,6 +163,7 @@ if __name__ == '__main__':
# Add views # Add views
admin.add_view(FileView(File, db.session)) admin.add_view(FileView(File, db.session))
admin.add_view(ImageView(Image, db.session)) admin.add_view(ImageView(Image, db.session))
admin.add_view(RuleView(RuleSample, db.session, name='Rule'))
# Create DB # Create DB
db.create_all() db.create_all()
......
{% extends 'admin/model/create.html' %}
{% import 'rule_demo.html' as rule_demo %}
\ No newline at end of file
{% macro wrap() %}
<div style="border: 1px solid gray; background-color: #f0f0f0; padding-top: 8px; margin-bottom: 8px">
{{ caller() }}
</div>
{% endmacro %}
\ No newline at end of file
{% extends 'admin/model/edit.html' %}
{% import 'rule_demo.html' as rule_demo %}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
{% block body %} {% block body %}
{% call lib.form_tag(form) %} {% call lib.form_tag(form) %}
{{ lib.render_form_fields(form, widget_args=form_widget_args) }} {{ lib.render_form_fields(h.get_render_ctx(), admin_view, form, form_rules=form_rules) }}
<div class="form-buttons"> <div class="form-buttons">
{{ lib.render_form_buttons(return_url, extra()) }} {{ lib.render_form_buttons(return_url, extra()) }}
</div> </div>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
{% block body %} {% block body %}
{% call lib.form_tag(form) %} {% call lib.form_tag(form) %}
{{ lib.render_form_fields(form, widget_args=form_widget_args) }} {{ lib.render_form_fields(h.get_render_ctx(), admin_view, form, form_rules=form_rules) }}
<div class="form-buttons"> <div class="form-buttons">
{{ lib.render_form_buttons(return_url) }} {{ lib.render_form_buttons(return_url) }}
</div> </div>
......
...@@ -32,7 +32,7 @@ class InlineModelFormList(InlineFieldList): ...@@ -32,7 +32,7 @@ class InlineModelFormList(InlineFieldList):
self._pk = get_primary_key(model) self._pk = get_primary_key(model)
super(InlineModelFormList, self).__init__(self.form_field_type(form, self._pk), **kwargs) super(InlineModelFormList, self).__init__(self.form_field_type(form, self._pk, inline_view), **kwargs)
def display_row_controls(self, field): def display_row_controls(self, field):
return field.get_pk() is not None return field.get_pk() is not None
......
...@@ -13,6 +13,13 @@ class BaseForm(form.Form): ...@@ -13,6 +13,13 @@ class BaseForm(form.Form):
super(BaseForm, self).__init__(formdata=formdata, obj=obj, prefix=prefix, **kwargs) super(BaseForm, self).__init__(formdata=formdata, obj=obj, prefix=prefix, **kwargs)
class FormOpts(object):
__slots__ = ['widget_args']
def __init__(self, widget_args):
self.widget_args = widget_args
def recreate_field(unbound): def recreate_field(unbound):
""" """
Create new instance of the unbound field, resetting wtforms creation counter. Create new instance of the unbound field, resetting wtforms creation counter.
...@@ -24,3 +31,13 @@ def recreate_field(unbound): ...@@ -24,3 +31,13 @@ def recreate_field(unbound):
raise ValueError('recreate_field expects UnboundField instance, %s was passed.' % type(unbound)) raise ValueError('recreate_field expects UnboundField instance, %s was passed.' % type(unbound))
return unbound.field_class(*unbound.args, **unbound.kwargs) return unbound.field_class(*unbound.args, **unbound.kwargs)
def get_form_opts(view):
"""
Return form options object from the view.
:param view:
Administrative view or inline model configuration class.
"""
return FormOpts(getattr(view, 'form_widget_args', {}))
from jinja2 import Markup
from flask.ext.admin._compat import string_types
from flask.ext.admin import helpers
class BaseRule(object):
"""
Base form rule. All form formatting rules should derive from `BaseRule`.
"""
def __init__(self):
self.parent = None
self.rule_set = None
def configure(self, rule_set, parent):
"""
Configure rule and assign to rule set.
:param rule_set:
Rule set
:param parent:
Parent rule (if any)
"""
self.parent = parent
self.rule_set = rule_set
return self
def __call__(self, form, form_opts=None, field_args={}):
"""
Render rule.
:param form:
Form object
:param form_opts:
Form options
:param field_args:
Optional arguments that should be passed to template or the field
"""
raise NotImplemented()
class NestedRule(BaseRule):
"""
Nested rule. Can contain child rules and render them.
"""
def __init__(self, rules=[], separator=''):
"""
Constructor.
:param rules:
Rule list
:param separator:
Default separator between rules when rendering them.
"""
super(NestedRule, self).__init__()
self.rules = list(rules)
self.separator = separator
def configure(self, rule_set, parent):
"""
Configure rule.
:param rule_set:
Rule set
:param parent:
Parent rule (if any)
"""
self.rules = rule_set.configure_rules(self.rules, self)
return super(NestedRule, self).configure(rule_set, parent)
def __iter__(self):
"""
Return rules.
"""
return self.rules
def __call__(self, form, form_opts=None, field_args={}):
"""
Render all children.
:param form:
Form object
:param form_opts:
Form options
:param field_args:
Optional arguments that should be passed to template or the field
"""
result = []
for r in self.rules:
result.append(r(form, form_opts, field_args))
return Markup(self.separator.join(result))
class Macro(BaseRule):
"""
Render macro by its name from current Jinja2 context.
"""
def __init__(self, macro_name, **kwargs):
"""
Constructor.
:param macro_name:
Macro name
:param kwargs:
Default macro parameters
"""
super(Macro, self).__init__()
self.macro_name = macro_name
self.default_args = kwargs
def _resolve(self, context, name):
"""
Resolve macro in a Jinja2 context
:param context:
Jinja2 context
:param name:
Macro name. May be full path (with dots)
"""
parts = name.split('.')
field = context.resolve(parts[0])
if not field:
return None
for p in parts[1:]:
field = getattr(field, p, None)
if not field:
return field
return field
def __call__(self, form, form_opts=None, field_args={}):
"""
Render macro rule.
:param form:
Form object
:param form_opts:
Form options
:param field_args:
Optional arguments that should be passed to the macro
"""
context = helpers.get_render_ctx()
macro = self._resolve(context, self.macro_name)
if not macro:
raise ValueError('Cannot find macro %s in current context.' % self.macro_name)
opts = dict(self.default_args)
opts.update(field_args)
return macro(**opts)
class Container(Macro):
"""
Render container around child rule.
"""
def __init__(self, macro_name, child_rule, **kwargs):
"""
Constructor.
:param macro_name:
Macro name that will be used as a container
:param child_rule:
Child rule to be rendered inside of container
:param kwargs:
Container macro arguments
"""
super(Container, self).__init__(macro_name, **kwargs)
self.child_rule = child_rule
def configure(self, rule_set, parent):
"""
Configure rule.
:param rule_set:
Rule set
:param parent:
Parent rule (if any)
"""
self.child_rule.configure(rule_set, self)
return super(Container, self).configure(rule_set, parent)
def __call__(self, form, form_opts=None, field_args={}):
"""
Render container.
:param form:
Form object
:param form_opts:
Form options
:param field_args:
Optional arguments that should be passed to template or the field
"""
context = helpers.get_render_ctx()
def caller(**kwargs):
return context.call(self.child_rule, form, form_opts, kwargs)
args = dict(field_args)
args['caller'] = caller
return super(Container, self).__call__(form, form_opts, args)
class Field(Macro):
"""
Form field rule.
"""
def __init__(self, field_name, render_field='lib.render_field'):
"""
Constructor.
:param field_name:
Field name to render
:param render_field:
Macro that will be used to render the field.
"""
super(Field, self).__init__(render_field)
self.field_name = field_name
def __call__(self, form, form_opts=None, field_args={}):
"""
Render field.
:param form:
Form object
:param form_opts:
Form options
:param field_args:
Optional arguments that should be passed to template or the field
"""
field = getattr(form, self.field_name, None)
if not field:
raise ValueError('Form %s does not have field %s' % (form, form_opts, self.field_name))
opts = {}
if form_opts:
opts.update(form_opts.widget_args.get(self.field_name, {}))
opts.update(field_args)
params = {
'form': form,
'field': field,
'kwargs': opts
}
return super(Field, self).__call__(form, form_opts, params)
class Header(Macro):
"""
Render header text.
"""
def __init__(self, text, header_macro='lib.render_header'):
"""
Constructor.
:param text:
Text to render
:param header_macro:
Header rendering macro
"""
super(Header, self).__init__(header_macro, text=text)
class FieldSet(NestedRule):
"""
Field set with header.
"""
def __init__(self, rules, header=None, separator=''):
"""
Constructor.
:param rules:
Child rules
:param header:
Header text
:param separator:
Child rule separator
"""
if header:
rule_set = [Header(header)] + list(rules)
else:
rule_set = list(rules)
super(FieldSet, self).__init__(rule_set, separator=separator)
class RuleSet(object):
"""
Rule set.
"""
def __init__(self, view, rules):
"""
Constructor.
:param view:
Administrative view
:param rules:
Rule list
"""
self.view = view
self.rules = self.configure_rules(rules)
def convert_string(self, value):
"""
Convert string to rule.
Override this method to change default behavior.
"""
return Field(value)
def configure_rules(self, rules, parent=None):
"""
Configure all rules recursively - bind them to current RuleSet and
convert string references to `Field` rules.
:param rules:
Rule list
:param parent:
Parent rule (if any)
"""
result = []
for r in rules:
if isinstance(r, BaseRule):
result.append(r.configure(self, parent))
elif isinstance(r, string_types):
result.append(self.convert_string(r).configure(self, parent))
else:
raise ValueError('Dont know how to convert %s' % repr(r))
return result
def __iter__(self):
"""
Iterate through registered rules.
"""
for r in self.rules:
yield r
from jinja2 import contextfunction
from flask import g, request from flask import g, request
from wtforms.validators import DataRequired, InputRequired from wtforms.validators import DataRequired, InputRequired
...@@ -69,3 +70,18 @@ def is_field_error(errors): ...@@ -69,3 +70,18 @@ def is_field_error(errors):
return True return True
return False return False
@contextfunction
def resolve_ctx(context):
"""
Resolve current Jinja2 context and store it for general consumption.
"""
g._admin_render_ctx = context
def get_render_ctx():
"""
Get view template context.
"""
return getattr(g, '_admin_render_ctx', None)
...@@ -7,7 +7,7 @@ from jinja2 import contextfunction ...@@ -7,7 +7,7 @@ from jinja2 import contextfunction
from flask.ext.admin.babel import gettext from flask.ext.admin.babel import gettext
from flask.ext.admin.base import BaseView, expose from flask.ext.admin.base import BaseView, expose
from flask.ext.admin.form import BaseForm from flask.ext.admin.form import BaseForm, rules, get_form_opts
from flask.ext.admin.model import filters, typefmt from flask.ext.admin.model import filters, typefmt
from flask.ext.admin.actions import ActionsMixin from flask.ext.admin.actions import ActionsMixin
from flask.ext.admin.helpers import get_form_data, validate_form_on_submit from flask.ext.admin.helpers import get_form_data, validate_form_on_submit
...@@ -403,6 +403,39 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -403,6 +403,39 @@ class BaseModelView(BaseView, ActionsMixin):
in your `AjaxModelLoader` class. in your `AjaxModelLoader` class.
""" """
form_create_rules = None
"""
List of rendering rules for model creation form.
This property changed default form rendering behavior and makes possible to rearrange order
of rendered fields, add some text between fields, group them, etc. If not set, will use
default Flask-Admin form rendering logic.
Here's simple example which illustrates how to use::
from flask.ext.admin.form import rules
class MyModelView(ModelView):
form_rules = [
# Define field set with header text and four fields
rules.FieldSet('User', ('first_name', 'last_name', 'email', 'phone')),
# ... and it is just shortcut for:
rules.Header('User'),
rules.Field('first_name'),
rules.Field('last_name'),
# ...
# It is possible to create custom rule blocks:
MyBlock('Hello World'),
# It is possible to call macros from current context
rules.Macro('my_macro', foobar='baz')
]
"""
form_edit_rules = None
"""
Same as `form_create_rules`, just for model edit form.
"""
# Actions # Actions
action_disallowed_list = ObsoleteAttr('action_disallowed_list', action_disallowed_list = ObsoleteAttr('action_disallowed_list',
'disallowed_actions', 'disallowed_actions',
...@@ -526,6 +559,17 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -526,6 +559,17 @@ class BaseModelView(BaseView, ActionsMixin):
self._filter_groups = None self._filter_groups = None
self._filter_types = None self._filter_types = None
# Form rendering rules
if self.form_create_rules:
self._form_create_rules = rules.RuleSet(self, self.form_create_rules)
else:
self._form_create_rules = None
if self.form_edit_rules:
self._form_edit_rules = rules.RuleSet(self, self.form_edit_rules)
else:
self._form_edit_rules = None
# Primary key # Primary key
def get_pk_value(self, model): def get_pk_value(self, model):
""" """
...@@ -1159,7 +1203,8 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -1159,7 +1203,8 @@ class BaseModelView(BaseView, ActionsMixin):
return self.render(self.create_template, return self.render(self.create_template,
form=form, form=form,
form_widget_args=self.form_widget_args, form_opts=get_form_opts(self),
form_rules=self._form_create_rules,
return_url=return_url) return_url=return_url)
@expose('/edit/', methods=('GET', 'POST')) @expose('/edit/', methods=('GET', 'POST'))
...@@ -1194,7 +1239,8 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -1194,7 +1239,8 @@ class BaseModelView(BaseView, ActionsMixin):
return self.render(self.edit_template, return self.render(self.edit_template,
model=model, model=model,
form=form, form=form,
form_widget_args=self.form_widget_args, form_opts=get_form_opts(self),
form_rules=self._form_edit_rules,
return_url=return_url) return_url=return_url)
@expose('/delete/', methods=('POST',)) @expose('/delete/', methods=('POST',))
......
...@@ -86,6 +86,13 @@ class InlineFieldList(FieldList): ...@@ -86,6 +86,13 @@ class InlineFieldList(FieldList):
setattr(obj, name, output) setattr(obj, name, output)
class InlineFormField(FormField):
"""
Inline version of the ``FormField`` widget.
"""
widget = InlineFormWidget()
class InlineModelFormField(FormField): class InlineModelFormField(FormField):
""" """
Customized ``FormField``. Customized ``FormField``.
...@@ -95,8 +102,8 @@ class InlineModelFormField(FormField): ...@@ -95,8 +102,8 @@ class InlineModelFormField(FormField):
""" """
widget = InlineFormWidget() widget = InlineFormWidget()
def __init__(self, form, pk, **kwargs): def __init__(self, form_class, pk, **kwargs):
super(InlineModelFormField, self).__init__(form, **kwargs) super(InlineModelFormField, self).__init__(form_class, **kwargs)
self._pk = pk self._pk = pk
...@@ -109,13 +116,6 @@ class InlineModelFormField(FormField): ...@@ -109,13 +116,6 @@ class InlineModelFormField(FormField):
field.populate_obj(obj, name) field.populate_obj(obj, name)
class InlineFormField(FormField):
"""
Inline version of the ``FormField`` widget.
"""
widget = InlineFormWidget()
class AjaxSelectField(SelectFieldBase): class AjaxSelectField(SelectFieldBase):
""" """
Ajax Model Select Field Ajax Model Select Field
......
...@@ -15,6 +15,9 @@ class InlineFormWidget(RenderTemplateWidget): ...@@ -15,6 +15,9 @@ class InlineFormWidget(RenderTemplateWidget):
def __init__(self): def __init__(self):
super(InlineFormWidget, self).__init__('admin/model/inline_form.html') super(InlineFormWidget, self).__init__('admin/model/inline_form.html')
def __call__(self, field, **kwargs):
return super(InlineFormWidget, self).__call__(field, **kwargs)
class AjaxSelect2Widget(object): class AjaxSelect2Widget(object):
def __init__(self, multiple=False): def __init__(self, multiple=False):
......
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
{{ layout.messages() }} {{ layout.messages() }}
{% set render_ctx = h.resolve_ctx() %}
{% block body %}{% endblock %} {% block body %}{% endblock %}
</div> </div>
{% endblock %} {% endblock %}
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
{%- endmacro %} {%- endmacro %}
{# ---------------------- Forms -------------------------- #} {# ---------------------- Forms -------------------------- #}
{% macro render_field(form, field, set_focus=False, kwargs={}) %} {% macro render_field(form, field, kwargs={}) %}
{% set direct_error = h.is_field_error(field.errors) %} {% set direct_error = h.is_field_error(field.errors) %}
<div class="control-group{{ ' error' if direct_error else '' }}"> <div class="control-group{{ ' error' if direct_error else '' }}">
<div class="control-label"> <div class="control-label">
...@@ -88,11 +88,7 @@ ...@@ -88,11 +88,7 @@
</div> </div>
<div class="controls"> <div class="controls">
<div> <div>
{% if set_focus %}
{{ field(autofocus='autofocus', **kwargs)|safe }}
{% else %}
{{ field(**kwargs)|safe }} {{ field(**kwargs)|safe }}
{% endif %}
</div> </div>
{% if field.description %} {% if field.description %}
<p class="help-block">{{ field.description }}</p> <p class="help-block">{{ field.description }}</p>
...@@ -108,7 +104,11 @@ ...@@ -108,7 +104,11 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro render_form_fields(form, set_focus=True, widget_args={}) %} {% macro render_header(form, text) %}
<h3>{{ text }}</h3>
{% endmacro %}
{% macro render_form_fields(form, form_opts={}, form_rules=None) %}
{% if form.hidden_tag is defined %} {% if form.hidden_tag is defined %}
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{% else %} {% else %}
...@@ -117,10 +117,20 @@ ...@@ -117,10 +117,20 @@
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% for f in form if f.type != 'HiddenField' and f.type != 'CSRFTokenField' %} {% if form_rules %}
{% set kwargs = widget_args.get(f.name, {}) %} {% for r in form_rules %}
{{ render_field(form, f, not loop.index0 and set_focus, kwargs) }} {{ r(form, form_opts=form_opts) }}
{% endfor %} {% endfor %}
{% else %}
{% for f in form if f.type != 'HiddenField' and f.type != 'CSRFTokenField' %}
{% if form_opts %}
{% set kwargs = form_opts.widget_args.get(f.name, {}) %}
{% else %}
{% set kwargs = {} %}
{% endif %}
{{ render_field(form, f, kwargs) }}
{% endfor %}
{% endif %}
{% endmacro %} {% endmacro %}
{% macro form_tag(form=None) %} {% macro form_tag(form=None) %}
...@@ -145,9 +155,9 @@ ...@@ -145,9 +155,9 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro render_form(form, cancel_url, extra=None, widget_args={}) -%} {% macro render_form(form, cancel_url, extra=None, form_opts={}, form_rules=None) -%}
{% call form_tag() %} {% call form_tag() %}
{{ render_form_fields(form, widget_args=widget_args) }} {{ render_form_fields(form, form_opts=form_opts, form_rules=form_rules) }}
{{ render_form_buttons(cancel_url, extra) }} {{ render_form_buttons(cancel_url, extra) }}
{% endcall %} {% endcall %}
{% endmacro %} {% endmacro %}
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
</ul> </ul>
{% call lib.form_tag(form) %} {% call lib.form_tag(form) %}
{{ lib.render_form_fields(form, widget_args=form_widget_args) }} {{ lib.render_form_fields(form, form_opts=form_opts, form_rules=form_rules) }}
{{ lib.render_form_buttons(return_url, extra()) }} {{ lib.render_form_buttons(return_url, extra()) }}
{% endcall %} {% endcall %}
{% endblock %} {% endblock %}
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
{% block body %} {% block body %}
{% call lib.form_tag(form) %} {% call lib.form_tag(form) %}
{{ lib.render_form_fields(form, widget_args=form_widget_args) }} {{ lib.render_form_fields(form, form_opts=form_opts, form_rules=form_rules) }}
{{ lib.render_form_buttons(return_url, extra()) }} {{ lib.render_form_buttons(return_url, extra()) }}
{% endcall %} {% endcall %}
{% endblock %} {% endblock %}
......
{% import 'admin/lib.html' as lib with context %} {% import 'admin/lib.html' as lib with context %}
<div class="fa-inline-field"> <div class="fa-inline-field">
{{ lib.render_form_fields(field, False) }} {{ lib.render_form_fields(field) }}
</div> </div>
{% macro another_test() %}
Hello another_test
{% endmacro %}
{% extends 'admin/model/create.html' %}
{% import 'another_macro.html' as test_lib %}
{% macro test(arg) %}
Value = {{ arg }}
{% endmacro %}
{% macro wrap() %}
<wrapper>
{{ caller() }}
</wrapper>
{% endmacro %}
from nose.tools import eq_, ok_, raises
from . import setup
from .test_basic import CustomModelView, create_models
from flask.ext.admin.form import rules
def test_form_rules():
app, db, admin = setup()
Model1, _ = create_models(db)
db.create_all()
view = CustomModelView(Model1, db.session,
form_create_rules=('test2', 'test1', rules.Field('test4')))
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1view/new/')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
pos1 = data.find('Test1')
pos2 = data.find('Test2')
pos3 = data.find('Test3')
pos4 = data.find('Test4')
ok_(pos1 > pos2)
ok_(pos4 > pos1)
ok_(pos3 == -1)
def test_rule_macro():
app, db, admin = setup()
Model1, _ = create_models(db)
db.create_all()
view = CustomModelView(Model1, db.session,
create_template='macro.html',
form_create_rules=(rules.Macro('test', arg='foobar'),
rules.Macro('test_lib.another_test')))
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1view/new/')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('Value = foobar' in data)
ok_('Hello another_test' in data)
def test_rule_container():
app, db, admin = setup()
Model1, _ = create_models(db)
db.create_all()
view = CustomModelView(Model1, db.session,
create_template='macro.html',
form_create_rules=(rules.Container('wrap', rules.Macro('test_lib.another_test')),))
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1view/new/')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
pos1 = data.find('<wrapper>')
pos2 = data.find('another_test')
pos3 = data.find('</wrapper>')
ok_(pos1 != -1)
ok_(pos2 != -1)
ok_(pos3 != -1)
ok_(pos1 < pos2 < pos3)
def test_rule_header():
app, db, admin = setup()
Model1, _ = create_models(db)
db.create_all()
view = CustomModelView(Model1, db.session,
form_create_rules=(rules.Header('hello'),))
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1view/new/')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('<h3>hello</h3>' in data)
def test_rule_field_set():
app, db, admin = setup()
Model1, _ = create_models(db)
db.create_all()
view = CustomModelView(Model1, db.session,
form_create_rules=(rules.FieldSet(['test2', 'test1', 'test4'], 'header'),))
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1view/new/')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('<h3>header</h3>' in data)
pos1 = data.find('Test1')
pos2 = data.find('Test2')
pos3 = data.find('Test3')
pos4 = data.find('Test4')
ok_(pos1 > pos2)
ok_(pos4 > pos1)
ok_(pos3 == -1)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment