38

I'm attempting to create a web service using MongoDB and Flask (using the pymongo driver). A query to the database returns documents with the "_id" field included, of course. I don't want to send this to the client, so how do I remove it?

Here's a Flask route:

@app.route('/theobjects')
def index():
    objects = db.collection.find()
    return str(json.dumps({'results': list(objects)}, 
        default = json_util.default,
        indent = 4))

This returns:

{
"results": [
    {
        "whatever": {
            "field1": "value", 
            "field2": "value", 
        }, 
        "whatever2": {
            "field3": "value"
        },
        ...
        "_id": {
            "$oid": "..."
        }, 

    ...
    }
]}

I thought it was a dictionary and I could just delete the element before returning it:

del objects['_id']

But that returns a TypeError:

TypeError: 'Cursor' object does not support item deletion

So it isn't a dictionary, but something I have to iterate over with each result as a dictionary. So I try to do that with this code:

for object in objects:
    del object['_id']

Each object dictionary looks the way I'd like it to now, but the objects cursor is empty. So I try to create a new dictionary and after deleting _id from each, add to a new dictionary that Flask will return:

new_object = {}
for object in objects:
    for key, item in objects.items():
        if key == '_id':
            del object['_id']
            new_object.update(object)

This just returns a dictionary with the first-level keys and nothing else.

So this is sort of a standard nested dictionaries problem, but I'm also shocked that MongoDB doesn't have a way to easily deal with this.

The MongoDB documentation explains that you can exclude _id with

{ _id : 0 }

But that does nothing with pymongo. The Pymongo documentation explains that you can list the fields you want returned, but "(“_id” will always be included)". Seriously? Is there no way around this? Is there something simple and stupid that I'm overlooking here?

llrs
  • 3,308
  • 35
  • 68
ddw
  • 391
  • 1
  • 3
  • 5
  • What is your actual claim here? Your code for creating a new dict is weird and not necessary. What is your real problem? –  Sep 10 '12 at 05:05
  • My problem was that .find({}, {'_id': False}) wasn't excluding _id. However it was a problem with my code and it's now working. Thanks for your help. – ddw Sep 10 '12 at 12:15
  • “but the objects cursor is empty”: that’s because you can iterate only once, so you’d need to iterate, get next dict, remove '_id', put dict into a list, and at the end return that list, not objects. – merwok Jun 27 '13 at 20:47

3 Answers3

118

To exclude the _id field in a find query in pymongo, you can use:

db.collection.find({}, {'_id': False})

The documentation is somewhat missleading on this as it says the _id field is always included. But you can exclude it like shown above.

Thomas
  • 2,608
  • 1
  • 13
  • 10
  • Thanks, this is correct. I had tried this before posting but I guess there was a problem with the string I was passing in the first parameter. I fixed that and now all is good. – ddw Sep 10 '12 at 12:20
2

Above answer fails if we want specific fields and still ignore _id. Use the following in such cases:

db.collection.find({'required_column_A':1,'required_col_B':1, '_id': False})
0

You are calling

del objects['_id']

on the cursor object!

The cursor object is obviously an iterable over the result set and not single document that you can manipulate.

for obj in objects:
     del obj['_id']

is likely what you want.

So your claim is completely wrong as the following code shows:

import pymongo

c = pymongo.Connection()
db = c['mydb']
db.foo.remove({})
db.foo.save({'foo' : 42})

for row in db.foo.find():
    del row['_id']
    print row



$ bin/python foo.py 

> {u'foo': 42}
  • Yep, I tried your code too before originally posting. But how would I then combine these nested dictionaries into one dictionary that can be passed to the client? Also, I realized that I was trying to del on a cursor object, that's why I said "So it isn't a dictionary, but something I have to iterate over with each result as a dictionary." Maybe I should have left out my discovery process but I thought the more info the better. – ddw Sep 10 '12 at 12:16