cloudant / python-cloudant

A Python library for Cloudant and CouchDB
Apache License 2.0
163 stars 55 forks source link

Make query()['docs'] contain cloudant.document.Document instead of dict #417

Closed rominf closed 5 years ago

rominf commented 5 years ago

Please read these guidelines before opening an issue.

Bug Description

1. Steps to reproduce and the simplest code sample possible to demonstrate the issue

from cloudant.client import CouchDB
from cloudant.query import Query

couchdb = CouchDB('admin', 'password', url='http://127.0.0.1:5984', connect=True)
couchdb.create_database('todos')
todos = couchdb['todos']
todos.create_document({'what': 'Show the query + update usecase', 'done': False})
query = Query(todos, selector={'what': 'Show the query + update usecase'})
doc = query()['docs'][0]
type(doc)  # dict
doc = todos[doc['_id']]
type(doc) # cloudant.document.Document
doc['done'] = True
doc.save()

2. What you expected to happen

As you see, if I want to update some queried document, first I have to extract it again as cloudant.document.Document using:

database[doc['_id']]

It's not convenient. Why documents are not extracted as cloudant.document.Document when I call query?

3. What actually happened

Document are extracted as dict.

Environment details

emlaver commented 5 years ago

Hi @rominf, please use the recommended get_query_result(...) function in the Database module. There are some documented examples of iterating docs returned from it.

Something like this should work:

todos = couchdb['todos']
selector = {'what': 'Show the query + update usecase'}
result = todos.get_query_result(selector)
for doc in result:
   doc['done'] = True
   doc.save()
rominf commented 5 years ago

@emlaver, thank you. That's what I needed. Why doesn't this example written in "Getting started"? I suggest to add it there.

rominf commented 5 years ago

It's very strange, but now it doesn't work, as I expect:

from cloudant.client import CouchDB
couchdb = CouchDB('admin', 'password', url='http://127.0.0.1:5984', connect=True)
menu_plan_types = couchdb['menu_plan_types']
selector = {'name': 'business'}
for menu_plan_type in menu_plan_types.get_query_result(selector):
    print(type(menu_plan_type))
# <class 'dict'>

When I try to save, I get an exception of course. Why it's not working stabily?

emlaver commented 5 years ago

@rominf What's the exception you are getting?

rominf commented 5 years ago

@emlaver, I'm getting:

AttributeError: 'dict' object has no attribute 'save'

Because it's a dict, but not Document.

Is there a way to force this function always return collection of Documents?

emlaver commented 5 years ago

@rominf you'll need to create a Document object (passing in the Database object and doc id), update the doc, then save:

for doc in result:
   # Copy doc
   updated_doc = Document(todos, doc['_id'])
   updated_doc.update(doc)
   # Update doc
   updated_doc['done'] = True
   # Save
   updated_doc.save()

You can't force this function to return a Document object because we can't guarantee that the query result is a complete document because of field projection. Because of this, it doesn't make sense to always return document objects.

rominf commented 5 years ago

Ok, thank you for an answer, @emlaver. Can you please include this into documentation (as it's not obvious)?