-
Notifications
You must be signed in to change notification settings - Fork 0
/
a-todo-app-with-flask-and-pony.html
272 lines (215 loc) · 44.5 KB
/
a-todo-app-with-flask-and-pony.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
<!DOCTYPE html>
<html> <head lang=en><meta charset=UTF-8><title>A Todo app with flask and Pony | EF</title><link href=//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css rel=stylesheet><link href=http://fonts.googleapis.com/css?family=Inconsolata rel=stylesheet type=text/css><link rel=stylesheet href=http://nafiulis.me/theme/css/main.css><link rel=stylesheet href=http://nafiulis.me/theme/css/pygment.css><script src=http://nafiulis.me/theme/js/jquery.min.js></script><script src=http://nafiulis.me/theme/js/main.js></script></head> <body> <!--Heading at the top saying "Engineering Fantasy"--> <div id=header_top> <div class=title> <a href=http://nafiulis.me><span id=engineering>Engineering</span><span id=fantasy>Fantasy</span></a> </div> </div> <button type=button class="js-menu-trigger sliding-menu-button button-nav"> <img src=https://raw.githubusercontent.com/thoughtbot/refills/master/source/images/menu-white.png alt="Menu Icon"> </button> <!--Navigation Bar--> <nav class="js-menu sliding-menu-content"> <span class=section-header>Pages</span> <ul> <li><a href=http://nafiulis.me>Home</a></li> <li><a href=http://nafiulis.me/tags.html>Tags</a></li> <li><a href=http://nafiulis.me/pages/about-me.html>About Me</a></li> </ul> <span class=section-header>Categories</span> <ul> <li><a href=http://nafiulis.me/category/anime.html>Anime</a></li> <li><a href=http://nafiulis.me/category/education.html>Education</a></li> <li><a href=http://nafiulis.me/category/productivity.html>Productivity</a></li> <li><a href=http://nafiulis.me/category/programming.html>programming</a></li> <li><a href=http://nafiulis.me/category/rants.html>rants</a></li> </ul> </nav> <div class="js-menu-screen menu-screen"></div> <!--Main Container--> <div class=container> <!--Top most menu--> <div id=menu> <div class=left> <a href=http://nafiulis.me/feeds/all.atom.xml><i class="fa fa-rss
icon"></i></a> <a href=https://twitter.com/gamesbrainiac><i class="fa fa-twitter icon"></i></a> </div> <div class=center> <h1 class=message>Nafiul Islam on casting spells with code</h1> </div> <div class=right> <a href=https://github.com/gamesbrainiac><i class="fa fa-github icon"></i></a> <a href=http://stackoverflow.com/users/1624921/games-brainiac><i class="fa fa-stack-overflow icon" style="padding-right: 30px"></i></a> </div> </div> <!--Main blog list container--> <div id=blogroll> <div class=article-container> <h1>A Todo app with flask and Pony</h1> <p class=time>Thursday, 09 October 2014</p> <div class=article-content> <div class="contents topic" id=table-of-contents> <p class="topic-title first">Table of Contents</p> <ul class=simple> <li><a class="reference internal" href=#installation id=id1>Installation</a></li> <li><a class="reference internal" href=#routes id=id2>Routes</a></li> <li><a class="reference internal" href=#using-pony id=id3>Using Pony</a><ul> <li><a class="reference internal" href=#querying id=id4>Querying</a></li> <li><a class="reference internal" href=#creating-objects id=id5>Creating Objects</a></li> <li><a class="reference internal" href=#deleting-objects id=id6>Deleting Objects</a></li> </ul> </li> <li><a class="reference internal" href=#references id=id7>References</a></li> <li><a class="reference internal" href=#update id=id8>Update</a></li> <li><a class="reference internal" href=#p-s id=id9>P.S.</a></li> </ul> </div> <p><a class="reference external" href=http://ponyorm.com/ >Pony</a> is a new ORM that has some very nice features especially when it comes to querying. It's still in the works, missing features like migration and <code>py3</code> support (although version <code>0.6</code> supports <code>py3</code> and already has a release candidate), but the creators tell me that both these features will be made available by the end of this year. So, I decided to make a simple todo app with pony, <a class="reference external" href=http://flask.pocoo.org/ >flask</a> and <a class="reference external" href=http://flask-restful.readthedocs.org/en/latest/ >flask-restful</a>. You can get the <a class="reference external" href=https://github.com/gamesbrainiac/Pony-Todo-API/tree/83287fc4fd99ebb6dc9e613571695a8c835c46b9>source code</a> from github.</p> <div class=section id=installation> <h2><a class=toc-backref href=#id1>Installation</a></h2> <p>First, we need to set up our project and install a couple of requirements:</p> <div class=highlight><pre><span></span>pip install flask flask-restful pony
</pre></div> </div> <div class=section id=routes> <h2><a class=toc-backref href=#id2>Routes</a></h2> <p>We are going to be making an API for out todo list. A todo item will have three things:</p> <ul class=simple> <li><code>id</code>, the primary key</li> <li><code>data</code>, what the item is about e.g. write a tutorial on pony</li> <li><code>tags</code>, what type of item it is, e.g. work, important, home</li> </ul> <p>In order to do this, lets create our routes:</p> <ul class=simple> <li><code>/</code> will show you all the todo items on a <code>GET</code> request. One can add new items with a <code>PUT</code> request.</li> <li><code>/<int:todo_id></code> will allow a <code>GET</code> request, and a <code>DELETE</code> request. that shows you information regarding the todo item with that <code>id</code>.</li> <li><code>/tags/</code>, will show you all the tags that are available, with links to those tag elements</li> <li><code>/tags/<int:tag_id></code> will show you the corresponding tag.</li> </ul> <p>Thus, a <code>tag</code> must have an <code>id</code> and a value, which will likely be a string. Lets get to making our routes then:</p> <div class=highlight><pre><span></span><span class=c1># Inside app.py</span>
<span class=kn>from</span> <span class=nn>flask</span> <span class=kn>import</span> <span class=n>Flask</span>
<span class=kn>import</span> <span class=nn>flask.ext.restful</span> <span class=kn>as</span> <span class=nn>rest</span>
<span class=n>app</span> <span class=o>=</span> <span class=n>Flask</span><span class=p>(</span><span class=vm>__name__</span><span class=p>)</span>
<span class=n>api</span> <span class=o>=</span> <span class=n>rest</span><span class=o>.</span><span class=n>Api</span><span class=p>(</span><span class=n>app</span><span class=p>)</span>
<span class=c1># Resource ######################################################################</span>
<span class=k>class</span> <span class=nc>Todos</span><span class=p>(</span><span class=n>rest</span><span class=o>.</span><span class=n>Resource</span><span class=p>):</span>
<span class=k>def</span> <span class=nf>get</span><span class=p>(</span><span class=bp>self</span><span class=p>):</span>
<span class=sd>"""Will give you all the todo items"""</span>
<span class=k>return</span> <span class=p>{}</span>
<span class=k>def</span> <span class=nf>put</span><span class=p>(</span><span class=bp>self</span><span class=p>):</span>
<span class=sd>"""Payload contains information to create new todo item"""</span>
<span class=k>return</span> <span class=p>{}</span>
<span class=k>class</span> <span class=nc>TodoItem</span><span class=p>(</span><span class=n>rest</span><span class=o>.</span><span class=n>Resource</span><span class=p>):</span>
<span class=k>def</span> <span class=nf>get</span><span class=p>(</span><span class=bp>self</span><span class=p>,</span> <span class=n>todo_id</span><span class=p>):</span>
<span class=sd>"""</span>
<span class=sd> Get specific information on a Todo item</span>
<span class=sd> :param todo_id: The Todo Item's ID, which is unique and a primary key</span>
<span class=sd> :type todo_id: int</span>
<span class=sd> """</span>
<span class=k>return</span> <span class=p>{}</span>
<span class=k>class</span> <span class=nc>Tags</span><span class=p>(</span><span class=n>rest</span><span class=o>.</span><span class=n>Resource</span><span class=p>):</span>
<span class=k>def</span> <span class=nf>get</span><span class=p>(</span><span class=bp>self</span><span class=p>):</span>
<span class=sd>"""Will show you all tags"""</span>
<span class=k>return</span> <span class=p>{}</span>
<span class=k>class</span> <span class=nc>TagItem</span><span class=p>(</span><span class=n>rest</span><span class=o>.</span><span class=n>Resource</span><span class=p>):</span>
<span class=k>def</span> <span class=nf>get</span><span class=p>(</span><span class=bp>self</span><span class=p>,</span> <span class=n>tag_id</span><span class=p>):</span>
<span class=sd>"""</span>
<span class=sd> Will show you information about a specific tag</span>
<span class=sd> :param tag_id: ID for the tag</span>
<span class=sd> :type tag_id: int</span>
<span class=sd> """</span>
<span class=k>return</span> <span class=p>{}</span>
<span class=c1># Routes #######################################################################</span>
<span class=n>api</span><span class=o>.</span><span class=n>add_resource</span><span class=p>(</span><span class=n>Todos</span><span class=p>,</span> <span class=s1>'/'</span><span class=p>,</span> <span class=n>endpoint</span><span class=o>=</span><span class=s1>'Home'</span><span class=p>)</span>
<span class=n>api</span><span class=o>.</span><span class=n>add_resource</span><span class=p>(</span><span class=n>TodoItem</span><span class=p>,</span> <span class=s1>'/<int:todo_id>'</span><span class=p>,</span> <span class=n>endpoint</span><span class=o>=</span><span class=s1>'TodoItem'</span><span class=p>)</span>
<span class=n>api</span><span class=o>.</span><span class=n>add_resource</span><span class=p>(</span><span class=n>Tags</span><span class=p>,</span> <span class=s1>'/tags/'</span><span class=p>,</span> <span class=n>endpoint</span><span class=o>=</span><span class=s1>'Tags'</span><span class=p>)</span>
<span class=n>api</span><span class=o>.</span><span class=n>add_resource</span><span class=p>(</span><span class=n>TagItem</span><span class=p>,</span> <span class=s1>'/tags/<int:tag_id>'</span><span class=p>,</span> <span class=n>endpoint</span><span class=o>=</span><span class=s1>'TagItem'</span><span class=p>)</span>
<span class=k>if</span> <span class=vm>__name__</span> <span class=o>==</span> <span class=s1>'__main__'</span><span class=p>:</span>
<span class=n>app</span><span class=o>.</span><span class=n>run</span><span class=p>(</span><span class=n>debug</span><span class=o>=</span><span class=bp>True</span><span class=p>)</span>
</pre></div> <p>The <code>get</code> and <code>put</code> methods inside the <code>rest.Resource</code> objects indicate the types of methods that you can use to interact with that route. The <code>api.add_resource(...)</code> function allows you to register the API handler, i.e. a <code>rest.Resource</code> object and a route. the <code>endpoint</code> is optional.</p> <p>The <code>endpoint</code> is an optional parameter and gives its route a reference, which is useful for redirection.</p> </div> <div class=section id=using-pony> <h2><a class=toc-backref href=#id3>Using Pony</a></h2> <p>Right now, all the API will return are empty json objects. Before we actually start returning stuff, lets make our models:</p> <div class=highlight><pre><span></span><span class=c1># Inside models.py</span>
<span class=kn>from</span> <span class=nn>pony</span> <span class=kn>import</span> <span class=n>orm</span> <span class=c1># 1</span>
<span class=n>db</span> <span class=o>=</span> <span class=n>orm</span><span class=o>.</span><span class=n>Database</span><span class=p>()</span> <span class=c1># 2</span>
<span class=k>class</span> <span class=nc>Todo</span><span class=p>(</span><span class=n>db</span><span class=o>.</span><span class=n>Entity</span><span class=p>):</span> <span class=c1># 3</span>
<span class=n>_table_</span> <span class=o>=</span> <span class=s1>'Todos'</span> <span class=c1># 4</span>
<span class=n>data</span> <span class=o>=</span> <span class=n>orm</span><span class=o>.</span><span class=n>Required</span><span class=p>(</span><span class=nb>unicode</span><span class=p>)</span> <span class=c1># 5</span>
<span class=n>tags</span> <span class=o>=</span> <span class=n>orm</span><span class=o>.</span><span class=n>Set</span><span class=p>(</span><span class=s2>"Tag"</span><span class=p>)</span> <span class=c1># 6</span>
<span class=k>class</span> <span class=nc>Tag</span><span class=p>(</span><span class=n>db</span><span class=o>.</span><span class=n>Entity</span><span class=p>):</span>
<span class=n>_table_</span> <span class=o>=</span> <span class=s1>'Tags'</span>
<span class=n>name</span> <span class=o>=</span> <span class=n>orm</span><span class=o>.</span><span class=n>Required</span><span class=p>(</span><span class=nb>unicode</span><span class=p>,</span> <span class=n>unique</span><span class=o>=</span><span class=bp>True</span><span class=p>)</span> <span class=c1># 7</span>
<span class=n>tags</span> <span class=o>=</span> <span class=n>orm</span><span class=o>.</span><span class=n>Set</span><span class=p>(</span><span class=s2>"Todo"</span><span class=p>)</span> <span class=c1># 8</span>
</pre></div> <p>The first thing we do is import <code>pony.orm</code> in <code class=coderef>1</code> . In most of the documentation, they import everything via <code>from pony.orm import *</code>, but since I like namespaces, I prefer using <code>orm</code>. <code>pony.orm</code> houses everything you need to create and query objects.</p> <p>We initialize the database using <code>orm.Database()</code> in <code class=coderef>2</code>. This instance of the <code>pony.orm.Database</code> class stores all the data needed to create the tables that your models define.</p> <p>In <code class=coderef>3</code>, we create an <code>Entity</code> object by inheriting from <code>db.Entity</code>. Note that we use the <code>db</code> instance, and not any import from <code>pony.orm</code>. Its similar to SQLAlchemy's <code>sqlalchemy.schema.MetaData</code> object.</p> <p>In <code class=coderef>4</code>, we set the table name to <code>Todos</code>, its a lot like <code>__tablename__</code> in SQLAlchemy.</p> <p>In Pony, you can have either <code>Required</code> columns or <code>Optional</code> columns <a class="reference external" href=http://doc.ponyorm.com/entities.html#entity-attributes>(described here)</a>. In <code class=coderef>5</code>, we set <code>data</code> to be a <code>Required</code> field of type <code>unicode</code>. Note that this <code>unicode</code> class is not from the <code>pony.orm</code> namespace.</p> <p>You can also have many-to-many types in pony, using <code>Set</code> in <code class=coderef>6</code> and <code class=coderef>8</code>. In <code class=coderef>7</code>, we set <code>unique</code> to be <code>True</code>. You have a couple of optional attributes that you can add to <code>Required</code> or <code>Optional</code> fields. <a class="reference external" href=http://doc.ponyorm.com/entities.html#required-and-optional>(Required and Optional Explained in more detail)</a></p> <p>Notices any <code>id</code> anywhere? No? Well, thats because pony implicitly creates the <code>id</code> column for you. <a class="reference external" href=http://doc.ponyorm.com/firststeps.html#defining-entities>(More about implicit ID creation)</a></p> <p>Now, in <code>app.py</code> we need to bind a database and then generate mappings:</p> <div class=highlight><pre><span></span><span class=c1># Inside app.py</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
<span class=kn>from</span> <span class=nn>models</span> <span class=kn>import</span> <span class=o>*</span>
<span class=n>app</span> <span class=o>=</span> <span class=n>Flask</span><span class=p>(</span><span class=vm>__name__</span><span class=p>)</span>
<span class=n>api</span> <span class=o>=</span> <span class=n>rest</span><span class=o>.</span><span class=n>Api</span><span class=p>(</span><span class=n>app</span><span class=p>)</span>
<span class=n>db</span><span class=o>.</span><span class=n>bind</span><span class=p>(</span><span class=s1>'sqlite'</span><span class=p>,</span> <span class=s1>'todo_api.db'</span><span class=p>,</span> <span class=n>create_db</span><span class=o>=</span><span class=bp>True</span><span class=p>)</span>
<span class=n>db</span><span class=o>.</span><span class=n>generate_mapping</span><span class=p>(</span><span class=n>create_tables</span><span class=o>=</span><span class=bp>True</span><span class=p>)</span>
<span class=c1># Resource #####################################################################</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
</pre></div> <p>We first bind the objects, in this case <code>Tag</code> and <code>Todo</code> to the sqlite database. <a class="reference external" href=http://doc.ponyorm.com/database.html#binding-the-database-object-to-a-specific-database>(More on binding databases)</a></p> <p>We set <code>create_tables</code> and <code>create_db</code> to <code>True</code> because this is a new database, and no tables have been created yet. <a class="reference external" href=http://doc.ponyorm.com/database.html?highlight=bind#mapping-entities-to-the-database-tables>(More information regarding creating tables)</a></p> <p><code>generate_mapping</code> generates all the SQL needed to create the tables. If you actually set <code>orm.sql_debug(True)</code>, then all the SQL that is being generated will be logged into your console.</p> <p>These are what the generated tables look like:</p> <img alt="What the generated tables look like in Pony" class=align-center src=images/pony_01.png> <div class=section id=querying> <h3><a class=toc-backref href=#id4>Querying</a></h3> <p>Lets get to finding stuff from the database. First, we need to show all the todo items in our database for our <code>Todos.get</code> function:</p> <div class=highlight><pre><span></span><span class=c1># Inside app.py</span>
<span class=k>class</span> <span class=nc>Todos</span><span class=p>(</span><span class=n>rest</span><span class=o>.</span><span class=n>Resource</span><span class=p>):</span>
<span class=k>def</span> <span class=nf>get</span><span class=p>(</span><span class=bp>self</span><span class=p>):</span>
<span class=sd>"""Will give you all the todo items"""</span>
<span class=k>with</span> <span class=n>orm</span><span class=o>.</span><span class=n>db_session</span><span class=p>:</span> <span class=c1># 1</span>
<span class=k>return</span> <span class=p>{</span>
<span class=n>item</span><span class=o>.</span><span class=n>id</span><span class=p>:</span> <span class=p>{</span>
<span class=s1>'task'</span><span class=p>:</span> <span class=n>item</span><span class=o>.</span><span class=n>data</span><span class=p>,</span>
<span class=s1>'tags'</span><span class=p>:</span> <span class=p>[</span><span class=n>tag</span><span class=o>.</span><span class=n>id</span> <span class=k>for</span> <span class=n>tag</span> <span class=ow>in</span> <span class=n>item</span><span class=o>.</span><span class=n>tags</span><span class=p>]</span> <span class=c1># 3</span>
<span class=p>}</span>
<span class=k>for</span> <span class=n>item</span> <span class=ow>in</span> <span class=n>Todo</span><span class=o>.</span><span class=n>select</span><span class=p>()</span> <span class=c1># 2</span>
<span class=p>}</span>
</pre></div> <p>We are using a context manager, <code>orm.db_session</code> in <code class=coderef>1</code>. This treats everything that pony does within the code-block as a transaction, you don't have to worry about committing. We are going to return a list of all the items according to their <code>id</code>, we have the <code>'task'</code> value in the return dictionary provide the information regarding the <code>Todo</code> item. As for <code>'tags'</code> we loop through the values in the item's tags and give the <code>id</code>. Note that in <code class=coderef>2</code>, we are looping through <em>all</em> the <code>Todo</code> items in the database, <code>Todo.select()</code> is the same as <code>orm.select(tdo for tdo in Todo)</code>.</p> <p>However, the <code>id</code> of the tags alone are not enough information about the tags associated with a certain <code>Todo</code> item, it would be better if we could generate links to the tag pages. We can actually add a <code>property</code> inside <code>Tag</code> as <code>url</code>:</p> <div class=highlight><pre><span></span><span class=c1># Inside models.py</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
<span class=nd>@property</span>
<span class=k>def</span> <span class=nf>url</span><span class=p>(</span><span class=bp>self</span><span class=p>):</span>
<span class=k>return</span> <span class=s2>"http://localhost:5000/tags/{}"</span><span class=o>.</span><span class=n>format</span><span class=p>(</span><span class=bp>self</span><span class=o>.</span><span class=n>id</span><span class=p>)</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
</pre></div> <p>We use <code>property</code> because we will be able to access it as if it were any other attribute.</p> <p>Now that we have added this property, we can call the <code>url</code> property instead of the <code>id</code>:</p> <div class=highlight><pre><span></span><span class=c1># Inside app.py</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
<span class=s1>'tags'</span><span class=p>:</span> <span class=p>[</span><span class=n>tag</span><span class=o>.</span><span class=n>url</span> <span class=k>for</span> <span class=n>tag</span> <span class=ow>in</span> <span class=n>item</span><span class=o>.</span><span class=n>tags</span><span class=p>]</span> <span class=c1># 3</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
</pre></div> <p>Now that this is done, lets send a <code>GET</code> request to the <code>/</code> url:</p> <div class=highlight><pre><span></span>quazinafiulislam@Nafiuls-Mac: ~
$ http GET http://localhost:5000/
HTTP/1.0 <span class=m>200</span> OK
Content-Length: <span class=m>3</span>
Content-Type: application/json
Date: Thu, <span class=m>09</span> Oct <span class=m>2014</span> <span class=m>10</span>:32:00 GMT
Server: Werkzeug/0.9.6 Python/2.7.6
<span class=o>{}</span>
</pre></div> </div> <div class=section id=creating-objects> <h3><a class=toc-backref href=#id5>Creating Objects</a></h3> <p>So that (probably) works! We won't know until we've added a <code>Todo</code> item. We haven't inserted anything into our database yet so lets do that. We are going to be accepted <code>PUT</code> requests for our <code>/</code> route. In order to do this, we need to import the <code>json</code> module, as well as <code>flask.request</code>:</p> <div class=highlight><pre><span></span><span class=c1># Inside app.py</span>
<span class=kn>import</span> <span class=nn>json</span> <span class=c1># New import</span>
<span class=kn>from</span> <span class=nn>flask</span> <span class=kn>import</span> <span class=n>Flask</span><span class=p>,</span> <span class=n>request</span> <span class=c1># New import</span>
<span class=kn>import</span> <span class=nn>flask.ext.restful</span> <span class=kn>as</span> <span class=nn>rest</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
</pre></div> <p>Lets start by working on our <code>Todos.put</code> function. This is how the data is going to come in:</p> <div class=highlight><pre><span></span><span class=p>{</span>
<span class=nt>"data"</span><span class=p>:</span> <span class=s2>"Buy Milk!"</span><span class=p>,</span>
<span class=nt>"tags"</span><span class=p>:</span> <span class=p>[</span><span class=s2>"work"</span><span class=p>,</span> <span class=s2>"high"</span><span class=p>]</span>
<span class=p>}</span>
</pre></div> <p>In order to make this function work, we need to do a couple of things:</p> <ul class=simple> <li>Extract <code>data</code> and <code>tags</code> from the json request.</li> <li>Create a new <code>Todo</code> item.</li> <li>Create new tags, if tags exist, then you need to use them instead of creating new ones.</li> </ul> <p>In order to fulfill those roles, this is the function I ended up with:</p> <div class=highlight><pre><span></span><span class=c1># Inside app.py</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
<span class=k>def</span> <span class=nf>put</span><span class=p>(</span><span class=bp>self</span><span class=p>):</span>
<span class=sd>"""Payload contains information to create new todo item"""</span>
<span class=n>info</span> <span class=o>=</span> <span class=n>json</span><span class=o>.</span><span class=n>loads</span><span class=p>(</span><span class=n>request</span><span class=o>.</span><span class=n>data</span><span class=p>)</span> <span class=c1># 1</span>
<span class=k>with</span> <span class=n>orm</span><span class=o>.</span><span class=n>db_session</span><span class=p>:</span> <span class=c1># 2</span>
<span class=n>item</span> <span class=o>=</span> <span class=n>Todo</span><span class=p>(</span><span class=n>data</span><span class=o>=</span><span class=n>info</span><span class=p>[</span><span class=s1>'data'</span><span class=p>])</span> <span class=c1># 3</span>
<span class=k>for</span> <span class=n>tag_name</span> <span class=ow>in</span> <span class=n>info</span><span class=p>[</span><span class=s1>'tags'</span><span class=p>]:</span> <span class=c1># 4</span>
<span class=n>tag</span> <span class=o>=</span> <span class=n>Tag</span><span class=o>.</span><span class=n>get</span><span class=p>(</span><span class=n>name</span><span class=o>=</span><span class=n>tag_name</span><span class=p>)</span> <span class=c1># 5</span>
<span class=k>if</span> <span class=n>tag</span> <span class=ow>is</span> <span class=bp>None</span><span class=p>:</span> <span class=c1># 6</span>
<span class=n>tag</span> <span class=o>=</span> <span class=n>Tag</span><span class=p>(</span><span class=n>name</span><span class=o>=</span><span class=n>tag_name</span><span class=p>)</span> <span class=c1># 7</span>
<span class=n>item</span><span class=o>.</span><span class=n>tags</span> <span class=o>+=</span> <span class=n>tag</span> <span class=c1># 8</span>
<span class=k>return</span> <span class=p>{},</span> <span class=mi>200</span> <span class=c1># 9</span>
<span class=p>(</span><span class=o>...</span><span class=p>)</span>
</pre></div> <p>In <code class=coderef>1</code> we are loading the data from <code>request.data</code>, and turning that json string into a python dictionary. After doing so, we start our database session by using the <code>orm.db_session</code> context manager in <code class=coderef>2</code>.</p> <p>We start off in <code class=coderef>3</code> by creating a new <code>Todo</code> item. Note that our arguments are named/keyword arguments. We now loop over all the values in values in <code>info['tags']</code> using <code>tag_name</code> as the temporary variable in <code class=coderef>4</code>.</p> <p>In <code class=coderef>5</code>, we try to <em>retrieve</em> the tag if it exists in the database. If it doesn't <code class=coderef>6</code>, then we create a new one in <code class=coderef>7</code>. We then append this tag to the item's list of tags in <code class=coderef>8</code>.</p> <p>In <code class=coderef>9</code> we return an empty json object, with a 200 code, to say that everything happened smoothly. Note that in this entire function, it feels like there's <em>no database</em>. I just find this amazing that I feel as if I'm working with native python object stored in memory.</p> <div class=highlight><pre><span></span><span class=nx>quazinafiulislam</span><span class=err>@</span><span class=nx>Nafiuls</span><span class=o>-</span><span class=nx>Mac</span><span class=o>:</span> <span class=o>~</span>
<span class=nx>$</span> <span class=nx>http</span> <span class=nx>PUT</span> <span class=nx>http</span><span class=o>:</span><span class=c1>//localhost:5000/ data="Buy rice" tags:='["work", "high"]'</span>
<span class=nx>HTTP</span><span class=o>/</span><span class=mf>1.0</span> <span class=mi>200</span> <span class=nx>OK</span>
<span class=nx>Content</span><span class=o>-</span><span class=nx>Length</span><span class=o>:</span> <span class=mi>3</span>
<span class=nx>Content</span><span class=o>-</span><span class=nx>Type</span><span class=o>:</span> <span class=nx>application</span><span class=o>/</span><span class=nx>json</span>
<span class=nb>Date</span><span class=o>:</span> <span class=nx>Thu</span><span class=p>,</span> <span class=mi>09</span> <span class=nx>Oct</span> <span class=mi>2014</span> <span class=mi>11</span><span class=o>:</span><span class=mi>36</span><span class=o>:</span><span class=mi>04</span> <span class=nx>GMT</span>
<span class=nx>Server</span><span class=o>:</span> <span class=nx>Werkzeug</span><span class=o>/</span><span class=mf>0.9.6</span> <span class=nx>Python</span><span class=o>/</span><span class=mf>2.7.6</span>
<span class=p>{}</span>
</pre></div> <p>Yay! So that worked. Or did it? Lets go back to our home page at <code>/</code> and lets see whats up:</p> <div class=highlight><pre><span></span><span class=nx>quazinafiulislam</span><span class=err>@</span><span class=nx>Nafiuls</span><span class=o>-</span><span class=nx>Mac</span><span class=o>:</span> <span class=o>~</span>
<span class=nx>$</span> <span class=nx>http</span> <span class=nx>GET</span> <span class=nx>http</span><span class=o>:</span><span class=c1>//localhost:5000/</span>
<span class=nx>HTTP</span><span class=o>/</span><span class=mf>1.0</span> <span class=mi>200</span> <span class=nx>OK</span>
<span class=nx>Content</span><span class=o>-</span><span class=nx>Length</span><span class=o>:</span> <span class=mi>166</span>
<span class=nx>Content</span><span class=o>-</span><span class=nx>Type</span><span class=o>:</span> <span class=nx>application</span><span class=o>/</span><span class=nx>json</span>
<span class=nb>Date</span><span class=o>:</span> <span class=nx>Sat</span><span class=p>,</span> <span class=mi>11</span> <span class=nx>Oct</span> <span class=mi>2014</span> <span class=mi>02</span><span class=o>:</span><span class=mi>33</span><span class=o>:</span><span class=mi>24</span> <span class=nx>GMT</span>
<span class=nx>Server</span><span class=o>:</span> <span class=nx>Werkzeug</span><span class=o>/</span><span class=mf>0.9.6</span> <span class=nx>Python</span><span class=o>/</span><span class=mf>2.7.6</span>
<span class=p>{</span>
<span class=s2>"1"</span><span class=o>:</span> <span class=p>{</span>
<span class=s2>"tags"</span><span class=o>:</span> <span class=p>[</span>
<span class=s2>"http://localhost:5000/tags/1"</span><span class=p>,</span>
<span class=s2>"http://localhost:5000/tags/2"</span>
<span class=p>],</span>
<span class=s2>"task"</span><span class=o>:</span> <span class=s2>"Buy rice"</span>
<span class=p>}</span>
<span class=p>}</span>
</pre></div> <p>Now that we're done with the <code>Todo.get</code> and <code>Todo.put</code>, lets do <code>TodoItem.get</code>:</p> <div class=highlight><pre><span></span><span class=k>class</span> <span class=nc>TodoItem</span><span class=p>(</span><span class=n>rest</span><span class=o>.</span><span class=n>Resource</span><span class=p>):</span>
<span class=k>def</span> <span class=nf>get</span><span class=p>(</span><span class=bp>self</span><span class=p>,</span> <span class=n>todo_id</span><span class=p>):</span>
<span class=sd>"""</span>
<span class=sd> Get specific information on a Todo item</span>
<span class=sd> :param todo_id: The Todo Item's ID, which is unique and a primary key</span>
<span class=sd> :type todo_id: int</span>
<span class=sd> """</span>
<span class=k>try</span><span class=p>:</span>
<span class=k>with</span> <span class=n>orm</span><span class=o>.</span><span class=n>db_session</span><span class=p>:</span>
<span class=n>todo</span> <span class=o>=</span> <span class=n>Todo</span><span class=p>[</span><span class=n>todo_id</span><span class=p>]</span> <span class=c1># 1</span>
<span class=n>tags</span> <span class=o>=</span> <span class=p>[{</span><span class=n>tag</span><span class=o>.</span><span class=n>name</span><span class=p>:</span> <span class=n>tag</span><span class=o>.</span><span class=n>url</span><span class=p>}</span> <span class=k>for</span> <span class=n>tag</span> <span class=ow>in</span> <span class=n>todo</span><span class=o>.</span><span class=n>tags</span><span class=p>]</span> <span class=c1># 2</span>
<span class=k>return</span> <span class=p>{</span>
<span class=s2>"task"</span><span class=p>:</span> <span class=n>todo</span><span class=o>.</span><span class=n>data</span><span class=p>,</span>
<span class=s2>"tags"</span><span class=p>:</span> <span class=n>tags</span>
<span class=p>}</span>
<span class=k>except</span> <span class=n>orm</span><span class=o>.</span><span class=n>ObjectNotFound</span><span class=p>:</span> <span class=c1># 3</span>
<span class=k>return</span> <span class=p>{},</span> <span class=mi>404</span>
</pre></div> <p>Since we are getting <code>todo_id</code> from the request itself, we can use it to get the <code>Todo</code> item with that <code>id</code> in <code class=coderef>1</code>. This syntax is the same as <code>Todo.get(id=todo_id)</code>.</p> <p>In <code class=coderef>2</code>, we are are creating a list of dictionaries from the tags in <code>todo.tags</code>. We are using <code>tag.name</code> and <code>tag.url</code>. If you just wanted to get the names of of the tags, you could do either of these:</p> <div class=highlight><pre><span></span><span class=n>tag_names</span> <span class=o>=</span> <span class=nb>list</span><span class=p>(</span><span class=n>todo</span><span class=o>.</span><span class=n>tags</span><span class=o>.</span><span class=n>name</span><span class=p>)</span>
</pre></div> <div class=highlight><pre><span></span><span class=n>tag_names</span> <span class=o>=</span> <span class=p>[</span><span class=n>tag</span><span class=o>.</span><span class=n>name</span> <span class=k>for</span> <span class=n>tag</span> <span class=ow>in</span> <span class=n>todo</span><span class=o>.</span><span class=n>tags</span><span class=p>]</span>
</pre></div> <p>If you think I'm pulling your leg, you can check the <a class="reference external" href=https://github.com/gamesbrainiac/Pony-Todo-API/tree/83287fc4fd99ebb6dc9e613571695a8c835c46b9>source code</a> and run it for yourself. In <code class=coderef>3</code> if we cannot find the object, then we return a <code>404</code> error message back.</p> <p>Now on to <code>Tags.get</code>:</p> <div class=highlight><pre><span></span><span class=k>class</span> <span class=nc>Tags</span><span class=p>(</span><span class=n>rest</span><span class=o>.</span><span class=n>Resource</span><span class=p>):</span>
<span class=k>def</span> <span class=nf>get</span><span class=p>(</span><span class=bp>self</span><span class=p>):</span>
<span class=sd>"""Will show you all tags"""</span>
<span class=k>with</span> <span class=n>orm</span><span class=o>.</span><span class=n>db_session</span><span class=p>:</span>
<span class=k>return</span> <span class=p>{</span>
<span class=n>tag</span><span class=o>.</span><span class=n>name</span><span class=p>:</span> <span class=n>tag</span><span class=o>.</span><span class=n>url</span>
<span class=k>for</span> <span class=n>tag</span> <span class=ow>in</span> <span class=n>Tag</span><span class=o>.</span><span class=n>select</span><span class=p>()</span>
<span class=p>}</span>
</pre></div> <p>Nothing new here, very similar logic to our <code>/</code> url. Finally in our <code>TagItem.get</code> we have:</p> <div class=highlight><pre><span></span><span class=k>class</span> <span class=nc>TagItem</span><span class=p>(</span><span class=n>rest</span><span class=o>.</span><span class=n>Resource</span><span class=p>):</span>
<span class=k>def</span> <span class=nf>get</span><span class=p>(</span><span class=bp>self</span><span class=p>,</span> <span class=n>tag_id</span><span class=p>):</span>
<span class=sd>"""</span>
<span class=sd> Will show you information about a specific tag</span>
<span class=sd> :param tag_id: ID for the tag</span>
<span class=sd> :type tag_id: int</span>
<span class=sd> """</span>
<span class=k>try</span><span class=p>:</span>
<span class=k>with</span> <span class=n>orm</span><span class=o>.</span><span class=n>db_session</span><span class=p>:</span>
<span class=n>tag</span> <span class=o>=</span> <span class=n>Tag</span><span class=p>[</span><span class=n>tag_id</span><span class=p>]</span>
<span class=n>todos</span> <span class=o>=</span> <span class=nb>list</span><span class=p>(</span><span class=n>tag</span><span class=o>.</span><span class=n>todos</span><span class=o>.</span><span class=n>data</span><span class=p>)</span> <span class=c1># 1</span>
<span class=k>return</span> <span class=p>{</span>
<span class=s2>"tag"</span><span class=p>:</span> <span class=n>tag</span><span class=o>.</span><span class=n>name</span><span class=p>,</span>
<span class=s2>"tasks"</span><span class=p>:</span> <span class=n>todos</span>
<span class=p>}</span>
<span class=k>except</span> <span class=n>orm</span><span class=o>.</span><span class=n>ObjectNotFound</span><span class=p>:</span>
<span class=k>return</span> <span class=p>{},</span> <span class=mi>404</span>
</pre></div> <p>This is similar logic to that of <code>TodoItem.get</code>. The difference here is that in <code class=coderef>1</code>, we're getting all the data for the <code>todos</code> in a different way since we only need to know one thing. The code in <code class=coderef>1</code> is the same as:</p> <div class=highlight><pre><span></span><span class=n>todos</span> <span class=o>=</span> <span class=p>[</span><span class=n>tag</span><span class=o>.</span><span class=n>data</span> <span class=k>for</span> <span class=n>tag</span> <span class=ow>in</span> <span class=n>todo</span><span class=o>.</span><span class=n>tags</span><span class=p>]</span>
</pre></div> </div> <div class=section id=deleting-objects> <h3><a class=toc-backref href=#id6>Deleting Objects</a></h3> <p>All that's left is to find a way to delete objects from the database.</p> <div class=highlight><pre><span></span><span class=k>def</span> <span class=nf>delete</span><span class=p>(</span><span class=bp>self</span><span class=p>,</span> <span class=n>todo_id</span><span class=p>):</span>
<span class=k>try</span><span class=p>:</span>
<span class=k>with</span> <span class=n>orm</span><span class=o>.</span><span class=n>db_session</span><span class=p>:</span>
<span class=n>todo</span> <span class=o>=</span> <span class=n>Todo</span><span class=p>[</span><span class=n>todo_id</span><span class=p>]</span> <span class=c1># 1</span>
<span class=k>if</span> <span class=n>todo</span><span class=p>:</span>
<span class=n>tags</span> <span class=o>=</span> <span class=n>todo</span><span class=o>.</span><span class=n>tags</span><span class=o>.</span><span class=n>copy</span><span class=p>()</span> <span class=c1># 2</span>
<span class=n>todo</span><span class=o>.</span><span class=n>delete</span><span class=p>()</span> <span class=c1># 3</span>
<span class=k>for</span> <span class=n>tag</span> <span class=ow>in</span> <span class=n>tags</span><span class=p>:</span>
<span class=k>if</span> <span class=ow>not</span> <span class=n>tag</span><span class=o>.</span><span class=n>todos</span><span class=p>:</span> <span class=c1># 4</span>
<span class=n>tag</span><span class=o>.</span><span class=n>delete</span><span class=p>()</span> <span class=c1># 5</span>
<span class=k>except</span> <span class=n>orm</span><span class=o>.</span><span class=n>ObjectNotFound</span><span class=p>:</span>
<span class=k>return</span> <span class=p>{},</span> <span class=mi>400</span>
<span class=k>return</span> <span class=p>{},</span> <span class=mi>200</span>
</pre></div> <p>In <code class=coderef>1</code> we get the todo item like before. In <code class=coderef>2</code>, we copy the tags, because when we delete them in <code class=coderef>3</code>, we can no longer reference them through <code>todo.tags</code>, since the <code>todo</code> item is marked for deletion. If you do not do this, then you are going to get a <code>OperationWithDeletedObjectError</code>.</p> <p>In <code class=coderef>4</code> we check to see if the <code>tag</code> has todo items associated with it, if it doesn't then we delete the tag too in <code class=coderef>5</code>.</p> <p>Now, a demonstration of the API that we've built:</p> <div class="external youtube"><iframe width=640 height=360 src=//www.youtube.com/embed/LxssYsNPZZk frameborder=0></iframe></div></div> </div> <div class=section id=references> <h2><a class=toc-backref href=#id7>References</a></h2> <ul class=simple> <li><a class="reference external" href=http://ponyorm.com/ >Official Pony ORM documentation</a></li> <li><a class="reference external" href=http://flask-restful.readthedocs.org/en/latest/ >Official Flask-RESTful documentation</a></li> </ul> </div> <div class=section id=update> <h2><a class=toc-backref href=#id8>Update</a></h2> <p><em>Last Updated on 15 Oct 2014 7:00 PM</em></p> <p>I'd like to thank the pony ORM authors for their help in making this tutorial.</p> <p>You can also take a look at the <a class="reference external" href=https://github.com/gamesbrainiac/Pony-Todo-API/tree/Modularized>modularized version of the source code</a></p> </div> <div class=section id=p-s> <h2><a class=toc-backref href=#id9>P.S.</a></h2> <p>If you want to know what command line tool I used to make requests, I used <a class="reference external" href=https://github.com/jakubroztocil/httpie>httpie</a>.</p> </div> </div> </div> <div class=post-meta><span class=meta-type>Category: </span> <span><a href=http://nafiulis.me/category/programming.html>programming</a></span> <span class=meta-type> Tags: </span> <span> <a href=http://nafiulis.me/tag/python.html>python</a>, <a href=http://nafiulis.me/tag/pony.html>pony</a>, <a href=http://nafiulis.me/tag/flask.html>flask</a>, <a href=http://nafiulis.me/tag/orm.html>ORM</a>, <a href=http://nafiulis.me/tag/flask-restful.html>flask-restful</a> </span> </div> <div id=disqus_thread style="margin-top: 10px; margin-left: 20px; margin-right: 20px;"></div> <script type=text/javascript>
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'nafiulisme'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script> <noscript>Please enable JavaScript to view the <a href=http://disqus.com/?ref_noscript>comments powered by Disqus.</a></noscript> </div> <!--Footer--> <div id=footer> <footer> Code examples licenced under MIT License <br> Copyright <i class="fa fa-copyright"></i> 2018 Quazi Nafiul Islam </footer> </div> </div> <script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-55554110-1', 'auto');
ga('send', 'pageview');
</script> </body> </html>