MongoEngine / MongoEngine/mongoengine
Error querying CachedReferenceField - Only accept a document object
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 4.3k
- Forks
- 1.2k
- Avg merge
- 4h 41m
- Merged PRs (30d)
- 11
Description
shift = Shift.objects().first()
query = Q(date=datetime.datetime.now()) & Q(shift=shift)
Confirmation.objects(query).first()
produces the following error in mongoengine 0.9
Traceback (most recent call last):
File "F:/_Projects/api/development/_tmp/test.py", line 29, in
Confirmation.objects(query).first()
File "F:_Projects\api\development\mongoengine\queryset\base.py", line 309, in first
result = queryset[0]
File "F:_Projects\api\development\mongoengine\queryset\base.py", line 160, in getitem
return queryset._document._from_son(queryset._cursor[key],
File "F:_Projects\api\development\mongoengine\queryset\base.py", line 1409, in _cursor
self._cursor_obj = self._collection.find(self._query,
File "F:_Projects\api\development\mongoengine\queryset\base.py", line 1443, in _query
self._mongo_query = self._query_obj.to_query(self._document)
File "F:_Projects\api\development\mongoengine\queryset\visitor.py", line 92, in to_query
query = self.accept(SimplificationVisitor())
File "F:_Projects\api\development\mongoengine\queryset\visitor.py", line 142, in accept
return visitor.visit_combination(self)
File "F:_Projects\api\development\mongoengine\queryset\visitor.py", line 42, in visit_combination
return Q(**self._query_conjunction(queries))
File "F:_Projects\api\development\mongoengine\queryset\visitor.py", line 62, in _query_conjunction
combined_query.update(copy.deepcopy(query))
File "C:\Programming\Python278\Lib\copy.py", line 163, in deepcopy
y = copier(x, memo)
File "C:\Programming\Python278\Lib\copy.py", line 257, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
File "C:\Programming\Python278\Lib\copy.py", line 182, in deepcopy
rv = reductor(2)
File "F:_Projects\api\development\mongoengine\base\document.py", line 192, in getstate
data['_data'] = self.to_mongo()
File "F:_Projects\api\development\mongoengine\base\document.py", line 330, in to_mongo
value = field.to_mongo(value)
File "F:_Projects\api\development\mongoengine\fields.py", line 1078, in to_mongo
self.error('Only accept a document object')
File "F:_Projects\api\development\mongoengine\base\fields.py", line 136, in error
raise ValidationError(message, errors=errors, field_name=field_name)
mongoengine.errors.ValidationError: Only accept a document object
this happens at line 1066 at CachedReferenceField class defined in mongoengine\fields.py
def to_mongo(self, document):
id_field_name = self.document_type._meta['id_field']
id_field = self.document_type._fields[id_field_name]
doc_tipe = self.document_type
if isinstance(document, Document):
# We need the id from the saved object to create the DBRef
id_ = document.pk
if id_ is None:
self.error('You can only reference documents once they have'
' been saved to the database')
else:
self.error('Only accept a document object')
this is screenshoft of debug session:
http://snag.gy/UUdsn.jpg
class Department(Document):
code = StringField(max_length=255, required=True)
name = StringField(max_length=255, required=True)
supervisor = ReferenceField(Supervisor)
timezone = StringField(max_length=50, required=True)
holidays = ListField(DateTimeField())
start_year = StringField(max_length=5)
end_year = StringField(max_length=5)
created_at = DateTimeField()
updated_at = DateTimeField()
deactivated_at = DateTimeField()
# speed up fields
supervisor_name = StringField(max_length=255)
supervisor_phone = StringField(max_length=255)
class District(Document):
code = StringField(max_length=255, required=True)
name = StringField(max_length=255, required=True)
department = ReferenceField(Department, required=True)
timezone = StringField(max_length=50, required=True)
holidays = ListField(DateTimeField())
start_year = StringField(max_length=5)
end_year = StringField(max_length=5)
created_at = DateTimeField()
updated_at = DateTimeField()
deactivated_at = DateTimeField()
class Shift(Document):
parent = ReferenceField('Shift') # shift parent ID, if we have type=reschedule or reassigned
type = StringField(max_length=50, required=True, choices=['regular', 'holiday', 'newshift', 'reschedule', 'reassignment'])
date = DateTimeField() #only if exception is created
school = ReferenceField(School, required=True)
location = ReferenceField(Location, required=True)
guard = ReferenceField(Guard, required=True) #only if exception is created
name = StringField(max_length=255)
weekday = IntField(required=True, min_value=1, max_value=7) # isoweekday, Monday = 1
start = TimeField(required=True)
finish = TimeField(required=True)
start_sec = IntField(required=True)
finish_sec = IntField(required=True)
weight = IntField(required=True) # used for sorting - this is hh_mm_ss for local of this school timezone
holidays = ListField(DateTimeField()) # only for regular shift, let's not create a custom exception for each holiday date
comment = StringField(max_length=2000)
created_at = DateTimeField()
updated_at = DateTimeField()
deactivated_at = DateTimeField()
# speed up fields
guard_name = StringField(max_length=255)
guard_phone = StringField(max_length=255)
school_name = StringField(max_length=255)
location_name = StringField(max_length=255)
parent_cache = CachedReferenceField('Shift', fields=['type', 'start', 'finish', 'start_sec', 'finish_sec', 'guard', 'weight'])
class Confirmation(Document):
date = DateTimeField(required=True)
guard = ReferenceField(Guard, required=True)
department = ReferenceField(Department, required=True)
district = ReferenceField(District, required=True)
school = ReferenceField(School, required=True)
shift = ReferenceField(Shift, required=True)
location = ReferenceField(Location, required=True)
confirmed = BooleanField() #confirmed status
declined = BooleanField() #declined status
checkin = BooleanField() #checked in status
checkout = BooleanField() #checkout status
# checkin/out distance will be will be caculated by using distance function above with pre-save signal defined in __init__.py
checkin_coordinates = GeoPointField() # NamedTuple (lon, lat), Point = namedtuple('Point', 'lon lat'), or see lib.geo GeoPoint class
checkout_coordinates = GeoPointField() # NamedTuple (lon, lat), Point = namedtuple('Point', 'lon lat'), or see lib.geo GeoPoint class
checkin_distance = DecimalField() # in feet, calculates on save automatically, see self._on_save_update_distance() method
checkout_distance = DecimalField() # in feet, calculates on save automatically, see self._on_save_update_distance() method
# why declined
declined_reason = StringField(max_length=500) # explanation of the decline by guard
declined_reassigned = ReferenceField(PooledGuard) # if we have re-assigned the decline, link to another PooledGuard ID
# time stamps
confirmed_at = DateTimeField()
declined_at = DateTimeField()
checkin_at = DateTimeField()
checkout_at = DateTimeField()
created_at = DateTimeField()
updated_at = DateTimeField()
# speed up fields
supervisor = ReferenceField(Supervisor, required=True)
supervisor_name = StringField(max_length=255, required=True)
supervisor_phone = StringField(max_length=255, required=True)
guard_name = StringField(max_length=255, required=True)
guard_phone = StringField(max_length=255, required=True)
guard_device_token = StringField(max_length=255)
department_name = StringField(max_length=255, required=True)
department_code = StringField(max_length=255, required=True)
district_name = StringField(max_length=255, required=True)
district_code = StringField(max_length=255, required=True)
school_name = StringField(max_length=255, required=True)
school_phone = StringField(max_length=255, required=True)
school_code = StringField(max_length=255, required=True)
shift_launch = DateTimeField(required=True)
shift_start_sec = IntField(required=True)
shift_finish_sec = IntField(required=True)
location_cache = CachedReferenceField(Location, fields=['name', 'coordinates'])
shift_cache = CachedReferenceField(Shift, fields=['type', 'date', 'weekday', 'name', 'start',
'finish', 'start_sec', 'finish_sec', 'guard', 'parent', 'weight'])
I have fixed this by
query = Q(date=datetime.datetime.now()) & Q(shift=shift.id)
but still want to report this bug.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with mongoengine/queryset/visitor.py and the CachedReferenceField implementation in mongoengine/fields.py, following the traceback from query combination through document serialization. Reproduce the Q(date=...) & Q(shift=shift) query and add a regression test showing that querying with the document object does not raise ValidationError; the existing shift.id workaround indicates the expected behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- mongodb, python
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100