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
ee297aa3
Commit
ee297aa3
authored
Mar 22, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Time, DateTime, Date widgets and fields, documentation update.
parent
d463725e
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
651 additions
and
14 deletions
+651
-14
TODO.txt
TODO.txt
+5
-0
quickstart.rst
doc/quickstart.rst
+4
-4
simple.py
examples/sqla/simple.py
+1
-0
sqlamodel.py
flask_adminex/ext/sqlamodel.py
+24
-5
form.py
flask_adminex/form.py
+78
-0
model.py
flask_adminex/model.py
+2
-2
datepicker.css
flask_adminex/static/css/datepicker.css
+141
-0
bootstrap-datepicker.js
flask_adminex/static/js/bootstrap-datepicker.js
+374
-0
form.js
flask_adminex/static/js/form.js
+6
-0
test.html
flask_adminex/static/js/test.html
+13
-0
edit.html
flask_adminex/templates/admin/model/edit.html
+3
-3
No files found.
TODO.txt
View file @
ee297aa3
- Core
- Right-side menu items (auth?)
- Pregenerate URLs for menu
- Conditional js include for forms or pages
- More form widgets (date time, time, etc)
- Model Admin
- Ability to sort by fields that are not visible?
- SQLA Model Admin
- Use of date time widgets
- Validation of the joins in the query
- Automatic joined load for foreign keys
- Built-in filtering support
- Many2Many support
- Ability to override form field types
- WYSIWYG editor support
- File admin
- Documentation
- Unit tests
...
...
doc/quickstart.rst
View file @
ee297aa3
...
...
@@ -8,7 +8,7 @@ Introduction
------------
While developing the library, I attempted to make it as flexible as possible. Developer should
not
patch a library
to achieve desired functionality.
not
monkey-patch anything
to achieve desired functionality.
Library uses one simple, but powerful concept - administrative pieces are built as classes with
view methods.
...
...
@@ -30,12 +30,12 @@ implementing reusable functional pieces that are highly customizable.
For example, Flask-AdminEx provides ready-to-use SQLAlchemy model interface. It is implemented as a
class which accepts two parameters: model and a database session. While it exposes some
class-level variables which change behavior of the interface (somewhat similar to django.contrib.admin),
nothing prohibits you from overriding form creation
or
database access methods or adding more views.
nothing prohibits you from overriding form creation
logic,
database access methods or adding more views.
Initialization
--------------
To start using
Admin
, you have to create `Admin` class instance and associate it with Flask application::
To start using
Flask-AdminEx
, you have to create `Admin` class instance and associate it with Flask application::
from flask import Flask
from flask.ext.adminex import Admin
...
...
@@ -47,7 +47,7 @@ To start using Admin, you have to create `Admin` class instance and associate it
app.run()
If you
will run this application and will
navigate to `http://localhost:5000/admin/ <http://localhost:5000/admin/>`_,
If you
start this application and
navigate to `http://localhost:5000/admin/ <http://localhost:5000/admin/>`_,
you should see empty "Home" page with a navigation bar on top.
You can change application name by passing `name` parameter to the `Admin` class constructor::
...
...
examples/sqla/simple.py
View file @
ee297aa3
...
...
@@ -31,6 +31,7 @@ class Post(db.Model):
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
title
=
db
.
Column
(
db
.
String
(
120
))
text
=
db
.
Column
(
db
.
Text
)
date
=
db
.
Column
(
db
.
DateTime
)
user_id
=
db
.
Column
(
db
.
Integer
,
db
.
ForeignKey
(
User
.
id
))
user
=
db
.
relationship
(
User
,
backref
=
'posts'
)
...
...
flask_adminex/ext/sqlamodel.py
View file @
ee297aa3
...
...
@@ -3,13 +3,14 @@ from sqlalchemy.orm.interfaces import MANYTOONE, ONETOMANY
from
sqlalchemy.orm.attributes
import
InstrumentedAttribute
from
sqlalchemy.sql.expression
import
desc
from
wtforms.ext.sqlalchemy.orm
import
model_form
,
ModelConverter
from
wtforms
import
fields
from
wtforms.ext.sqlalchemy.orm
import
model_form
,
converts
,
ModelConverter
from
wtforms.ext.sqlalchemy.fields
import
QuerySelectField
,
QuerySelectMultipleField
from
flask
import
flash
from
flask.ext.adminex.model
import
BaseModelView
from
flask.ext.adminex
.form
import
AdminF
orm
from
flask.ext.adminex
import
f
orm
class
AdminModelConverter
(
ModelConverter
):
...
...
@@ -40,13 +41,17 @@ class AdminModelConverter(ModelConverter):
return
self
.
view
.
session
.
query
(
remote_model
)
if
prop
.
direction
is
MANYTOONE
:
return
QuerySelectField
(
query_factory
=
query_factory
,
**
kwargs
)
return
QuerySelectField
(
query_factory
=
query_factory
,
widget
=
form
.
ChosenSelectWidget
(),
**
kwargs
)
elif
prop
.
direction
is
ONETOMANY
:
# Skip backrefs
if
not
local_column
.
foreign_keys
and
self
.
view
.
hide_backrefs
:
return
None
return
QuerySelectMultipleField
(
query_factory
=
query_factory
,
**
kwargs
)
return
QuerySelectMultipleField
(
query_factory
=
query_factory
,
widget
=
form
.
ChosenSelectWidget
(
multiple
=
True
),
**
kwargs
)
else
:
# Ignore pk/fk
if
isinstance
(
prop
,
ColumnProperty
):
...
...
@@ -58,6 +63,20 @@ class AdminModelConverter(ModelConverter):
return
super
(
AdminModelConverter
,
self
)
.
convert
(
model
,
mapper
,
prop
,
field_args
)
@
converts
(
'Date'
)
def
conv_date
(
self
,
field_args
,
**
kwargs
):
field_args
[
'widget'
]
=
form
.
DatePickerWidget
()
return
fields
.
DateField
(
**
field_args
)
@
converts
(
'DateTime'
)
def
conv_datetime
(
self
,
field_args
,
**
kwargs
):
field_args
[
'widget'
]
=
form
.
DateTimePickerWidget
()
return
fields
.
DateTimeField
(
**
field_args
)
@
converts
(
'Time'
)
def
conv_time
(
self
,
field_args
,
**
kwargs
):
return
form
.
TimeField
(
**
field_args
)
class
ModelView
(
BaseModelView
):
"""
...
...
@@ -151,7 +170,7 @@ class ModelView(BaseModelView):
Create form from the model.
"""
return
model_form
(
self
.
model
,
AdminForm
,
form
.
AdminForm
,
self
.
form_columns
,
field_args
=
self
.
form_args
,
converter
=
AdminModelConverter
(
self
))
...
...
flask_adminex/form.py
View file @
ee297aa3
import
time
import
datetime
from
flask.ext
import
wtf
from
wtforms
import
fields
,
widgets
class
AdminForm
(
wtf
.
Form
):
...
...
@@ -17,3 +21,77 @@ class AdminForm(wtf.Form):
return
True
return
False
class
TimeField
(
fields
.
Field
):
"""
A text field which stores a `datetime.time` matching a format.
"""
widget
=
widgets
.
TextInput
()
def
__init__
(
self
,
label
=
None
,
validators
=
None
,
formats
=
None
,
**
kwargs
):
"""
Constructor
`label`
Label
`validators`
Field validators
`formats`
Supported time formats, as a enumerable.
`kwargs`
Any additional parameters
"""
super
(
TimeField
,
self
)
.
__init__
(
label
,
validators
,
**
kwargs
)
self
.
format
=
formats
or
(
'
%
H:
%
M:
%
S'
,
'
%
H:
%
M'
,
'
%
I:
%
M:
%
S
%
p'
,
'
%
I:
%
M
%
p'
,
'
%
I:
%
M:
%
S
%
p'
,
'
%
I:
%
M
%
p'
)
def
_value
(
self
):
if
self
.
raw_data
:
return
u' '
.
join
(
self
.
raw_data
)
else
:
return
self
.
data
and
self
.
data
.
strftime
(
self
.
format
)
or
u''
def
process_formdata
(
self
,
valuelist
):
if
valuelist
:
date_str
=
u' '
.
join
(
valuelist
)
for
format
in
self
.
formats
:
try
:
timetuple
=
time
.
strptime
(
date_str
,
format
)
self
.
data
=
datetime
.
time
(
timetuple
.
tm_hour
,
timetuple
.
tm_min
,
timetuple
.
tm_sec
)
return
except
ValueError
:
pass
raise
ValueError
(
'Invalid time format'
)
class
ChosenSelectWidget
(
widgets
.
Select
):
def
__call__
(
self
,
field
,
**
kwargs
):
if
field
.
allow_blank
and
not
self
.
multiple
:
kwargs
[
'data-role'
]
=
u'chosenblank'
else
:
kwargs
[
'data-role'
]
=
u'chosen'
return
super
(
ChosenSelectWidget
,
self
)
.
__call__
(
field
,
**
kwargs
)
class
ChosenSelectField
(
fields
.
SelectField
):
widget
=
ChosenSelectWidget
class
DatePickerWidget
(
widgets
.
TextInput
):
def
__call__
(
self
,
field
,
**
kwargs
):
kwargs
[
'data-role'
]
=
u'datepicker'
return
super
(
DatePickerWidget
,
self
)
.
__call__
(
field
,
**
kwargs
)
class
DateTimePickerWidget
(
widgets
.
TextInput
):
def
__call__
(
self
,
field
,
**
kwargs
):
kwargs
[
'data-role'
]
=
u'datetimepicker'
return
super
(
DateTimePickerWidget
,
self
)
.
__call__
(
field
,
**
kwargs
)
flask_adminex/model.py
View file @
ee297aa3
...
...
@@ -98,7 +98,7 @@ class BaseModelView(BaseView):
form_args
=
None
"""
Dictionary of form field arguments. Refer to WTForm documentation
on
Dictionary of form field arguments. Refer to WTForm documentation
for
list of possible options.
Example::
...
...
@@ -106,7 +106,7 @@ class BaseModelView(BaseView):
class MyModelView(BaseModelView):
form_args = dict(
name=dict(label='First Name', validators=[wtf.required()])
}
)
"""
# Various settings
...
...
flask_adminex/static/css/datepicker.css
0 → 100644
View file @
ee297aa3
.datepicker
{
background-color
:
#ffffff
;
border-color
:
#999
;
border-color
:
rgba
(
0
,
0
,
0
,
0.2
);
border-style
:
solid
;
border-width
:
1px
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
-webkit-box-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.2
);
-moz-box-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.2
);
box-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.2
);
-webkit-background-clip
:
padding-box
;
-moz-background-clip
:
padding-box
;
background-clip
:
padding-box
;
display
:
none
;
position
:
absolute
;
z-index
:
900
;
margin-left
:
0
;
margin-right
:
0
;
margin-bottom
:
18px
;
padding-bottom
:
4px
;
width
:
218px
;
}
.datepicker
.nav
{
font-weight
:
bold
;
width
:
100%
;
padding
:
4px
0
;
background-color
:
#f5f5f5
;
color
:
#808080
;
border-bottom
:
1px
solid
#ddd
;
-webkit-box-shadow
:
inset
0
1px
0
#ffffff
;
-moz-box-shadow
:
inset
0
1px
0
#ffffff
;
box-shadow
:
inset
0
1px
0
#ffffff
;
zoom
:
1
;
}
.datepicker
.nav
:before
,
.datepicker
.nav
:after
{
display
:
table
;
content
:
""
;
zoom
:
1
;
*
display
:
inline
;
}
.datepicker
.nav
:after
{
clear
:
both
;
}
.datepicker
.nav
span
{
display
:
block
;
float
:
left
;
text-align
:
center
;
height
:
28px
;
line-height
:
28px
;
position
:
relative
;
}
.datepicker
.nav
.bg
{
width
:
100%
;
background-color
:
#fdf5d9
;
height
:
28px
;
position
:
absolute
;
top
:
0
;
left
:
0
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
}
.datepicker
.nav
.fg
{
width
:
100%
;
position
:
absolute
;
top
:
0
;
left
:
0
;
}
.datepicker
.button
{
cursor
:
pointer
;
padding
:
0
4px
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
}
.datepicker
.button
:hover
{
background-color
:
#808080
;
color
:
#ffffff
;
}
.datepicker
.months
{
float
:
left
;
margin-left
:
4px
;
}
.datepicker
.months
.name
{
width
:
72px
;
padding
:
0
;
}
.datepicker
.years
{
float
:
right
;
margin-right
:
4px
;
}
.datepicker
.years
.name
{
width
:
36px
;
padding
:
0
;
}
.datepicker
.dow
,
.datepicker
.days
div
{
float
:
left
;
width
:
30px
;
line-height
:
25px
;
text-align
:
center
;
}
.datepicker
.dow
{
font-weight
:
bold
;
color
:
#808080
;
}
.datepicker
.calendar
{
padding
:
4px
;
}
.datepicker
.days
div
{
cursor
:
pointer
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
}
.datepicker
.days
div
:hover
{
background-color
:
#0064cd
;
color
:
#ffffff
;
}
.datepicker
.overlap
{
color
:
#bfbfbf
;
}
.datepicker
.today
{
background-color
:
#fee9cc
;
}
.datepicker
.selected
{
background-color
:
#bfbfbf
;
color
:
#ffffff
;
}
.datepicker
.time
{
clear
:
both
;
padding-top
:
8px
;
margin-left
:
4px
;
}
.datepicker
.time
label
{
text-align
:
center
;
}
.datepicker
.time
input
{
width
:
200px
;
}
flask_adminex/static/js/bootstrap-datepicker.js
0 → 100644
View file @
ee297aa3
This diff is collapsed.
Click to expand it.
flask_adminex/static/js/form.js
0 → 100644
View file @
ee297aa3
$
(
function
()
{
$
(
'[data-role=chosen]'
).
chosen
();
$
(
'[data-role=chosenblank]'
).
chosen
({
allow_single_deselect
:
true
});
$
(
'[data-role=datepicker]'
).
datepicker
();
$
(
'[data-role=datetimepicker]'
).
datepicker
({
displayTime
:
true
});
});
flask_adminex/static/js/test.html
0 → 100644
View file @
ee297aa3
<html>
<head>
<link
href=
"../bootstrap/css/bootstrap.css"
rel=
"stylesheet"
>
<link
href=
"../css/datepicker.css"
rel=
"stylesheet"
>
</head>
<body>
<form
action=
""
>
<input
data-datepicker=
"datepicker"
type=
"text"
value=
"2011-10-12"
/>
</form>
<script
src=
"http://code.jquery.com/jquery-1.7.min.js"
></script>
<script
src=
"bootstrap-datepicker.js"
></script>
</body>
</html>
\ No newline at end of file
flask_adminex/templates/admin/model/edit.html
View file @
ee297aa3
...
...
@@ -2,6 +2,7 @@
{% block head %}
<link
href=
"{{ url_for('admin.static', filename='chosen/chosen.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='css/datepicker.css') }}"
rel=
"stylesheet"
>
{% endblock %}
{% block body %}
...
...
@@ -37,7 +38,6 @@
{% endblock %}
{% block tail %}
<script>
$
(
"select"
).
chosen
({
allow_single_deselect
:
true
});
</script>
<script
src=
"{{ url_for('admin.static', filename='js/bootstrap-datepicker.js') }}"
></script>
<script
src=
"{{ url_for('admin.static', filename='js/form.js') }}"
></script>
{% endblock %}
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