Method createIndex() not callable on a collection - python-3.x

In the doc of the createIndex they say db.collection.createIndex(keys, options) so i call createIndex() with the code below. The name of my database is articles and the name of the collection is bce. Inside bce there is already a document with the field article.
class Stockage():
def __init__(self):
self.connexion()
def connexion(self):
client = pymongo.MongoClient("localhost", 27017)
self.bce = client.articles.bce
sql = Stockage()
sql.bce.createIndex({article : "text"})
And i have the following error :
Traceback (most recent call last):
File "<ipython-input-1132-fc8762d315d1>", line 1, in <module>
sql.bce.createIndex({article : "text"})
File "C:\ProgramData\Anaconda3\lib\site-packages\pymongo\collection.py", line 3321, in __call__
self.__name.split(".")[-1])
TypeError: 'Collection' object is not callable. If you meant to call the 'createIndex' method on a 'Collection' object it is failing because no such method exists.
Is not bce a collection ?

This is because in Pymongo the method is called create_index() instead of createIndex() as it is named in the mongo shell.
It also has a different format for the parameter compared to its mongo shell counterpart. Instead of accepting a document/dict as index specification, it accepts a list of tuples instead. This is because you cannot guarantee the ordering of dict keys in Python.
So the correct line should be:
sql.bce.create_index([("article", pymongo.TEXT)])
More details are available in the Pymongo Collection page.

Related

Why calling method for two times result in TypeError: object is not callable

Define two class based on BaseHandler as below:
class BaseHandler:
def successor(self, successor):
self.successor = successor
class ScoreHandler1(BaseHandler):
pass
class ScoreHandler2(BaseHandler):
pass
Initialize two instances:
h1 = ScoreHandler1()
h2 = ScoreHandler2()
Call successor method first time :
h1.successor(h2)
Now call it second time:
h1.successor(h2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'ScoreHandler2' object is not callable
Why can't call the method more times?
callable(ScoreHandler2)
True
The first time you call h1.successor(h2), you're calling the method named "successor". Inside this method, you set the attribute named "successor" to the object h2.
The second time you call h1.successor(h2), you're calling the attribute named "successor", which you defined previously to h2. Since ScoreHandler2 does not implement __call__, it'll raise an error.
To fix this, avoid naming attributes with the same name as methods.

Django model object as parameter for celery task raises EncodeError - 'object of type someModelName is not JSON serializable'

Im working with a django project(im pretty new to django) and running into an issue passing a model object between my view and a celery task.
I am taking input from a form which contains several ModelChoiceField fields and using the selected object in a celery task. When I queue the task(from the post method in the view) using someTask.delay(x, y, z) where x, y and z are various objects from the form ModelChoiceFields I get the error object of type <someModelName> is not JSON serializable.
That said, if I create a simple test function and pass any of the same objects from the form into the function I get the expected behavior and the name of the object selected in the form is logged.
def test(object):
logger.debug(object.name)
I have done some poking based on the above error and found django serializers which allows for a workaround by serializing the object using serializers.serialize('json', [template]), in the view before passing it to the celery task.
I can then access the object in the celery task by using template = json.loads(template)[0].get('fields') to access its required bits as a dictionary -- while this works, it does seem a bit inelegant and I wanted to see if there is something I am missing here.
Im obviously open to any feedback/guidance here however my main questions are:
Why do I get the object...is not JSON serializable error when passing a model object into a celery task but not when passing to my simple test function?
Is the approach using django serializers before queueing the celery task considered acceptable/correct or is there a cleaner way to achieve this goal?
Any suggestions would be greatly appreciated.
Traceback:
I tried to post the full traceback here as well however including that caused the post to get flagged as 'this looks like spam'
Internal Server Error: /build/
Traceback (most recent call last):
File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/kombu/serialization.py", line 49, in _reraise_errors
yield
File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/kombu/serialization.py", line 220, in dumps
payload = encoder(data)
File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 65, in dumps
return _dumps(s, cls=cls or _default_encoder,
File "/usr/lib/python3.8/json/__init__.py", line 234, in dumps
return cls(
File "/usr/lib/python3.8/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python3.8/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 55, in default
return super().default(o)
File "/usr/lib/python3.8/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type Template is not JSON serializable
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
Add this lines to settings.py
# Project/settings.py
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
Then instead of passing object, send JSON with id/pk if you're using a model instance call the task like this..
test.delay({'pk': 1})
Django model instance is not available in celery environment, as it runs in a different process
How you can get the model instance inside task then? Well, you can do something like below -
def import_django_instance():
"""
Makes django environment available
to tasks!!
"""
import django
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Project.settings')
django.setup()
# task
#shared_task(name="simple_task")
def simple_task(data):
import_django_instance()
from app.models import AppModel
pk = data.get('pk')
instance = AppModel.objects.get(pk=pk)
# your operation

'DocumentReference' object has no attribute 'to_dict'

I'm using python3 to query a database on firestore. My code is the following:
def getWebConsultReData():
collection = db.collection("webconsult_threescrap").where("compositeKey", "==", "10004-5327729026")
docs = [snapshot.reference for snapshot in collection.stream()]
mDictList = []
print(docs)
for doc in docs:
formattedData = doc.to_dict()
getWebConsultReData()
However, I got the following error:
[<google.cloud.firestore_v1.document.DocumentReference object at 0x7f2a183413c8>]
Traceback (most recent call last):
File "<ipython-input-42-172d5765da1d>", line 9, in <module>
getWebConsultReData()
File "<ipython-input-42-172d5765da1d>", line 7, in getWebConsultReData
formattedData = doc.to_dict()
AttributeError: 'DocumentReference' object has no attribute 'to_dict'
What I'm sure are:
The filter is valid, in fact, the following snapshot shows the exact syntax with GUI
Also the document exists.
Can anybody help me out? Thank you very much!
Your problem is that at the point of the error, docs is an array of DocumentReferences and not an array of DocumentSnapshots because you remove all the other data a few lines up. DocumentReference only gives a path to the document, it does not contain the entirety of the document's data.
To do something equivalent (gather an array of DocumentReferences but also have some access to the actual documents), you'd need to do something like this:
def getWebConsultReData():
collection = db.collection("webconsult_threescrap").where("compositeKey", "==", "10004-5327729026")
docs = []
for doc in collection.stream():
formattedData = doc.to_dict()
print(formattedData)
docs.append(doc.reference)
print(docs)
getWebConsultReData()
If it helps, you can also review the example in the documentation of how to get multiple documents from a collection.
from google.cloud import firestore
def tableconversations(userdata):
docarray = []
db = firestore.Client()
collection = db.collection('tableQuestions').where('userID', '==', userdata['ID']).get()
print("user history matched records",collection)
for doc in collection:
print(doc.to_dict())
docarray.append(doc.to_dict())
return docarray

pymodm can't find an object, while pymongo successfully finds it

I have a problem getting an object from the mongodb instance. If I search for this object with pymongo interface, everything is fine - object can be found. If try to do the very same thing with pymodm - it fails with error.
Here is what I'm doing:
from pymodm import connect, MongoModel, fields
from pymongo import MongoClient
class detection_object(MongoModel):
legacy_id = fields.IntegerField()
client = MongoClient(MONGODB_URI)
db = client[MONGODB_DEFAULT_SCHEME]
collection = db['detection_object']
do = collection.find_one({'legacy_id': 1437424})
print(do)
connect(MONGODB_URI)
do = detection_object.objects.raw({'legacy_id': 1437424}).first()
print(do)
The first print outputs this: {'_id': ObjectId('5c4099dcffa4fb11494d983d'), 'legacy_id': 1437424}. However, during the execution of this command: do = detection_object.objects.raw({'legacy_id': 1437424}).first() interpreter fails with the following error:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/pymodm/queryset.py", line 127, in first
return next(iter(self.limit(-1)))
StopIteration
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/konsof01/PycharmProjects/testthisfuckingshit/settings.py", line 29, in <module>
do = detection_object.objects.raw({'legacy_id': 1437424}).first()
File "/usr/local/lib/python3.7/site-packages/pymodm/queryset.py", line 129, in first
raise self._model.DoesNotExist()
__main__.DoesNotExist
How can this be? I'm trying to query the very same object, with the same connection and collection. Any ideas, please?
you could try it as follows:
detection_object.objects.raw({'legacy_id': "1437424"} ).first()
probably the legacy_id is stored as string.
Othewise, make sure the db name is present at the end of the MONGO_URI as it is underlined in the docs.
Each document in your 'detection_object' collection requires to have '_cls' attribute. The string value stored in this attribute should be
__main__.classname
(class name according to your code is detection_object).
For example a document in your database needs to look like this:
{'_id': ObjectId('5c4099dcffa4fb11494d983d'), 'legacy_id': 1437424, '_cls': '__ main __.detection_object'}

hasattr telling lies? (AttributeError: 'method' object has no attribute '__annotations__')

The following code
class Foo:
def bar(self) -> None:
pass
foo = Foo()
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__ = 'hi'
crashes with
AttributeError: 'method' object has no attribute '__annotations__'
How can this happen?
The attribute error here is raised because you can't set any attribute on a method object:
>>> foo.bar.baz = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method' object has no attribute 'baz'
The exception here is perhaps confusing because method objects wrap a function object and proxy attribute read access to that underlying function object. So when attributes on the function exist, then hasattr() on the method will return True:
>>> hasattr(foo.bar, 'baz')
False
>>> foo.bar.__func__.baz = 42
>>> hasattr(foo.bar, 'baz')
True
>>> foo.bar.baz
42
However, you still can't set those attributes via the method, regardless:
>>> hasattr(foo.bar, 'baz')
True
>>> foo.bar.baz = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method' object has no attribute 'baz'
So, just because the attribute can be read doesn't mean you can set it. hasattr() is speaking the truth, you just interpreted it to mean something different.
Now, if you tried to set the __annotations__ attribute directly on the underlying function object you'd get another error message:
>>> foo.bar.__func__.__annotations__ = 'hi'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __annotations__ must be set to a dict object
You would want to use a dictionary object here:
>>> foo.bar.__func__.__annotations__ = {'return': 'hi'}
>>> foo.bar.__annotations__
{'return': 'hi'}
However, because __annotations__ is a mutable dictionary, it is just easier to directly manipulate the keys and values to that object, which is perfectly feasible to do via the method wrapper:
>>> foo.bar.__annotations__['return'] = 'int'
>>> foo.bar.__annotations__
{'return': 'int'}
Now, if you were hoping to set per instance annotations, you can't get away with setting attributes on method objects, because method objects are ephemeral, they are created just for the call, then usually discarded right after.
You would have to use custom method descriptor objects via a metaclass and re-create the __annotations__ attribute for those each time, or you could instead pre-bind methods with a new function object that would be given their own attributes. You then have to pay a larger memory price:
import functools
foo.bar = lambda *args, **kwargs: Foo.bar(foo, *args, **kwargs)
functools.update_wrapper(foo.bar, Foo.bar) # copy everything over to the new wrapper
foo.bar.__annotations__['return'] = 'hi'
Either way you completely kill important speed optimisations made in Python 3.7 this way.
And tools that operate on the most important use case for __annatotions__, type hints, do not actually execute code, they read code statically and would completely miss these runtime alterations.
You're getting an error. because __annotations__ is a dictionary. If you want to change values you'll have to do it like this:
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__['return'] = 'hi'
This will make the return value of your foo.bar be hi instead of None. The only thing I'm not sure about is how the __annotations__ are protected, not allowing you to change them from a dict to string, but I suppose it's some internal check in the source.
UPDATE
For more control over the signature you can use the inspect module and get the Signature object of your class(or method) and edit it from there. For example
import inspect
sig = inspect.signature(foo.bar)
sig.return_annotation # prints None (before modifying)
sig.replace(return_annotation="anything you want")
More on that here

Resources