Sample Application¶
The mongo_objects
source code
as well as the source distribution package include a sample Flask
application that demonstrates many common usage patterns.
Starting The Sample Application¶
The sample/run-sample-app
shell script makes the following
assumptions:
The bash shell is available at
/bin/bash
.python3 and pip3 are available on the path with access to the Internet.
MongoDB is installed as mongod but not running.
The
sample
directory is writable.
The script performs the following actions:
Creates a Python 3 virtual environment in the
sample/venv
directory and installs the required modules as listed insample/requirements.txt
.Creates a
sample/db
directory and starts mongod in unsecured mode against that directory. To override this step and use a different MongoDB instance, provide the connect string in theMONGO_CONNECT_URI
environment variable.Starts flask running the
sample/mongo_objects_sample.py
sample application moduleShuts down mongod when flask terminates.
Press Control-C to terminate flask and end the script. For example:
% sample/run-sample-app
created new virtual environment for mongo_objects sample app
activated mongo_objects sample app virtual environment
Collecting Flask
Collecting Flask-PyMongo
Collecting Flask-WTF
Collecting MarkupSafe
Collecting pymongo
Collecting WTForms
Collecting Werkzeug>=3.0.0 (from Flask)
Collecting Jinja2>=3.1.2 (from Flask)
Collecting itsdangerous>=2.1.2 (from Flask)
Collecting click>=8.1.3 (from Flask)
Collecting blinker>=1.6.2 (from Flask)
Collecting dnspython<3.0.0,>=1.16.0 (from pymongo)
...
Installing collected packages: MarkupSafe, itsdangerous, dnspython, click, blinker, WTForms, Werkzeug, pymongo, Jinja2, Flask, Flask-WTF, Flask-PyMongo
Successfully installed Flask-3.0.3 Flask-PyMongo-2.3.0 Flask-WTF-1.2.1 Jinja2-3.1.4 MarkupSafe-2.1.5 WTForms-3.1.2 Werkzeug-3.0.3 blinker-1.8.2 click-8.1.7 dnspython-2.6.1 itsdangerous-2.2.0 pymongo-4.7.2
installed required modules for mongo_objects sample app virtual environment
Started MongoDB PID 66702
* Serving Flask app 'mongo_objects_sample'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 942-992-575
^CStopped MongoDB
Application Overview¶
The sample Flask application manages ticket sales to imaginary events.
There are two modes. In admin mode, you manage events while in customer mode you may view events and “purchase” tickets. Use the dropdown menu in the navigation bar to switch between modes.
The data for the entire application is stored in a single MongoDB collection
named events
in the mongo_objects_sample
database.
Each event including related information like venue location, types of tickets
and tickets sold is stored as a single MongoDB document.
We recommend installing the MongoDB Compass tool
to inspect the documents managed by mongo_objects
.
MongoUserDict: Scheduling An Event¶
Events are stored as MongoUserDict
subclasses called Event
. Use admin
mode to create your first event as follows:

Now use Compass to view the contents of the events
collection.
There should be one document similar to the following:
{
"_id": "663c54bbeba88ab288f3bd3d",
"name": "End Of The Century Party",
"description": "This will be even more fun than last time!",
"eventDate": "2099-12-31T00:00:00.000Z",
"_updated": "2024-05-09T04:44:43.030Z",
"_created": "2024-05-09T04:44:43.030Z"
}
This is a typical MongoDB document dictionary, although you’ll notice
MongoUserDict
helpfully added _created and _updated
timestamps to the document when it was saved.
If you look at the event details page in the sample app, you’ll see
a URL similar to http://127.0.0.1:5000/admin-event-detail/663c54bbeba88ab288f3bd3d
.
The unique number in the URL is just the MongoDB ObjectId.
The MongoUserDict.id()
and MongoUserDict.load_by_id()
methods to make it easy to locate
documents by their ObjectId.
Because events are actually represented using a MongoUserDict
subclass
named Event
, we have added our own methods to assist with the event
workflow. displayDate()
provides consistent date formatting while
isSoldOut()
provides a single calculation for ticket availability.
By passing an Event
object to another function or even to a Jinja
template, not only the event data but also all these methods are available
as well.
MongoSingleProxy: Adding A Venue¶
The venue location is stored as a simple subdocument dictionary within the
parent event document. We use a MongoSingleProxy
subclass named
Venue
to manage this data structure.
Use the admin mode to add venue information to the event as follows:

Check the MongoDB document with Compass:
{
"_id": "663c54bbeba88ab288f3bd3d",
"name": "End Of The Century Party",
"description": "This will be even more fun than last time!",
"eventDate": "2099-12-31T00:00:00.000Z",
"_updated": "2024-05-09T07:07:18.207Z",
"_created": "2024-05-09T04:44:43.030Z",
"venue": {
"name": "The Grand Theater",
"address": "123 Grand Avenue",
"phone": "123 456 7890"
}
}
You’ll notice that the _updated field has indeed been updated to reflect the last document save time.
There’s also a new venue dictionary within the main document.
This represents the Venue
proxy object. The venue name
came from the container_name in the class definition.
If there are any venue-specific methods, we could include them in
the Venue
class instead of cluttering up Event
.
Since the data for each proxy object only exists in the parent
MongoDB document, the parent document must be loaded before the
proxy is available. We’ve included a helpful Event.getVenue()
method
to return the Venue
proxy object from a Event
already loaded into memory.
Each proxy object has a unique ID that includes the parent document
ObjectId. There’s an Event.loadVenueById()
class method that
parses the venue ID, loads the correct Event
document and
returns the appropriate Venue
proxy object.
MongoDictProxy: Defining Ticket Types¶
For “one-to-many” relationships, SQL statements require table joins to connect the information. In No-SQL databases like MongoDB, it is more efficient to simply include the “many” items as multiple subdocuments within the same primary document.
The MongoDictProxy
class manages a single dictionary container
that in turn holds multiple subdocument dictionaries of the same type.
In our sample app, for example, each event may have multiple ticket
types for sale. Instead of a separate “ticket type” collection, all
ticket types are included as TicketType
subdocuments
within the main Event
object.
To illustrate this functionality, use the admin mode to add three ticket types to our event.
VIP tickets:

General admission:

Discounted student tickets:

Use MongoDB Compass to check the document content:
{
"_id": "663c54bbeba88ab288f3bd3d",
"name": "End Of The Century Party",
"description": "This will be even more fun than last time!",
"eventDate": "2099-12-31T00:00:00.000Z",
"_updated": "2024-05-09T07:21:59.145Z",
"_created": "2024-05-09T04:44:43.030Z",
"venue": {
"name": "The Grand Theater",
"address": "123 Grand Avenue",
"phone": "123 456 7890"
},
"_last_unique_integer": 3,
"ticketTypes": {
"1": {
"name": "VIP Ticket",
"description": "The best seats in the house!",
"cost": 200,
"ticketsTotal": 10
},
"2": {
"name": "General Admission",
"description": "Everyone is welcome!",
"cost": 100,
"ticketsTotal": 100
},
"3": {
"name": "Student Ticket",
"description": "Take a break from the books",
"cost": 50,
"ticketsTotal": 50
}
}
}
The biggest change is the new ticketTypes dictionary. The name
came from the container_name attribute of the TicketType
class.
The ticketTypes dictionary itself contains three dictionaries,
one for each of our TicketType
proxy objects. The keys
for each dictionary were auto-assigned by MongoDictProxy
and have nothing to do with the content of the subdocument. Even
if the content changes, the key remains the same.
A _last_unique_integer value has also been added automatically to track on a per-document basis the last proxy ID value that was issued.
Of course, the _updated timestamp was also updated.
If you use the admin mode to explore ticket type details, you’ll
see a URL like http://127.0.0.1:5000/admin-ticket-detail/663c54bbeba88ab288f3bd3dg1
.
Each proxy object has a unique ID consisting of the document ObjectId plus the
proxy subdocument key. g
is used as a URL-safe, non-hexidecimal separator.
Each proxy object provides a id()
method that constructs the unique ID.
MongoUserDict
provides load_proxy_by_id()
to parse a proxy object
ID, load the correct parent document and return the appropriate subdocument
object. In fact, our Event
class method loadTicketTypeById()
just calls load_proxy_by_id()
to locate and return TicketType
objects.
Once an Event
object is loaded, getTicketTypes()
has been
included to call MongoUserDict.get_proxies()
and return a list
of all TicketType
proxies. The method getTicketType()
can be called
to create a proxy for a single ticket type based on the key.
MongoListProxy: Selling Tickets¶
Another pattern for saving “one-to-many” subdocuments in the
parent document is to use a list as the container. MongoListProxy
supports using lists as a container object.
Our sample app uses a MongoListProxy
subclass called
Ticket
to represent each ticket sold.
Use the customer mode of the sample app to purchase a VIP ticket to our end-of-the-century party.

Let’s look at the actual data with Compass:
{
"_id": "663c54bbeba88ab288f3bd3d",
"name": "End Of The Century Party",
"description": "This will be even more fun than last time!",
"eventDate": "2099-12-31T00:00:00.000Z",
"_updated": "2024-05-09T08:56:25.171Z",
"_created": "2024-05-09T04:44:43.030Z",
"venue": {
"name": "The Grand Theater",
"address": "123 Grand Avenue",
"phone": "123 456 7890"
},
"_last_unique_integer": 4,
"ticketTypes": {
"1": {
"name": "VIP Ticket",
"description": "The best seats in the house!",
"cost": 200,
"ticketsTotal": 10
},
"2": {
"name": "General Admission",
"description": "Everyone is welcome!",
"cost": 100,
"ticketsTotal": 100
},
"3": {
"name": "Student Ticket",
"description": "Take a break from the books",
"cost": 50,
"ticketsTotal": 50
}
},
"tickets": [
{
"name": "Fred",
"ticketTypeKey": "1",
"issued": "2024-05-09T08:56:25.171Z",
"_sdkey": "4"
}
]
}
The major change is the new tickets list containing a single subdocument dictionary representing the ticket we just purchased for Fred.
Each MongoDictProxy
and MongoListProxy
proxy object
is tracked by a key. Since lists don’t natively use keys, a _sdkey
(short for subdocument key) value has been added to the dictionary
in the tickets list.
This key value is used when constructing the full proxy ID. In fact, if you switch back to admin mode and look at the VIP tickets detail page, you’ll see that Fred’s ticket number is just the proxy ID.

The key is also used to track the subdocument dictionary within the
list. If the list is modified, MongoListProxy
will use the
key to locate the new index of the subdocument dictionary in the list.
Because a new key has been issued, _last_unique_integer has been updated as well as the _updated timestamp.
PolymorphicMongoDictProxy: Ticket Benefits¶
It to sometimes convenient to store similar but not identical types
of information within the same container. All mongo_objects
proxy classes have polymorphic versions that will return different
objects from the same container based on an identifier.
Our sample app uses a tree of PolymorphicMongoDictProxy
subclasses
to track the benefits associated with each ticket type. Some benefits
are considered intangible features like a front row seat or unobstructed view.
Other benefits are tangible gifts like free popcorn or a gift bag.
This data structure is defined as follows:
class Benefit( mongo_objects.PolymorphicMongoListProxy ):
container_name = 'benefits'
proxy_subclass_map = {}
class Feature( Benefit ):
proxy_subclass_key = 'ft'
class Gift( Benefit ):
proxy_subclass_key = 'gt'
The Benefit
base class defines the container_name that the
two subclasses will share. The Feature
and Gift
subclasses each define a unique proxy_subclass_key value that
PolymorphicMongoDictProxy
uses to decide which class to
instantiate.
Use admin mode to add a feature to the VIP ticket type:

Now add a gift to the VIP ticket type:

Let’s look at the data:
{
"_id": "663c54bbeba88ab288f3bd3d",
"name": "End Of The Century Party",
"description": "This will be even more fun than last time!",
"eventDate": "2099-12-31T00:00:00.000Z",
"_updated": "2024-05-09T09:38:22.027Z",
"_created": "2024-05-09T04:44:43.030Z",
"venue": {
"name": "The Grand Theater",
"address": "123 Grand Avenue",
"phone": "123 456 7890"
},
"_last_unique_integer": 6,
"ticketTypes": {
"1": {
"name": "VIP Ticket",
"description": "The best seats in the house!",
"cost": 200,
"ticketsTotal": 10,
"benefits": [
{
"name": "Front Row Seat",
"description": "Guaranteed unobstructed view.",
"_psckey": "ft",
"_sdkey": "5"
},
{
"name": "Panda Plushie",
"description": "Cute and cuddly",
"value": 20,
"_psckey": "gt",
"_sdkey": "6"
}
]
},
"2": {
"name": "General Admission",
"description": "Everyone is welcome!",
"cost": 100,
"ticketsTotal": 100
},
"3": {
"name": "Student Ticket",
"description": "Take a break from the books",
"cost": 50,
"ticketsTotal": 50
}
},
"tickets": [
{
"name": "Fred",
"ticketTypeKey": "1",
"issued": "2024-05-09T08:56:25.171Z",
"_sdkey": "4"
}
]
}
The “VIP Ticket” subdocument in the tickets dictionary now has a benefits
list containing two subdocuments of its own. mongo_objects
proxy objects
support nesting as many levels as you like and in any order. In this case,
a MongoListProxy
is nested within a MongoDictProxy
.
The method MongoUserDict.id()
builds IDs for nested proxies and the
class method MongoUserDict.load_proxy_by_id()
loads nested proxies.
This functionality is used to implement Event.loadBenefitById()
.
If you update the Front Row Seat feature added above, you’ll see an example
of a nested proxy ID in the URL, for example,
http://127.0.0.1:5000/admin-update-feature/663c54bbeba88ab288f3bd3dg1g5
.
Notice that each subdocument has a _psckey value that matches the
proxy_subclass_key value of the Benefit
subclass to be
instantiated for that particular subdocument.
There’s an important distinction to observe when calling get_proxies()
for polymorphic proxies:
If you call the base class, you will get all proxies in the container, each with the correct class. In the example above
Benefit.get_proxies()
will return a list with two objects, oneFeature
and oneGift
.If you call a subclass, you will only get proxies of that type. For example,
Feature.get_proxies()
will return a list containing a singleFeature
object containing the Front Row Seat subdocument.
That’s A Wrap¶
Take a closer look at the sample/mongo_objects_sample.py
to see the
interplay between MongoUserDict
parent documents and the three
subdocument proxy types.
Observe some commong coding patterns. For example, since proxy objects can
only exist after the parent document is loaded, proxy loading methods like
loadBenefitById()
, loadTicketTypeById()
and
loadVenueById()
are usually class methods of the parent document class
Event
.
Likewise, proxy creation method like getTicket()
, getTickets()
,
getTicketsByType()
, getTicketType()
, getTicketTypes()
and
getVenue()
are placed in the parent document class as well.
Refer to the rest of the documentation for information on other features like
PolymorphicMongoUserDict
polymorphic documents.