50 |
51 |
52 |
62 |
63 |
64 |
65 |
66 |
69 |
70 | - Dashboard
71 | {% block breadcrumbs %}{% endblock %}
72 |
73 | {% for category, message in get_flashed_messages(with_categories=true) %}
74 |
75 |
×
76 |
{{ message }}
77 |
78 | {% endfor %}
79 | {% block pre_content %}{% endblock %}
80 | {% block content %}{% endblock %}
81 |
82 |
83 |
84 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/includes/filter_dropdown.html:
--------------------------------------------------------------------------------
1 | {% macro list_fields(node, prefix) %}
2 | {% for field in node.fields %}
3 |
{% if prefix %}{{ ' / '.join(prefix) }} / {% endif %}{{ field.name }}
9 |
10 | {% endfor %}
11 | {% for child_prefix, child in node.children.items() %}
12 | {{ list_fields(child, prefix + [child_prefix]) }}
13 | {% endfor %}
14 | {% endmacro %}
15 |
16 | Add filter...
17 |
20 |
21 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/includes/filter_widgets.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% for field in filter_form %}
4 | {{ field() }}
5 | {% endfor %}
6 |
7 |
17 |
18 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/includes/form_raw_id.html:
--------------------------------------------------------------------------------
1 | {% if model_admin.foreign_key_lookups %}
2 | {% for field_name, search in model_admin.foreign_key_lookups.items() %}
3 |
18 | {% endfor %}
19 | {% endif %}
20 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/includes/pagination.html:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/index.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.html" %}
2 |
3 | {% block title %}Admin Dashboard{% endblock %}
4 |
5 | {% block content_title %}Admin Dashboard{% endblock %}
6 |
7 | {% block content %}
8 | {% if panels %}
9 |
10 | {% for panel in panels %}
11 | {{ panel.render()|safe() }}
12 | {% endfor %}
13 |
14 | {% endif %}
15 |
16 |
17 |
18 |
19 | | Model name |
20 | Records |
21 | |
22 |
23 |
24 |
25 | {% for iter_admin in model_admins %}
26 |
27 | | {{ iter_admin.get_admin_name()|fix_underscores }} |
28 | {{ iter_admin.get_query().count() }} |
29 |
30 |
34 | |
35 |
36 | {% endfor %}
37 |
38 |
39 | {% endblock %}
40 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/models/add.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/models/base_forms.html" %}
2 |
3 | {% block breadcrumbs %}
4 | {{ super() }}
5 |
/ Add new
6 | {% endblock %}
7 |
8 | {% block tab_add_class %}active{% endblock %}
9 |
10 | {% block content %}
11 | {% include "admin/includes/form_raw_id.html" %}
12 | {% if form.errors %}
13 |
14 |
×
15 |
There were errors with your form submission
16 |
17 | {% endif %}
18 |
34 | {% endblock %}
35 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/models/base.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.html" %}
2 |
3 | {% from 'macros/forms.html' import admin_field %}
4 |
5 | {% block body_class %}{{ super() }} model-admin{% endblock %}
6 |
7 | {% block title %}{{ model_admin.get_display_name()|fix_underscores }} Admin{% endblock %}
8 |
9 | {% block content_title %}{{ model_admin.get_display_name()|fix_underscores }} Admin{% endblock %}
10 |
11 | {% block breadcrumbs %}
12 |
/ {{ model_admin.get_display_name()|fix_underscores }}
13 | {% endblock %}
14 |
15 | {% block pre_content %}
16 |
22 | {% endblock %}
23 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/models/base_filters.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/models/base.html" %}
2 |
3 | {% block extra_script %}
4 | {{ super() }}
5 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/models/base_forms.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/models/base.html" %}
2 |
3 | {% block extra_script %}
4 | {{ super() }}
5 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/models/delete.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/models/base.html" %}
2 |
3 | {% block breadcrumbs %}
4 | {{ super() }}
5 |
/ Delete
6 | {% endblock %}
7 |
8 | {% block extra_tabs %}
9 |
Delete
10 | {% endblock %}
11 |
12 | {% block content %}
13 |
51 | {% endblock %}
52 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/models/edit.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/models/base_forms.html" %}
2 |
3 | {% block breadcrumbs %}
4 | {{ super() }}
5 |
/ Editing
6 | {% endblock %}
7 |
8 | {% block extra_tabs %}
9 |
Editing
10 | {% endblock %}
11 |
12 | {% block content %}
13 | {% include "admin/includes/form_raw_id.html" %}
14 | {% if form.errors %}
15 |
16 |
×
17 |
There were errors with your form submission
18 |
19 | {% endif %}
20 |
37 | {% endblock %}
38 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/models/export.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/models/base_filters.html" %}
2 |
3 | {% block breadcrumbs %}
4 | {{ super() }}
5 |
/ Export
6 | {% endblock %}
7 |
8 | {% block tab_export_class %}active{% endblock %}
9 |
10 | {% block extra_tabs %}
11 | {% include "admin/includes/filter_dropdown.html" %}
12 | {% endblock %}
13 |
14 | {% block content %}
15 | {% include "admin/includes/filter_widgets.html" %}
16 |
17 |
18 |
Currently {{ query.count() }} records are scheduled for export details
19 |
20 |
SQL:
21 | {{ sql[0] }}
22 |
23 | PARAMS:
24 | {{ sql[1] }}
25 |
26 |
27 |
28 |
63 | {% endblock %}
64 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/models/index.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/models/base_filters.html" %}
2 |
3 | {% block tab_index_class %}active{% endblock %}
4 |
5 | {% block extra_tabs %}
6 |
7 | With selected...
8 |
15 |
16 | {% include "admin/includes/filter_dropdown.html" %}
17 | {% endblock %}
18 |
19 | {% block content %}
20 | {% include "admin/includes/filter_widgets.html" %}
21 |
22 |
72 | {% include "admin/includes/pagination.html" %}
73 | {% endblock %}
74 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/panels/base.html:
--------------------------------------------------------------------------------
1 |
2 |
{% block panel_title %}{% endblock %}
3 |
4 | {% block panel_content %}{% endblock %}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/flask_peewee/templates/admin/panels/default.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/panels/base.html" %}
2 |
3 | {% block panel_title %}{{ panel.title }}{% endblock %}
4 |
5 | {% block panel_content %}
6 |
7 | {% endblock %}
8 |
--------------------------------------------------------------------------------
/flask_peewee/templates/auth/login.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% from 'macros/forms.html' import with_errors %}
4 |
5 | {% block content_title %}Log in{% endblock %}
6 |
7 | {% block content %}
8 |
27 | {% endblock %}
28 |
--------------------------------------------------------------------------------
/flask_peewee/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
{% block title %}Title here{% endblock %}
3 |
4 | {% for message in get_flashed_messages() %}
5 |
{{ message }}
6 | {% endfor %}
7 |
{% block content_title %}{% endblock %}
8 | {% block content %}{% endblock %}
9 |
10 |
--------------------------------------------------------------------------------
/flask_peewee/templates/macros/forms.html:
--------------------------------------------------------------------------------
1 | {% macro with_errors(field) %}
2 | {% if field.errors %}
3 | {% set css_class = 'invalid ' + kwargs.pop('class', '') %}
4 | {{ field(class=css_class, **kwargs) }}
5 | {% if field.description %}
{{ field.description|safe() }}{% endif %}
6 |
{% for error in field.errors %}- {{ error|e }}
{% endfor %}
7 | {% else %}
8 | {{ field(**kwargs) }}
9 | {% if field.description %}
{{ field.description|safe() }}{% endif %}
10 | {% endif %}
11 | {% endmacro %}
12 |
13 | {% macro admin_field(field) %}
14 |
15 | {{ field.label(class="control-label") }}
16 |
17 | {% set css_class = 'span8 ' + kwargs.pop('class', '') %}
18 | {% if field.errors %}
19 | {% set css_class = 'error ' + css_class %}
20 | {% endif %}
21 | {{ field(class=css_class, **kwargs) }}
22 | {% if field.description %}{{ field.description|safe() }}{% endif %}
23 | {% for error in field.errors %}{{ error|e }}{% endfor %}
24 |
25 |
26 | {% endmacro %}
27 |
--------------------------------------------------------------------------------
/flask_peewee/tests/__init__.py:
--------------------------------------------------------------------------------
1 | from flask_peewee.tests.admin import *
2 | from flask_peewee.tests.auth import *
3 | from flask_peewee.tests.rest import *
4 | from flask_peewee.tests.serializer import *
5 | from flask_peewee.tests.utils import *
6 |
--------------------------------------------------------------------------------
/flask_peewee/tests/auth.py:
--------------------------------------------------------------------------------
1 | from __future__ import with_statement
2 |
3 | import datetime
4 | try:
5 | import urlparse
6 | except ImportError:
7 | from urllib import parse as urlparse
8 |
9 | from flask import get_flashed_messages
10 | from flask import request
11 | from flask import session
12 | from flask import url_for
13 |
14 | from flask_peewee.auth import Auth
15 | from flask_peewee.auth import LoginForm
16 | from flask_peewee.tests.base import FlaskPeeweeTestCase
17 | from flask_peewee.tests.test_app import User
18 | from flask_peewee.tests.test_app import app
19 | from flask_peewee.tests.test_app import auth
20 | from flask_peewee.tests.test_app import db
21 |
22 |
23 | class TestAuth(Auth):
24 | def setup(self):
25 | pass
26 |
27 |
28 | class AuthTestCase(FlaskPeeweeTestCase):
29 | def setUp(self):
30 | super(AuthTestCase, self).setUp()
31 |
32 | self.test_auth = TestAuth(app, db)
33 |
34 | def login(self, username='admin', password='admin', context=None):
35 | context = context or self.app
36 | return context.post('/accounts/login/', data={
37 | 'username': username,
38 | 'password': password,
39 | })
40 |
41 | def logout(self, context=None):
42 | context = context or self.app
43 | return context.post('/accounts/logout/')
44 |
45 | def test_table(self):
46 | self.assertEqual(self.test_auth.User._meta.table_name, 'user')
47 |
48 | fake_auth = TestAuth(app, db, db_table='peewee_users')
49 | self.assertEqual(fake_auth.User._meta.table_name, 'peewee_users')
50 |
51 | def test_login_view(self):
52 | self.create_users()
53 |
54 | with self.flask_app.test_client() as c:
55 | resp = c.get('/accounts/login/')
56 | self.assertEqual(resp.status_code, 200)
57 |
58 | # check that we have no logged-in user
59 | self.assertContext('user', None)
60 |
61 | frm = self.get_context('form')
62 | self.assertTrue(isinstance(frm, LoginForm))
63 | self.assertEqual(frm.data, {'username': None, 'password': None})
64 |
65 | # make a post missing the username
66 | resp = c.post('/accounts/login/', data={
67 | 'username': '',
68 | 'password': 'xxx',
69 | })
70 | self.assertEqual(resp.status_code, 200)
71 |
72 | # check form for errors
73 | frm = self.get_context('form')
74 | self.assertEqual(frm.errors, {'username': [u'This field is required.']})
75 |
76 | # check that no messages were generated
77 | self.assertFalse('_flashes' in session)
78 |
79 | # check that the auth API does not indicate a logged-in user
80 | self.assertEqual(auth.get_logged_in_user(), None)
81 |
82 | # make a post with a bad username/password combo
83 | resp = c.post('/accounts/login/', data={
84 | 'username': 'normal',
85 | 'password': 'baz',
86 | })
87 | self.assertEqual(resp.status_code, 200)
88 |
89 | # both fields were present so no form errors, but flash the user
90 | # indicating bad username/password combo
91 | self.assertTrue('_flashes' in session)
92 | messages = get_flashed_messages()
93 |
94 | self.assertEqual(messages, [
95 | 'Incorrect username or password',
96 | ])
97 |
98 | # check that the auth API does not indicate a logged-in user
99 | self.assertEqual(auth.get_logged_in_user(), None)
100 |
101 | # make a post with an inactive user
102 | resp = c.post('/accounts/login/', data={
103 | 'username': 'inactive',
104 | 'password': 'inactive',
105 | })
106 | self.assertEqual(resp.status_code, 200)
107 |
108 | # still no logged-in user
109 | self.assertContext('user', None)
110 |
111 | # check that the auth API does not indicate a logged-in user
112 | self.assertEqual(auth.get_logged_in_user(), None)
113 |
114 | # finally post as a known good user
115 | resp = c.post('/accounts/login/', data={
116 | 'username': 'normal',
117 | 'password': 'normal',
118 | })
119 | self.assertEqual(resp.status_code, 302)
120 |
121 | # check that we now have a logged-in user
122 | self.assertEqual(auth.get_logged_in_user(), self.normal)
123 |
124 | def test_login_redirect_in_depth(self):
125 | self.create_users()
126 |
127 | with self.flask_app.test_client() as c:
128 | resp = c.get('/admin/')
129 | location = resp.location
130 | parsed = urlparse.urlparse(location)
131 | querystring = urlparse.parse_qs(parsed.query)
132 | self.assertEqual(querystring, {'next': ['/admin/']})
133 |
134 | # Following the redirect, the next url is passed to context.
135 | location = location.replace('http://localhost', '')
136 | resp = c.get(location)
137 | self.assertEqual(self.get_context('next'), '/admin/')
138 |
139 | # Simulate incorrect password.
140 | resp = c.post('/accounts/login/', data={
141 | 'username': 'normal',
142 | 'password': 'incorrect-password',
143 | 'next': '/admin/',
144 | })
145 | self.assertEqual(resp.status_code, 200)
146 | self.assertEqual(self.get_context('next'), '/admin/')
147 |
148 | resp = c.post('/accounts/login/', data={
149 | 'username': 'normal',
150 | 'password': 'normal',
151 | 'next': '/admin/',
152 | })
153 | self.assertEqual(resp.status_code, 302)
154 | self.assertTrue(resp.headers['location'].endswith('/admin/'))
155 |
156 | def test_login_default_redirect(self):
157 | self.create_users()
158 |
159 | with self.flask_app.test_client() as c:
160 | resp = c.post('/accounts/login/', data={
161 | 'username': 'normal',
162 | 'password': 'normal',
163 | })
164 | self.assertEqual(resp.status_code, 302)
165 | location = resp.location.replace('http://localhost', '')
166 | self.assertTrue(location, '/')
167 |
168 | def test_login_redirect(self):
169 | self.create_users()
170 |
171 | with self.flask_app.test_client() as c:
172 | resp = c.post('/accounts/login/', data={
173 | 'username': 'normal',
174 | 'password': 'normal',
175 | 'next': '/foo-baz/',
176 | })
177 | self.assertEqual(resp.status_code, 302)
178 | self.assertTrue(resp.headers['location'].endswith('/foo-baz/'))
179 |
180 | def test_login_logout(self):
181 | self.create_users()
182 |
183 | with self.flask_app.test_client() as c:
184 | resp = c.post('/accounts/login/', data={
185 | 'username': 'normal',
186 | 'password': 'normal',
187 | })
188 | self.assertEqual(auth.get_logged_in_user(), self.normal)
189 |
190 | resp = c.post('/accounts/logout/')
191 | self.assertEqual(auth.get_logged_in_user(), None)
192 |
193 | resp = c.post('/accounts/login/', data={
194 | 'username': 'admin',
195 | 'password': 'admin',
196 | })
197 | self.assertEqual(auth.get_logged_in_user(), self.admin)
198 |
199 | # log back in without logging out
200 | resp = c.post('/accounts/login/', data={
201 | 'username': 'normal',
202 | 'password': 'normal',
203 | })
204 | self.assertEqual(auth.get_logged_in_user(), self.normal)
205 |
206 | def test_login_required(self):
207 | self.create_users()
208 |
209 | with self.flask_app.test_client() as c:
210 | resp = c.get('/private/')
211 | self.assertEqual(resp.status_code, 302)
212 | self.assertTrue(resp.headers['location'].endswith((
213 | '/accounts/login/?next=%2Fprivate%2F',
214 | '/accounts/login/?next=/private/')))
215 |
216 | self.login('normal', 'normal', c)
217 |
218 | resp = c.get('/private/')
219 | self.assertEqual(resp.status_code, 200)
220 |
221 | self.assertEqual(auth.get_logged_in_user(), self.normal)
222 |
223 | self.login('admin', 'admin', c)
224 |
225 | resp = c.get('/private/')
226 | self.assertEqual(resp.status_code, 200)
227 |
228 | self.assertEqual(auth.get_logged_in_user(), self.admin)
229 |
230 | def test_admin_required(self):
231 | self.create_users()
232 |
233 | with self.flask_app.test_client() as c:
234 | resp = c.get('/secret/')
235 | self.assertEqual(resp.status_code, 302)
236 | self.assertTrue(resp.headers['location'].endswith((
237 | '/accounts/login/?next=%2Fsecret%2F',
238 | '/accounts/login/?next=/secret/')))
239 |
240 | self.login('normal', 'normal', c)
241 |
242 | resp = c.get('/secret/')
243 | self.assertEqual(resp.status_code, 302)
244 | self.assertTrue(resp.headers['location'].endswith((
245 | '/accounts/login/?next=%2Fsecret%2F',
246 | '/accounts/login/?next=/secret/')))
247 | self.assertEqual(auth.get_logged_in_user(), self.normal)
248 |
249 | self.login('admin', 'admin', c)
250 | resp = c.get('/secret/')
251 | self.assertEqual(resp.status_code, 200)
252 |
253 | self.assertEqual(auth.get_logged_in_user(), self.admin)
254 |
--------------------------------------------------------------------------------
/flask_peewee/tests/base.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from flask_peewee.tests import test_app
4 | from flask_peewee.tests.test_app import AModel
5 | from flask_peewee.tests.test_app import BDetails
6 | from flask_peewee.tests.test_app import BModel
7 | from flask_peewee.tests.test_app import CModel
8 | from flask_peewee.tests.test_app import DModel
9 | from flask_peewee.tests.test_app import EModel
10 | from flask_peewee.tests.test_app import FModel
11 | from flask_peewee.tests.test_app import Message
12 | from flask_peewee.tests.test_app import Note
13 | from flask_peewee.tests.test_app import User
14 |
15 |
16 | class FlaskPeeweeTestCase(unittest.TestCase):
17 | def setUp(self):
18 | Note.drop_table(True)
19 | Message.drop_table(True)
20 | User.drop_table(True)
21 | User.create_table()
22 | Message.create_table()
23 | Note.create_table()
24 |
25 | FModel.drop_table(True)
26 | EModel.drop_table(True)
27 | EModel.create_table()
28 | FModel.create_table()
29 |
30 | self.flask_app = test_app.app
31 | self.flask_app._template_context = {}
32 |
33 | self.app = test_app.app.test_client()
34 |
35 | def create_user(self, username, password, **kwargs):
36 | user = User(username=username, email=kwargs.pop('email', ''), **kwargs)
37 | user.set_password(password)
38 | user.save()
39 | return user
40 |
41 | def create_message(self, user, content, **kwargs):
42 | return Message.create(user=user, content=content, **kwargs)
43 |
44 | def create_users(self):
45 | users = [
46 | self.create_user('admin', 'admin', admin=True),
47 | self.create_user('normal', 'normal'),
48 | self.create_user('inactive', 'inactive', active=False),
49 | ]
50 | self.admin, self.normal, self.inactive = users
51 | return users
52 |
53 | def get_context(self, var_name):
54 | if var_name not in self.flask_app._template_context:
55 | raise KeyError('%s not in template context' % var_name)
56 | return self.flask_app._template_context[var_name]
57 |
58 | def assertContext(self, key, value):
59 | self.assertEqual(self.get_context(key), value)
60 |
--------------------------------------------------------------------------------
/flask_peewee/tests/serializer.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from flask_peewee.serializer import Deserializer
4 | from flask_peewee.serializer import Serializer
5 | from flask_peewee.tests.base import FlaskPeeweeTestCase
6 | from flask_peewee.tests.test_app import Message
7 | from flask_peewee.tests.test_app import Note
8 | from flask_peewee.tests.test_app import User
9 |
10 |
11 | class SerializerTestCase(FlaskPeeweeTestCase):
12 | def setUp(self):
13 | super(SerializerTestCase, self).setUp()
14 | self.s = Serializer()
15 | self.d = Deserializer()
16 |
17 | def test_serializer(self):
18 | users = self.create_users()
19 | serialized = self.s.serialize_object(self.admin)
20 | self.assertEqual(serialized, {
21 | 'id': self.admin.id,
22 | 'username': 'admin',
23 | 'password': self.admin.password,
24 | 'join_date': self.admin.join_date.strftime('%Y-%m-%d %H:%M:%S'),
25 | 'active': True,
26 | 'admin': True,
27 | 'email': '',
28 | })
29 |
30 | serialized = self.s.serialize_object(self.admin, fields={User: ['id', 'username']})
31 | self.assertEqual(serialized, {
32 | 'id': self.admin.id,
33 | 'username': 'admin',
34 | })
35 |
36 | serialized = self.s.serialize_object(self.admin, exclude={User: ['password', 'join_date']})
37 | self.assertEqual(serialized, {
38 | 'id': self.admin.id,
39 | 'username': 'admin',
40 | 'active': True,
41 | 'admin': True,
42 | 'email': '',
43 | })
44 |
45 | def test_deserializer(self):
46 | users = self.create_users()
47 |
48 | deserialized, models = self.d.deserialize_object(User(), {
49 | 'id': self.admin.id,
50 | 'username': 'admin',
51 | 'password': self.admin.password,
52 | 'join_date': self.admin.join_date.strftime('%Y-%m-%d %H:%M:%S'),
53 | 'active': True,
54 | 'admin': True,
55 | })
56 |
57 | for attr in ['id', 'username', 'password', 'active', 'admin']:
58 | self.assertEqual(
59 | getattr(deserialized, attr),
60 | getattr(self.admin, attr),
61 | )
62 |
63 | self.assertEqual(
64 | deserialized.join_date.strftime('%Y-%m-%d %H:%M:%S'),
65 | self.admin.join_date.strftime('%Y-%m-%d %H:%M:%S'),
66 | )
67 |
68 | admin_pk = self.admin.id
69 |
70 | deserialized, models = self.d.deserialize_object(self.admin, {
71 | 'username': 'edited',
72 | 'active': False,
73 | 'admin': False,
74 | })
75 |
76 | self.assertEqual(deserialized.username, 'edited')
77 | self.assertEqual(deserialized.admin, False)
78 | self.assertEqual(deserialized.active, False)
79 | self.assertEqual(deserialized.id, admin_pk)
80 |
81 | deserialized.save()
82 |
83 | self.assertEqual(User.select().count(), 3)
84 | edited = User.get(username='edited')
85 | self.assertEqual(edited.id, admin_pk)
86 |
87 | def test_s_and_d(self):
88 | self.create_users()
89 |
90 | s = self.s.serialize_object(self.admin)
91 | d, model_list = self.d.deserialize_object(User(), s)
92 | self.assertEqual(d, self.admin)
93 |
--------------------------------------------------------------------------------
/flask_peewee/tests/templates/admin/notes.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coleifer/flask-peewee/e4c8bd947b6524bb2fd93c3598e48818acf00307/flask_peewee/tests/templates/admin/notes.html
--------------------------------------------------------------------------------
/flask_peewee/tests/templates/base.html:
--------------------------------------------------------------------------------
1 | {# needed for tests #}
2 |
--------------------------------------------------------------------------------
/flask_peewee/tests/test_app.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from flask import Flask
4 | from flask import Response
5 | from flask import flash
6 | from flask import g
7 | from flask import redirect
8 | from flask import render_template
9 | from flask import request
10 | from flask import url_for
11 |
12 | from peewee import *
13 |
14 | # flask-peewee bindings
15 | from flask_peewee.admin import Admin
16 | from flask_peewee.admin import AdminPanel
17 | from flask_peewee.admin import ModelAdmin
18 | from flask_peewee.auth import Auth
19 | from flask_peewee.auth import BaseUser
20 | from flask_peewee.db import Database
21 | from flask_peewee.filters import QueryFilter
22 | from flask_peewee.rest import APIKeyAuthentication
23 | from flask_peewee.rest import AdminAuthentication
24 | from flask_peewee.rest import Authentication
25 | from flask_peewee.rest import RestAPI
26 | from flask_peewee.rest import RestResource
27 | from flask_peewee.rest import RestrictOwnerResource
28 | from flask_peewee.rest import UserAuthentication
29 | from flask_peewee.utils import get_object_or_404
30 | from flask_peewee.utils import make_password
31 | from flask_peewee.utils import object_list
32 |
33 |
34 | class TestFlask(Flask):
35 | def update_template_context(self, context):
36 | ret = super(TestFlask, self).update_template_context(context)
37 | self._template_context.update(context)
38 | return ret
39 |
40 |
41 | app = TestFlask(__name__)
42 | app.config.from_object('flask_peewee.tests.test_config.Configuration')
43 |
44 | db = Database(app)
45 |
46 | @app.before_request
47 | def clear_context():
48 | app._template_context = {}
49 |
50 |
51 | class User(db.Model, BaseUser):
52 | username = CharField()
53 | password = CharField()
54 | email = CharField()
55 | join_date = DateTimeField(default=datetime.datetime.now)
56 | active = BooleanField(default=True)
57 | admin = BooleanField(default=False, verbose_name='Can access admin')
58 |
59 | def __unicode__(self):
60 | return self.username
61 |
62 | def __hash__(self):
63 | return hash(self.username)
64 |
65 | def message_count(self):
66 | return self.message_set.count()
67 |
68 |
69 | class Message(db.Model):
70 | user = ForeignKeyField(User)
71 | content = TextField()
72 | pub_date = DateTimeField(default=datetime.datetime.now)
73 |
74 | def __unicode__(self):
75 | return '%s: %s' % (self.user, self.content)
76 |
77 |
78 | class Note(db.Model):
79 | user = ForeignKeyField(User)
80 | message = TextField()
81 | created_date = DateTimeField(default=datetime.datetime.now)
82 |
83 |
84 | class TestModel(db.Model):
85 | data = TextField()
86 |
87 | class Meta:
88 | order_by = ('id',)
89 |
90 |
91 | class AModel(db.Model):
92 | a_field = CharField()
93 |
94 | class BModel(db.Model):
95 | a = ForeignKeyField(AModel)
96 | b_field = CharField()
97 |
98 | class CModel(db.Model):
99 | b = ForeignKeyField(BModel)
100 | c_field = CharField()
101 |
102 | class DModel(db.Model):
103 | c = ForeignKeyField(CModel)
104 | d_field = CharField()
105 |
106 | class BDetails(db.Model):
107 | b = ForeignKeyField(BModel)
108 |
109 |
110 | class EModel(db.Model):
111 | e_field = CharField()
112 |
113 | class FModel(db.Model):
114 | e = ForeignKeyField(EModel, null=True)
115 | f_field = CharField()
116 |
117 |
118 | class APIKey(db.Model):
119 | key = CharField()
120 | secret = CharField()
121 |
122 |
123 | class NotePanel(AdminPanel):
124 | template_name = 'admin/notes.html'
125 |
126 | def get_urls(self):
127 | return (
128 | ('/create/', self.create),
129 | )
130 |
131 | def create(self):
132 | if request.method == 'POST':
133 | if request.form.get('message'):
134 | Note.create(
135 | user=auth.get_logged_in_user(),
136 | message=request.form['message'],
137 | )
138 | next = request.form.get('next') or self.dashboard_url()
139 | return redirect(next)
140 |
141 | def get_context(self):
142 | return {
143 | 'note_list': Note.select().order_by(('created_date', 'desc')).paginate(1, 3)
144 | }
145 |
146 |
147 | auth = Auth(app, db, user_model=User)
148 | admin = Admin(app, auth)
149 |
150 |
151 | class AAdmin(ModelAdmin):
152 | columns = ('a_field',)
153 |
154 | class BAdmin(ModelAdmin):
155 | columns = ('a', 'b_field',)
156 | include_foreign_keys = {'a': 'a_field'}
157 |
158 | class CAdmin(ModelAdmin):
159 | columns = ('b', 'c_field',)
160 | include_foreign_keys = {'b': 'b_field'}
161 |
162 | class DAdmin(ModelAdmin):
163 | columns = ('c', 'd_field',)
164 | include_foreign_keys = {'c': 'c_field'}
165 |
166 | class MessageAdmin(ModelAdmin):
167 | columns = ('user', 'content', 'pub_date',)
168 |
169 | class NoteAdmin(ModelAdmin):
170 | columns = ('user', 'message', 'created_date',)
171 |
172 |
173 | auth.register_admin(admin)
174 | admin.register(AModel, AAdmin)
175 | admin.register(BModel, BAdmin)
176 | admin.register(CModel, CAdmin)
177 | admin.register(DModel, DAdmin)
178 | admin.register(BDetails)
179 | admin.register(Message, MessageAdmin)
180 | admin.register(Note, NoteAdmin)
181 | admin.register_panel('Notes', NotePanel)
182 |
183 |
184 | class UserResource(RestResource):
185 | exclude = ('password', 'email',)
186 |
187 | def get_query(self):
188 | return User.select().where(User.active==True)
189 |
190 | class AResource(RestResource):
191 | pass
192 |
193 | class BResource(RestResource):
194 | include_resources = {'a': AResource}
195 |
196 | class CResource(RestResource):
197 | include_resources = {'b': BResource}
198 |
199 | class EResource(RestResource):
200 | pass
201 |
202 | class FResource(RestResource):
203 | include_resources = {'e': EResource}
204 |
205 | # rest api stuff
206 | dummy_auth = Authentication(protected_methods=[])
207 | user_auth = UserAuthentication(auth)
208 | admin_auth = AdminAuthentication(auth)
209 | api_key_auth = APIKeyAuthentication(APIKey, ['GET', 'POST', 'PUT', 'DELETE'])
210 |
211 | api = RestAPI(app, default_auth=user_auth)
212 |
213 | api.register(Message, RestrictOwnerResource)
214 | api.register(User, UserResource, auth=admin_auth)
215 | api.register(Note)
216 | api.register(TestModel, auth=api_key_auth)
217 | api.register(AModel, AResource, auth=dummy_auth)
218 | api.register(BModel, BResource, auth=dummy_auth)
219 | api.register(CModel, CResource, auth=dummy_auth)
220 |
221 | api.register(EModel, EResource, auth=dummy_auth)
222 | api.register(FModel, FResource, auth=dummy_auth)
223 |
224 |
225 | # views
226 | @app.route('/')
227 | def homepage():
228 | return Response()
229 |
230 | @app.route('/private/')
231 | @auth.login_required
232 | def private_timeline():
233 | return Response()
234 |
235 | @app.route('/secret/')
236 | @auth.admin_required
237 | def secret_area():
238 | return Response()
239 |
240 |
241 | admin.setup()
242 | api.setup()
243 |
--------------------------------------------------------------------------------
/flask_peewee/tests/test_config.py:
--------------------------------------------------------------------------------
1 | # config
2 |
3 | class Configuration(object):
4 | DATABASE = {
5 | 'name': 'test.db',
6 | 'engine': 'peewee.SqliteDatabase',
7 | }
8 | DEBUG = True
9 | SECRET_KEY = 'shhhh'
10 | TESTING = True
11 |
--------------------------------------------------------------------------------
/flask_peewee/tests/utils.py:
--------------------------------------------------------------------------------
1 | try:
2 | import simplejson as json
3 | except ImportError:
4 | import json
5 |
6 | import datetime
7 |
8 | from flask import request
9 | from werkzeug.exceptions import NotFound
10 |
11 | from flask_peewee.utils import check_password
12 | from flask_peewee.utils import get_object_or_404
13 | from flask_peewee.utils import make_password
14 | from flask_peewee.tests.base import FlaskPeeweeTestCase
15 | from flask_peewee.tests.test_app import Message
16 | from flask_peewee.tests.test_app import Note
17 | from flask_peewee.tests.test_app import User
18 | from flask_peewee.tests.test_app import app as flask_app
19 |
20 |
21 | class UtilsTestCase(FlaskPeeweeTestCase):
22 | def setUp(self):
23 | super(UtilsTestCase, self).setUp()
24 |
25 | def test_get_object_or_404(self):
26 | user = self.create_user('test', 'test')
27 |
28 | # test with model as first arg
29 | self.assertRaises(NotFound, get_object_or_404, User, User.username=='not-here')
30 | self.assertEqual(user, get_object_or_404(User, User.username=='test'))
31 |
32 | # test with query as first arg
33 | active = User.select().where(User.active==True)
34 | inactive = User.select().where(User.active==False)
35 | self.assertRaises(NotFound, get_object_or_404, active, User.username=='not-here')
36 | self.assertRaises(NotFound, get_object_or_404, inactive, User.username=='test')
37 | self.assertEqual(user, get_object_or_404(active, User.username=='test'))
38 |
39 | def test_passwords(self):
40 | p = make_password('testing')
41 | self.assertTrue(check_password('testing', p))
42 | self.assertFalse(check_password('testing ', p))
43 | self.assertFalse(check_password('Testing', p))
44 | self.assertFalse(check_password('', p))
45 |
46 | p2 = make_password('Testing')
47 | self.assertFalse(p == p2)
48 |
--------------------------------------------------------------------------------
/flask_peewee/utils.py:
--------------------------------------------------------------------------------
1 | import math
2 | import random
3 | import re
4 | import sys
5 | from hashlib import sha1
6 |
7 | from flask import abort
8 | from flask import render_template
9 | from flask import request
10 | from peewee import DoesNotExist
11 | from peewee import ForeignKeyField
12 | from peewee import Model
13 | from peewee import SelectQuery
14 |
15 | from flask_peewee._compat import text_type
16 |
17 |
18 | def get_object_or_404(query_or_model, *query):
19 | if not isinstance(query_or_model, SelectQuery):
20 | query_or_model = query_or_model.select()
21 | try:
22 | return query_or_model.where(*query).get()
23 | except DoesNotExist:
24 | abort(404)
25 |
26 | def object_list(template_name, qr, var_name='object_list', **kwargs):
27 | pq = PaginatedQuery(qr, kwargs.pop('paginate_by', 20))
28 | kwargs[var_name] = pq.get_list()
29 | return render_template(template_name, pagination=pq, page=pq.get_page(), **kwargs)
30 |
31 |
32 | class PaginatedQuery(object):
33 | page_var = 'page'
34 |
35 | def __init__(self, query_or_model, paginate_by):
36 | self.paginate_by = paginate_by
37 |
38 | if isinstance(query_or_model, SelectQuery):
39 | self.query = query_or_model
40 | self.model = self.query.model
41 | else:
42 | self.model = query_or_model
43 | self.query = self.model.select()
44 |
45 | def get_page(self):
46 | curr_page = request.args.get(self.page_var)
47 | if curr_page and curr_page.isdigit():
48 | return int(curr_page)
49 | return 1
50 |
51 | def get_pages(self):
52 | if not hasattr(self, '_get_pages'):
53 | self._get_pages = int(math.ceil(
54 | float(self.query.count()) / self.paginate_by))
55 | return self._get_pages
56 |
57 | def get_list(self):
58 | return self.query.paginate(self.get_page(), self.paginate_by)
59 |
60 |
61 | def get_next():
62 | if not request.query_string:
63 | return request.path
64 | return '%s?%s' % (request.path, request.query_string)
65 |
66 | def slugify(s):
67 | return re.sub('[^a-z0-9_\-]+', '-', s.lower())
68 |
69 | def load_class(s):
70 | path, klass = s.rsplit('.', 1)
71 | __import__(path)
72 | mod = sys.modules[path]
73 | return getattr(mod, klass)
74 |
75 | def get_dictionary_from_model(model, fields=None, exclude=None):
76 | model_class = type(model)
77 | data = {}
78 |
79 | fields = fields or {}
80 | exclude = exclude or {}
81 | curr_exclude = exclude.get(model_class, [])
82 | curr_fields = fields.get(model_class, model._meta.sorted_field_names)
83 |
84 | for field_name in curr_fields:
85 | if field_name in curr_exclude:
86 | continue
87 | field_obj = model_class._meta.fields[field_name]
88 | field_data = model.__data__.get(field_name)
89 | if isinstance(field_obj, ForeignKeyField) and field_data and field_obj.rel_model in fields:
90 | rel_obj = getattr(model, field_name)
91 | data[field_name] = get_dictionary_from_model(rel_obj, fields, exclude)
92 | else:
93 | data[field_name] = field_data
94 | return data
95 |
96 | def get_model_from_dictionary(model, field_dict):
97 | if isinstance(model, Model):
98 | model_instance = model
99 | check_fks = True
100 | else:
101 | model_instance = model()
102 | check_fks = False
103 | models = [model_instance]
104 | for field_name, value in field_dict.items():
105 | field_obj = model._meta.fields[field_name]
106 | if isinstance(value, dict):
107 | rel_obj = field_obj.rel_model
108 | if check_fks:
109 | try:
110 | rel_obj = getattr(model, field_name)
111 | except field_obj.rel_model.DoesNotExist:
112 | pass
113 | if rel_obj is None:
114 | rel_obj = field_obj.rel_model
115 | rel_inst, rel_models = get_model_from_dictionary(rel_obj, value)
116 | models.extend(rel_models)
117 | setattr(model_instance, field_name, rel_inst)
118 | else:
119 | setattr(model_instance, field_name, field_obj.python_value(value))
120 | return model_instance, models
121 |
122 | def path_to_models(model, path):
123 | accum = []
124 | if '__' in path:
125 | attr, path = path.split('__', 1)
126 | else:
127 | attr, path = path, ''
128 | if attr in model._meta.fields:
129 | field = model._meta.fields[attr]
130 | accum.append(field.rel_model)
131 | else:
132 | raise AttributeError('%s has no related field named "%s"' % (model, attr))
133 | if path:
134 | accum.extend(path_to_models(model, path))
135 | return accum
136 |
137 |
138 | # borrowing these methods, slightly modified, from django.contrib.auth
139 | def get_hexdigest(salt, raw_password):
140 | data = salt + raw_password
141 | return sha1(data.encode('utf8')).hexdigest()
142 |
143 | def make_password(raw_password):
144 | salt = get_hexdigest(text_type(random.random()), text_type(random.random()))[:5]
145 | hsh = get_hexdigest(salt, raw_password)
146 | return '%s$%s' % (salt, hsh)
147 |
148 | def check_password(raw_password, enc_password):
149 | salt, hsh = enc_password.split('$', 1)
150 | return hsh == get_hexdigest(salt, raw_password)
151 |
--------------------------------------------------------------------------------
/runtests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 | import unittest
5 |
6 | from flask_peewee import tests
7 |
8 | def runtests(*test_args):
9 | suite = unittest.TestLoader().loadTestsFromModule(tests)
10 | result = unittest.TextTestRunner(verbosity=2).run(suite)
11 | if result.failures:
12 | sys.exit(1)
13 | elif result.errors:
14 | sys.exit(2)
15 | sys.exit(0)
16 |
17 | if __name__ == '__main__':
18 | runtests(*sys.argv[1:])
19 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from setuptools import setup, find_packages
3 |
4 | requirements = ['Flask', 'werkzeug', 'jinja2', 'peewee>=3.0.0', 'wtforms', 'wtf-peewee']
5 | if sys.version_info[:2] < (2, 6):
6 | requirements.append('simplejson')
7 |
8 | setup(
9 | name='flask-peewee',
10 | version='3.0.6',
11 | url='http://github.com/coleifer/flask-peewee/',
12 | license='MIT',
13 | author='Charles Leifer',
14 | author_email='coleifer@gmail.com',
15 | description='Peewee integration for flask',
16 | packages=find_packages(),
17 | package_data = {
18 | 'flask_peewee': [
19 | 'static/*/*.css',
20 | 'static/*/*.js',
21 | 'static/*/*.gif',
22 | 'static/*/*.png',
23 | 'templates/*.html',
24 | 'templates/*/*.html',
25 | 'templates/*/*/*.html',
26 | 'tests/*.html',
27 | 'tests/*/*.html',
28 | ],
29 | },
30 | zip_safe=False,
31 | platforms='any',
32 | install_requires=requirements,
33 | classifiers=[
34 | 'Environment :: Web Environment',
35 | 'Intended Audience :: Developers',
36 | 'License :: OSI Approved :: BSD License',
37 | 'Operating System :: OS Independent',
38 | 'Programming Language :: Python',
39 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
40 | 'Topic :: Software Development :: Libraries :: Python Modules'
41 | ],
42 | test_suite='runtests.runtests',
43 | )
44 |
--------------------------------------------------------------------------------