0

I've been trying to craft a small "mongodb orm" on top of pymongo with attrs. For this I've found that there's not a way for me to keep a consistent structure through the whole app since pymongo is very permissive by default. Trying to build what I think would add consistency I found a wall that I'm eager to break but don't think is a trivial task at first glance.

Let's say I have define this class:

from attrs import define

@define(slots=True)
class User:

    @define()
    class UserDetails:
        status: UserStatus = UserStatus.ALIVE
        address: str = None

    user_id: CustomTypeId = None
    details: UserDetails = UserDetails()

And this class works great for handling defaults in my app. But when I want to do a query, instead of making:

db[UserCollection].find({'user_id': 'newid', 'details.status': 'ALIVE'})

I want to make

db[UserCollection].find({User.user_id: 'newid', User.details.status: 'ALIVE' })
# or from a class method
db[UserCollection].find({cls.user_id: 'newid', cls.details.status: 'ALIVE' })

However accessing this properties in the class level hasn't been trivial and it gets ugly real fast using external methods. With all of this being said, If I'm breaking a fundamental design of pymongo/python and there's a way to turn this around using a more conventional path, more glad to hear it. Thanks for reading and for your help!

1
  • 1
    What you want is possible, but not trivial. SQLAlchemy has done it that you can write filter(UserDetails.status == UserStatusALIVE) and it probably needs descriptors and/or meta classes. As a side-note: slots=True is default for define and you're creating ONE instance of UserDetails that is shared among all User-Instances. Not sure that's what you want (wrap it in Factory if not).
    – hynek
    Commented Nov 9, 2023 at 14:38

1 Answer 1

1

I' the author of cattrs and an attrs maintainer, and I've previously built a Mongo library using these libraries and Pymongo (not open source though) so I can share some tips.

You can use attrs.fields(User).user_id instead of User.user_id. (You can do from attrs import fields as f to keep it less verbose.) Due to recent changes in Mypy, this approach can have much better static type safety, if done right.

1
  • Thank you Tin, this is what I was looking for. It may not be as pydantic like what SQLAlchemy offers but with cattrs and attrs building the structures it's way easier. Thank you for all of your contributions!
    – Gabe
    Commented Nov 10, 2023 at 3:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.