├── screenshot.png
├── static
├── images
│ ├── spinner.gif
│ └── poweredby.png
└── css
│ └── main.css
├── main.py
├── models.py
├── views
├── base.mako
└── list.mako
├── controllers.py
└── README.asciidoc
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srackham/bottle-mongodb-example/HEAD/screenshot.png
--------------------------------------------------------------------------------
/static/images/spinner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srackham/bottle-mongodb-example/HEAD/static/images/spinner.gif
--------------------------------------------------------------------------------
/static/images/poweredby.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/srackham/bottle-mongodb-example/HEAD/static/images/poweredby.png
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from bottle import run, debug
3 |
4 | import controllers
5 |
6 | DEBUG = True
7 |
8 | debug(DEBUG)
9 | run(host='localhost', port=8080, reloader=DEBUG)
10 |
11 |
--------------------------------------------------------------------------------
/models.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from mongoengine import Document, connect
4 | from mongoengine import StringField, DateTimeField, FileField
5 |
6 | DB_NAME = 'bottle_mongodb_example_mongoengine'
7 |
8 | class Message(Document):
9 | nickname = StringField(required=True)
10 | text = StringField(required=True)
11 | date = DateTimeField(required=True, default=datetime.datetime.now)
12 | image_filename = StringField()
13 | image = FileField()
14 | thumb = FileField()
15 |
16 | connect(DB_NAME)
17 |
--------------------------------------------------------------------------------
/views/base.mako:
--------------------------------------------------------------------------------
1 | ## base.html
2 |
3 |
4 |
5 |
6 | ${self.attr.title}
7 |
8 |
14 |
15 |
16 | ${self.attr.title}
17 | ${self.body()}
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/static/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | max-width: 800px;
3 | font-family: Tahoma, Arial, sans-serif;
4 | }
5 |
6 | h1, h2 { color: green; }
7 | h1 { text-decoration: underline; }
8 | h2 { margin-bottom: 5px; }
9 |
10 | table {
11 | border: 2px solid green;
12 | background: #FFFFF8;
13 | -moz-box-shadow: 3px 3px 3px silver;
14 | -webkit-box-shadow: 3px 3px 3px silver;
15 | box-shadow: 3px 3px 3px silver;
16 | }
17 | table td {
18 | vertical-align: top;
19 | padding: 5px;
20 | }
21 | table#messages {
22 | width: 800px;
23 | border-collapse: collapse;
24 | }
25 | table#messages tr {
26 | height: 60px;
27 | }
28 | table#messages td {
29 | border: 1px solid green;
30 | }
31 | table#messages td.nickname {
32 | min-width: 120px;
33 | }
34 | table#messages td.text {
35 | min-width: 350px;
36 | }
37 | table#messages td.date {
38 | min-width: 150px;
39 | }
40 |
41 | textarea, input[type="text"], input[type="file"] {
42 | min-width: 300px;
43 | }
44 |
45 | img {
46 | border: none;
47 | }
48 | #poweredby img {
49 | width: 150px;
50 | }
51 | img#spinner {
52 | width: 15px;
53 | }
54 |
55 | #navigation {
56 | float: left;
57 | margin-top: 10px;
58 | }
59 | #navigation a {
60 | color: green;
61 | font-weight: bold;
62 | }
63 | #poweredby {
64 | float: right;
65 | margin-top: 5px;
66 | }
67 |
--------------------------------------------------------------------------------
/views/list.mako:
--------------------------------------------------------------------------------
1 | ## list.html
2 | <%inherit file="base.mako"/>
3 |
4 | <%!
5 | title = 'Bottle + MongoDB Example'
6 | %>
7 |
8 | ## Start body.
9 | New message
10 | ${self.create_form()}
11 | Messages
12 | ${self.messages_table()}
13 | ${self.footer()}
14 | ## End body.
15 |
16 | <%def name="create_form()">
17 |
42 | %def>
43 |
44 | <%def name="messages_table()">
45 |
46 | %for message in messages:
47 |
48 |
49 | %if message.image.grid_id is not None:
50 |
51 |
52 |
53 | %endif
54 | |
55 |
56 | ${message.nickname}
57 | |
58 |
59 | ${message.text}
60 | |
61 |
62 | ${message.date.strftime('%X %d %b %y')}
63 | |
64 |
65 | %endfor
66 |
67 | %def>
68 |
69 | <%def name="footer()">
70 |
71 | %if prev_page is not None:
72 |
< Prev
73 | %endif
74 | %if next_page is not None:
75 |
Next >
76 | %endif
77 |
78 |
79 |

80 |
81 | %def>
82 |
--------------------------------------------------------------------------------
/controllers.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import mimetypes
3 | import cStringIO as StringIO
4 |
5 | from bottle import request, response, get, post
6 | from bottle import static_file, redirect, HTTPResponse
7 | from bottle import mako_view as view
8 | from PIL import Image
9 | from pymongo.objectid import ObjectId
10 | from models import Message
11 |
12 | PAGE_SIZE = 5
13 |
14 | @get(['/', '/list', '/list/:page#\d+#'])
15 | @view('list.mako')
16 | def list(page=0):
17 | ''' List messages. '''
18 | page = int(page)
19 | prev_page = None
20 | next_page = None
21 | if page > 0:
22 | prev_page = page - 1
23 | if Message.objects.count() > (page + 1) * PAGE_SIZE:
24 | next_page = page + 1
25 | msgs = (Message.objects
26 | .order_by('-date')
27 | .skip(page * PAGE_SIZE)
28 | .limit(PAGE_SIZE))
29 | return {'messages': msgs,
30 | 'prev_page': prev_page,
31 | 'next_page': next_page,
32 | }
33 |
34 | @post('/create')
35 | def create():
36 | ''' Save new message. '''
37 | if not (request.POST.get('nickname') and request.POST.get('text')):
38 | redirect('/')
39 | msg = Message()
40 | msg.nickname = request.POST['nickname']
41 | msg.text = request.POST['text']
42 | if 'image' in request.files:
43 | upload = request.files['image']
44 | if not upload.filename.lower().endswith(
45 | ('.jpg', '.jpeg', '.png', '.bmp', '.gif')):
46 | redirect('/')
47 | mime = mimetypes.guess_type(upload.filename)[0]
48 | msg.image_filename = upload.filename
49 | # Save fullsize image
50 | msg.image.put(upload.file, content_type=mime)
51 | # Create and save thumbnail
52 | image = Image.open(msg.image)
53 | image.thumbnail((80, 60), Image.ANTIALIAS)
54 | data = StringIO.StringIO()
55 | image.save(data, image.format)
56 | data.seek(0)
57 | msg.thumb.put(data, content_type=mime)
58 | msg.save()
59 | redirect('/')
60 |
61 | @get('/:image_type#(image|thumb)#/:docid')
62 | def get_image(image_type, docid):
63 | ''' Send image or thumbnail from file stored in the database. '''
64 | f = Message.objects.with_id(ObjectId(docid))[image_type]
65 | response.content_type = f.content_type
66 | return HTTPResponse(f)
67 |
68 | @get('/static/:filename#.+#')
69 | def get_static_file(filename):
70 | ''' Send static files from ./static folder. '''
71 | return static_file(filename, root='./static')
72 |
--------------------------------------------------------------------------------
/README.asciidoc:
--------------------------------------------------------------------------------
1 | // Use this source for both GitHub README and blogpost.
2 | :blogpost-title: Bottle + MongoDB Example
3 | :blogpost-status: published
4 | :blogpost-doctype: article
5 | :blogpost-posttype: post
6 | :blogpost-categories: Bottle, MongoDB, Python
7 |
8 | = {blogpost-title}
9 |
10 | ifdef::blogpost[]
11 | *Published*: 2012-03-18
12 | endif::blogpost[]
13 |
14 | A didactic Web application written in Python to illustrate how to use
15 | the http://bottlepy.org/[Bottle] web-framework with the
16 | http://www.mongodb.org[MongoDB] database.
17 |
18 | It's a port I made of Mike Dirolf's
19 | https://github.com/mdirolf/djanMon[DjanMon] application (how to use
20 | Django with MongoDB).
21 |
22 | ifdef::blogpost[]
23 | // Wordpress processing instruction.
24 | pass::[]
25 | endif::blogpost[]
26 |
27 | http://bottlepy.org/[Bottle] is a wonderful micro web-framework, it's
28 | beautifully written and documented and has that ``just right'' feel
29 | about it. Bottle has support for multiple webservers and template
30 | engines -- I'm really enjoying it after working with Django.
31 |
32 | http://www.mongodb.org[MongoDB] is a schemaless document-oriented
33 | database designed for the Web. For me, working with MongoDB has been
34 | a revelation. Database creation, administration and migration (long
35 | the bane of database developers) is trivial in comparison with
36 | traditional SQL databases. Document storage management is baked into
37 | MongoDB and is used in many web applications (document storage has
38 | always been awkward and slow with relational databases).
39 |
40 | ifndef::blogpost[]
41 | This document is also published as a http://srackham.wordpress.com/2011/03/17/bottle-mongodb-example/[blogpost].
42 |
43 | Here's a https://github.com/srackham/bottle-mongodb-example/blob/master/screenshot.png[screenshot].
44 | endif::blogpost[]
45 |
46 | ifdef::blogpost[]
47 | You can find the source on https://github.com/srackham/bottle-mongodb-example[GitHub].
48 |
49 | Here's a screenshot:
50 |
51 | image::screenshot.png[]
52 | endif::blogpost[]
53 |
54 | To run the application install the <> then:
55 |
56 | git clone https://github.com/srackham/bottle-mongodb-example.git
57 | cd bottle-mongodb-example
58 | python bottle-mongodb-example.py
59 |
60 | The application creates and displays a paged list of user
61 | created messages which are stored in a MongoDB database along with
62 | uploaded images (no file system management required it's all in the
63 | database) -- not bad for 93 lines of Python code and a 79 line
64 | HTML template. The really neat thing is that you don't have to create
65 | the database, tables or schema -- it all happens automatically.
66 |
67 |
68 | [[X1]]
69 | == Prerequisites
70 | - Python.
71 | - MongoDB (see the http://www.mongodb.org/[MongoDB] website for
72 | install instructions).
73 | - PyMongo, the Python MongoDB driver:
74 |
75 | sudo easy_install pymongo
76 |
77 | - The Bottle web-framework:
78 |
79 | git clone https://github.com/defnull/bottle.git
80 | cd bottle
81 | python setup.py install
82 |
83 | I'm running in this environment -- it works for me but your mileage
84 | may vary:
85 |
86 | - Xubuntu 10.04
87 | - Bottle 0.9dev
88 | - Python 2.6.5
89 | - MongoDB 1.6.5
90 | - PyMongo 1.9
91 |
--------------------------------------------------------------------------------