I'm using pymodm
for ORM in my project.
I have the following simple case:
class IdentityRecord(MongoModel):
alias = fields.CharField() # normal name for identity
class IdentityImage(MongoModel):
filename = fields.CharField() # filename on filesystem
source_identity = fields.ReferenceField(IdentityRecord) # reference to an identity
As we can see, each IdentityImage
refers to IdentityRecord
.
If I have object IdentityRecord
, then how do I find all records from IdentityImage
that refer to this object inside python code?
Of course, I can do the following:
IdentityImage.objects.raw({'source_identity': identity.pk})
However, necessity to user 'source_identity'
string literal kinda defeats the purpose of ORM. Is there any way in this framework to query IdentityImage
collection by somehow using an instance of IdentityRecord
object?
Selective queries in a pymodm.manager.Manager
(such as IdentityImage.objects
in the example from the question) seem to require a dict
as the argument for the get
and raw
methods. In contrast, the filter_by
method in a sqlalchemy query accepts keyword arguments.
If you prefer keyword arguments to string literals, the following expression seems to work.
IdentityImage.objects.raw(dict(source_identity=identity.pk))
Omitting the dict
wrapper in that expression reveals that the raw
method does not accept field names as keyword arguments. If pymodm were modified to allow that, the notation for such a query would be simpler.
The following code is a mutation of the original code from the question. The first import
line is implicit in the original code, and explicit here. The other two import
lines enable the definition and the use of a new class, KWQuerySet
. Aside from a helper function for the new class, args
, the only other change is the last line, a new attribute in one of the classes from the original code, which uses the new class.
from pymodm import MongoModel, fields
from pymodm.queryset import QuerySet
from pymodm.manager import Manager
def args(arg=None, **kwargs):
return {**arg, **kwargs} if arg else kwargs
class KWQuerySet(QuerySet):
def raw(self, raw_query=None, **kwargs):
return super().raw(args(raw_query, **kwargs))
def get(self, raw_query=None, **kwargs):
return super().get(args(raw_query, **kwargs))
class IdentityRecord(MongoModel):
alias = fields.CharField() # normal name for identity
class IdentityImage(MongoModel):
filename = fields.CharField() # filename on filesystem
source_identity = fields.ReferenceField(IdentityRecord) # reference to an identity
objects = Manager.from_queryset(KWQuerySet)()
With the definition of IdentityImage
from the mutated code, the following query appears to work correctly in python 3.5.3, with the same meaning for identity
(an instance of IdentityRecord
) implied in the example query from the question.
IdentityImage.objects.raw(source_identity=identity.pk)
It's likely that versions of python prior to 3.5 would require an alternate implementation of the args
function in the mutated code. I have not fully explored the implications of this change to the notation for queries, but I believe any query which passes raw_query
as a dict
should still work, with or without the notation which uses keyword arguments, or a combination of both notations, a dict for raw_query
and separate keyword arguments for other field names.