Use of multiple collections with Flask/Jinja2

Hello, everyone!

I am creating a MongoDB web application using pymongo, Flask and Jinja2.

I connect with the database and it’s three collections the usual way:

client = MongoClient(“mongodb://127.0.0.1:27017”)
db = client.polymers

# get the collections
inventories = db.inventory
classes = db.classes
vendors = db.vendors

One of first things I’ve noticed is that I can reference the inventories collection as inventories.find(), etc. With the other two collections, I need to prepend db. and reference them as db.classes.find() and db.vendors.find().

While I thought that was strange enough, everything worked well in my application until…

I use Flask and Jinja2 to bind variables to the the HTML files. When I pass a variable from a query via the find() method to an HTML file via the render_template() method. However, when passing a variable with a parameter supplied

class_id = request.values.get(“_id”)
class_edit = db.classes.find({“_id”: ObjectId(class_id)})

It seems to return a null value. This is the true where I have to prepend db. on the classes and vendors collections.

I tried the find_one() method, but received an internal server error in the {{ for class in classes }} Jinja2 template within the HTML file that is rendered.

I would appreciate any advice. Thanks!

Mike.

Hi Mike,

Regarding the first issue - I am not able to reproduce this issue (needing to prepend db to some namespaces but not others) on my end. Can you provide a small repro script so that we can better assist you? Also, which version of Python and which version of the driver are you using?

Regarding the second issue - I am not sure why the find_one() method is not working for you but your instinct to use that method is correct. The find() method returns Cursor and not a document itself whereas the find_one method returns a single document or None. To extract documents from the cursor returned by find(), you must iterate over it or use index notation (e.g. db.coll.find({'_id': 'foo'})[0]). If you still face issues after this, please share some sample documents from the db.classes collection and also examples of what your request objects look like to help in diagnose the problem.

Hi Prashant:

Thanks for getting back to me so quickly!

I am using Python 3.7.3 and pymongo 3.10.1.

For my first issue, consider the following:

**client = MongoClient(“mongodb://127.0.0.1:27017”) **
db = client.polymers # get the database
# get the collections
inventories = db.inventory
classes = db.classes
vendors = db.vendors

@app.route("/")
def root():
** # display the list of polymers in the inventory collection i **
** inventory_list = inventories.find()**
** a1 = “active”**
** return render_template(‘index.html’, a1=a1, inventories=inventory_list, t=title, h=heading)**

@app.route("/classes")
def classes():
** # display the list of classes from the classes collection**
** class_list = db.classes.find()
** a2 = "active"

** return render_template(‘classes.html’, a2=a2, classes=class_list, t=title, h=heading)**

Notice that I had to prepend the call to classes.find() with db. I have a similar function for the vendors collection where I had to prepend with db. on the call to vendors.find(). However, I didn’t have to do that with the call to inventories.find(). Could it have something to do with the first connection to a collection in my code is the inventories collection? If I leave out the db. with the calls to classes.find() and vendors.find(), I get the Internal Server Error in the browser and the terminal window error message ends in:

File “app.py”, line 37, in classes
class_list = classes.find()
AttributeError: ‘function’ object has no attribute ‘find’ 1

For my second issue, consider the following:

@app.route("/edit")
def edit():
** polymer_id = request.values.get("_id")**
** print("–> polymer id: “, polymer_id)**
** inventory_edit = inventories.find({”_id": ObjectId(polymer_id)})**
** return render_template(‘edit.html’, inventories=inventory_edit, h=heading, t=title)**

@app.route("/edit-class")
def edit_class():
** class_id = request.values.get("_id")**
** print("–> class id: “, class_id)**
** class_edit = db.classes.find({”_id:": ObjectId(class_id)})**
** return render_template(‘edit-class.html’, classes=class_edit, h=heading, t=title)**

As you can see, the edit() function uses the find() method containing an ObjectId that renders the edit.html file as expected. I am presented with a form to edit the document corresponding to the ObjectId. Awesome!

However, the edit_class() function also uses the find() method containing an ObjectId does not render the edit-class.html file as expected. Nothing crashes, but the form and table within the {% for class in classes %}{% endfor %} is not processed.

Note that I added print statements in the two functions. I get an ObjectId back from each HTML. The generated URL, /edit-class?_id= is also correct, so I suspect there is something in the call to the find() method containing the ObjectId. This works in the MongoDB shell, but not in the app.

I tried find_one() function, but I totally forgot about it returning a document as opposed to a cursor. I believe I got an Internal Server Error with that, but it makes sense now since I was trying to process a result set instead of a document.

I would appreciate any advice! Thanks!

Mike.

The problem is that the method named “classes” conflicts with the “classes” global variable:

classes = db.classes
...
@app.route("/classes")
def classes():
    ...

You’ll need to rename one of these.

1 Like

Hi Shane:

Thanks for that suggestion! I will indeed change that, but I have the same issue with a similar function called vendors() and edit_vendor().

I will make the classes change to see if that will fix everything.

Mike.