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
0c2f226a
Commit
0c2f226a
authored
Aug 27, 2013
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow AJAX FKs usage in inline models
parent
f6b400ee
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
113 additions
and
37 deletions
+113
-37
ajax.py
flask_admin/contrib/sqla/ajax.py
+28
-1
form.py
flask_admin/contrib/sqla/form.py
+30
-6
view.py
flask_admin/contrib/sqla/view.py
+3
-28
form.py
flask_admin/model/form.py
+4
-2
test_inlineform.py
flask_admin/tests/sqlamodel/test_inlineform.py
+48
-0
No files found.
flask_admin/contrib/sqla/ajax.py
View file @
0c2f226a
from
sqlalchemy
import
or_
from
flask.ext.admin._compat
import
as_unicode
from
flask.ext.admin._compat
import
as_unicode
,
string_types
from
flask.ext.admin.model.ajax
import
AjaxModelLoader
,
DEFAULT_PAGE_SIZE
...
...
@@ -40,3 +40,30 @@ class QueryAjaxModelLoader(AjaxModelLoader):
query
=
query
.
filter
(
or_
(
*
filters
))
return
query
.
offset
(
offset
)
.
limit
(
limit
)
.
all
()
def
create_ajax_loader
(
model
,
session
,
name
,
field_name
,
fields
):
attr
=
getattr
(
model
,
field_name
,
None
)
if
attr
is
None
:
raise
ValueError
(
'Model
%
s does not have field
%
s.'
%
(
model
,
field_name
))
if
not
hasattr
(
attr
,
'property'
)
or
not
hasattr
(
attr
.
property
,
'direction'
):
raise
ValueError
(
'
%
s.
%
s is not a relation.'
%
(
model
,
field_name
))
remote_model
=
attr
.
prop
.
mapper
.
class_
remote_fields
=
[]
for
field
in
fields
:
if
isinstance
(
field
,
string_types
):
attr
=
getattr
(
remote_model
,
field
,
None
)
if
not
attr
:
raise
ValueError
(
'
%
s.
%
s does not exist.'
%
(
remote_model
,
field
))
remote_fields
.
append
(
attr
)
else
:
# TODO: Figure out if it is valid SQLAlchemy property?
remote_fields
.
append
(
field
)
return
QueryAjaxModelLoader
(
name
,
session
,
remote_model
,
remote_fields
)
flask_admin/contrib/sqla/form.py
View file @
0c2f226a
...
...
@@ -4,8 +4,7 @@ from sqlalchemy import Boolean, Column
from
flask.ext.admin
import
form
from
flask.ext.admin.form
import
Select2Field
from
flask.ext.admin.model.form
import
(
converts
,
ModelConverterBase
,
InlineFormAdmin
,
InlineModelConverterBase
,
FieldPlaceholder
)
InlineModelConverterBase
,
FieldPlaceholder
)
from
flask.ext.admin.model.fields
import
AjaxSelectField
,
AjaxSelectMultipleField
from
flask.ext.admin.model.helpers
import
prettify_name
from
flask.ext.admin._backwards
import
get_property
...
...
@@ -14,6 +13,7 @@ from flask.ext.admin._compat import iteritems
from
.validators
import
Unique
from
.fields
import
QuerySelectField
,
QuerySelectMultipleField
,
InlineModelFormList
from
.tools
import
is_inherited_primary_key
,
get_column_for_current_model
,
has_multiple_pks
from
.ajax
import
create_ajax_loader
try
:
# Field has better input parsing capabilities.
...
...
@@ -72,7 +72,7 @@ class AdminModelConverter(ModelConverterBase):
return
None
def
_model_select_field
(
self
,
prop
,
multiple
,
remote_model
,
**
kwargs
):
loader
=
self
.
view
.
_form_ajax_refs
.
get
(
prop
.
key
)
loader
=
getattr
(
self
.
view
,
'_form_ajax_refs'
,
{})
.
get
(
prop
.
key
)
if
loader
:
if
multiple
:
...
...
@@ -467,7 +467,7 @@ class InlineModelConverter(InlineModelConverterBase):
# Special case for model instances
if
info
is
None
:
if
hasattr
(
p
,
'_sa_class_manager'
):
return
InlineFormAdmin
(
p
)
return
self
.
form_admin_class
(
p
)
else
:
model
=
getattr
(
p
,
'model'
,
None
)
...
...
@@ -479,12 +479,36 @@ class InlineModelConverter(InlineModelConverterBase):
if
not
attr
.
startswith
(
'_'
)
and
attr
!=
'model'
:
attrs
[
attr
]
=
getattr
(
p
,
attr
)
return
InlineFormAdmin
(
model
,
**
attrs
)
return
self
.
form_admin_class
(
model
,
**
attrs
)
info
=
InlineFormAdmin
(
model
,
**
attrs
)
info
=
self
.
form_admin_class
(
model
,
**
attrs
)
# Resolve AJAX FKs
info
.
_form_ajax_refs
=
self
.
process_ajax_refs
(
info
)
return
info
def
process_ajax_refs
(
self
,
info
):
refs
=
getattr
(
info
,
'form_ajax_refs'
,
None
)
result
=
{}
if
refs
:
for
name
,
opts
in
iteritems
(
refs
):
new_name
=
'
%
s.
%
s'
%
(
info
.
model
.
__name__
.
lower
(),
name
)
loader
=
None
if
isinstance
(
opts
,
(
list
,
tuple
)):
loader
=
create_ajax_loader
(
info
.
model
,
self
.
session
,
new_name
,
name
,
opts
)
else
:
loader
=
opts
result
[
name
]
=
loader
self
.
view
.
_form_ajax_refs
[
new_name
]
=
loader
return
result
def
contribute
(
self
,
model
,
form_class
,
inline_model
):
"""
Generate form fields for inline forms and contribute them to
...
...
flask_admin/contrib/sqla/view.py
View file @
0c2f226a
...
...
@@ -16,7 +16,7 @@ from flask.ext.admin._backwards import ObsoleteAttr
from
flask.ext.admin.contrib.sqla
import
form
,
filters
,
tools
from
.typefmt
import
DEFAULT_FORMATTERS
from
.tools
import
is_inherited_primary_key
,
get_column_for_current_model
,
get_query_for_ids
from
.ajax
import
QueryAjaxModelL
oader
from
.ajax
import
create_ajax_l
oader
class
ModelView
(
BaseModelView
):
...
...
@@ -554,9 +554,7 @@ class ModelView(BaseModelView):
self
.
model_form_converter
)
for
m
in
self
.
inline_models
:
form_class
=
inline_converter
.
contribute
(
self
.
model
,
form_class
,
m
)
form_class
=
inline_converter
.
contribute
(
self
.
model
,
form_class
,
m
)
return
form_class
...
...
@@ -589,30 +587,7 @@ class ModelView(BaseModelView):
# AJAX foreignkey support
def
_create_ajax_loader
(
self
,
name
,
fields
):
attr
=
getattr
(
self
.
model
,
name
,
None
)
if
attr
is
None
:
raise
ValueError
(
'Model
%
s does not have field
%
s.'
%
(
self
.
model
,
name
))
if
not
hasattr
(
attr
,
'property'
)
or
not
hasattr
(
attr
.
property
,
'direction'
):
raise
ValueError
(
'
%
s.
%
s is not a relation.'
%
(
self
.
model
,
name
))
remote_model
=
attr
.
prop
.
mapper
.
class_
remote_fields
=
[]
for
field
in
fields
:
if
isinstance
(
field
,
string_types
):
attr
=
getattr
(
remote_model
,
field
,
None
)
if
not
attr
:
raise
ValueError
(
'
%
s.
%
s does not exist.'
%
(
remote_model
,
field
))
remote_fields
.
append
(
attr
)
else
:
# TODO: Figure out if it is valid SQLAlchemy property?
remote_fields
.
append
(
field
)
return
QueryAjaxModelLoader
(
name
,
self
.
session
,
remote_model
,
remote_fields
)
return
create_ajax_loader
(
self
.
model
,
self
.
session
,
name
,
name
,
fields
)
# Database-related API
def
get_query
(
self
):
...
...
flask_admin/model/form.py
View file @
0c2f226a
...
...
@@ -131,6 +131,8 @@ class ModelConverterBase(object):
class
InlineModelConverterBase
(
object
):
form_admin_class
=
InlineFormAdmin
def
__init__
(
self
,
view
):
"""
Base constructor
...
...
@@ -173,8 +175,8 @@ class InlineModelConverterBase(object):
- Model class
"""
if
isinstance
(
p
,
tuple
):
return
InlineFormAdmin
(
p
[
0
],
**
p
[
1
])
elif
isinstance
(
p
,
InlineFormAdmin
):
return
self
.
form_admin_class
(
p
[
0
],
**
p
[
1
])
elif
isinstance
(
p
,
self
.
form_admin_class
):
return
p
return
None
...
...
flask_admin/tests/sqlamodel/test_inlineform.py
View file @
0c2f226a
...
...
@@ -93,3 +93,51 @@ def test_inline_form():
eq_
(
rv
.
status_code
,
302
)
eq_
(
User
.
query
.
count
(),
0
)
eq_
(
UserInfo
.
query
.
count
(),
0
)
def
test_inline_form_ajax_fk
():
app
,
db
,
admin
=
setup
()
# Set up models and database
class
User
(
db
.
Model
):
__tablename__
=
'users'
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
name
=
db
.
Column
(
db
.
String
,
unique
=
True
)
def
__init__
(
self
,
name
=
None
):
self
.
name
=
name
class
Tag
(
db
.
Model
):
__tablename__
=
'tags'
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
name
=
db
.
Column
(
db
.
String
,
unique
=
True
)
class
UserInfo
(
db
.
Model
):
__tablename__
=
'user_info'
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
key
=
db
.
Column
(
db
.
String
,
nullable
=
False
)
val
=
db
.
Column
(
db
.
String
)
user_id
=
db
.
Column
(
db
.
Integer
,
db
.
ForeignKey
(
User
.
id
))
user
=
db
.
relationship
(
User
,
backref
=
db
.
backref
(
'info'
,
cascade
=
"all, delete-orphan"
,
single_parent
=
True
))
tag_id
=
db
.
Column
(
db
.
Integer
,
db
.
ForeignKey
(
Tag
.
id
))
tag
=
db
.
relationship
(
Tag
,
backref
=
'user_info'
)
db
.
create_all
()
# Set up Admin
class
UserModelView
(
ModelView
):
inline_models
=
[(
UserInfo
,
{
'form_ajax_refs'
:
{
'tag'
:
(
'name'
,)}})]
view
=
UserModelView
(
User
,
db
.
session
)
admin
.
add_view
(
view
)
form
=
view
.
create_form
()
user_info_form
=
form
.
info
.
unbound_field
.
args
[
0
]
loader
=
user_info_form
.
tag
.
args
[
0
]
eq_
(
loader
.
name
,
'userinfo.tag'
)
eq_
(
loader
.
model
,
Tag
)
ok_
(
'userinfo.tag'
in
view
.
_form_ajax_refs
)
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