Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
6021a848
Commit
6021a848
authored
Oct 14, 2013
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed #278. Form rendering rules are now there.
parent
c8102977
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
659 additions
and
30 deletions
+659
-30
simple.py
examples/forms/simple.py
+37
-0
rule_create.html
examples/forms/templates/rule_create.html
+2
-0
rule_demo.html
examples/forms/templates/rule_demo.html
+5
-0
rule_edit.html
examples/forms/templates/rule_edit.html
+2
-0
create.html
examples/layout/templates/create.html
+1
-1
edit.html
examples/layout/templates/edit.html
+1
-1
form.py
flask_admin/contrib/peewee/form.py
+1
-1
__init__.py
flask_admin/form/__init__.py
+17
-0
rules.py
flask_admin/form/rules.py
+350
-0
helpers.py
flask_admin/helpers.py
+16
-0
base.py
flask_admin/model/base.py
+49
-3
fields.py
flask_admin/model/fields.py
+9
-9
widgets.py
flask_admin/model/widgets.py
+3
-0
base.html
flask_admin/templates/admin/base.html
+2
-0
lib.html
flask_admin/templates/admin/lib.html
+22
-12
create.html
flask_admin/templates/admin/model/create.html
+1
-1
edit.html
flask_admin/templates/admin/model/edit.html
+1
-1
inline_form.html
flask_admin/templates/admin/model/inline_form.html
+1
-1
another_macro.html
flask_admin/tests/sqlamodel/templates/another_macro.html
+3
-0
macro.html
flask_admin/tests/sqlamodel/templates/macro.html
+12
-0
test_form_rules.py
flask_admin/tests/sqlamodel/test_form_rules.py
+124
-0
No files found.
examples/forms/simple.py
View file @
6021a848
...
@@ -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
()
...
...
examples/forms/templates/rule_create.html
0 → 100644
View file @
6021a848
{% extends 'admin/model/create.html' %}
{% import 'rule_demo.html' as rule_demo %}
\ No newline at end of file
examples/forms/templates/rule_demo.html
0 → 100644
View file @
6021a848
{% 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
examples/forms/templates/rule_edit.html
0 → 100644
View file @
6021a848
{% extends 'admin/model/edit.html' %}
{% import 'rule_demo.html' as rule_demo %}
examples/layout/templates/create.html
View file @
6021a848
...
@@ -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_arg
s) }}
{{ lib.render_form_fields(
h.get_render_ctx(), admin_view, form, form_rules=form_rule
s) }}
<div
class=
"form-buttons"
>
<div
class=
"form-buttons"
>
{{ lib.render_form_buttons(return_url, extra()) }}
{{ lib.render_form_buttons(return_url, extra()) }}
</div>
</div>
...
...
examples/layout/templates/edit.html
View file @
6021a848
...
@@ -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_arg
s) }}
{{ lib.render_form_fields(
h.get_render_ctx(), admin_view, form, form_rules=form_rule
s) }}
<div
class=
"form-buttons"
>
<div
class=
"form-buttons"
>
{{ lib.render_form_buttons(return_url) }}
{{ lib.render_form_buttons(return_url) }}
</div>
</div>
...
...
flask_admin/contrib/peewee/form.py
View file @
6021a848
...
@@ -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
...
...
flask_admin/form/__init__.py
View file @
6021a848
...
@@ -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'
,
{}))
flask_admin/form/rules.py
0 → 100644
View file @
6021a848
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
flask_admin/helpers.py
View file @
6021a848
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
)
flask_admin/model/base.py
View file @
6021a848
...
@@ -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'
,))
...
...
flask_admin/model/fields.py
View file @
6021a848
...
@@ -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
...
...
flask_admin/model/widgets.py
View file @
6021a848
...
@@ -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
):
...
...
flask_admin/templates/admin/base.html
View file @
6021a848
...
@@ -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 %}
...
...
flask_admin/templates/admin/lib.html
View file @
6021a848
...
@@ -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_arg
s) }}
{{ render_form_fields(form,
form_opts=form_opts, form_rules=form_rule
s) }}
{{ render_form_buttons(cancel_url, extra) }}
{{ render_form_buttons(cancel_url, extra) }}
{% endcall %}
{% endcall %}
{% endmacro %}
{% endmacro %}
flask_admin/templates/admin/model/create.html
View file @
6021a848
...
@@ -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_arg
s) }}
{{ lib.render_form_fields(form,
form_opts=form_opts, form_rules=form_rule
s) }}
{{ lib.render_form_buttons(return_url, extra()) }}
{{ lib.render_form_buttons(return_url, extra()) }}
{% endcall %}
{% endcall %}
{% endblock %}
{% endblock %}
...
...
flask_admin/templates/admin/model/edit.html
View file @
6021a848
...
@@ -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_arg
s) }}
{{ lib.render_form_fields(form,
form_opts=form_opts, form_rules=form_rule
s) }}
{{ lib.render_form_buttons(return_url, extra()) }}
{{ lib.render_form_buttons(return_url, extra()) }}
{% endcall %}
{% endcall %}
{% endblock %}
{% endblock %}
...
...
flask_admin/templates/admin/model/inline_form.html
View file @
6021a848
{% 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>
flask_admin/tests/sqlamodel/templates/another_macro.html
0 → 100644
View file @
6021a848
{% macro another_test() %}
Hello another_test
{% endmacro %}
flask_admin/tests/sqlamodel/templates/macro.html
0 → 100644
View file @
6021a848
{% extends 'admin/model/create.html' %}
{% import 'another_macro.html' as test_lib %}
{% macro test(arg) %}
Value = {{ arg }}
{% endmacro %}
{% macro wrap() %}
<wrapper>
{{ caller() }}
</wrapper>
{% endmacro %}
flask_admin/tests/sqlamodel/test_form_rules.py
0 → 100644
View file @
6021a848
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
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment