2

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?

Evhz
  • 8,852
  • 9
  • 51
  • 69
drsealks
  • 2,282
  • 1
  • 17
  • 34

1 Answers1

3

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.

Eirik Fuller
  • 1,454
  • 11
  • 9
  • This is already good enough, I will accept this answer - very nice simple idea. However, in my opinion, it still doesn't fully address the problem. But, dict constructor with kwargs is much better. Thanks! – drsealks Apr 03 '18 at 10:25
  • Fully addressing the problem might be best accomplished by enhancing `pymodm` to handle field names as kwargs natively, possibly with a subclass within the application rather than within `pymodm` itself. – Eirik Fuller Apr 03 '18 at 10:32