peewee-async icon indicating copy to clipboard operation
peewee-async copied to clipboard

Adding sugar via patching model classes - a discussion topic

Open rudyryk opened this issue 9 years ago • 3 comments

Peewee's design is based on maximum simplicity concept, there is not so much of magic in implementation and no "black" magic at all, as I can see. And peewee provides the exceptionally clean and powerful interface -- it's far cleaner than the one for sqlAlchemy and more powerful that the Django's one -- having probably the best balance between simplicity and power.

The design goal for the peewee-async project is to play nice with peewee core conforming its approach and add extra facilities for async programming. That is why the current implementation does not apply any real black magic as well. Yes we rely on some internal logic, but NO, we don't perform any kind of patching!

I like this approach, but probably it would be acceptable to add some more sugar so we could get less verbose async calls.

For now, we have a high-level API via the Manager class and can write code like that:

# `objects` is the `Manager` class instance
user = await objects.get(User, id=user_id)
posts = await objects.execute(Post.select().where(
    Post.is_published == True,
    Post.author == user))

Looks like OK and actually, we can implement nicer interface at the application level, for example:

class UsersManager:
   # ...

   async def get(cls, user_id):
        return (await cls.objects.get(User, id=user_id))

and so on.

So I don't think it's crucial to add more interface methods to the peewee-async, but I'm considering to add some sugar via Manager.extend() method: https://github.com/05bit/peewee-async/blob/extend/peewee_async.py#L115 It will allow to leave the Manager under the hood in the most cases and write like that:

user = await User.get_async(id=user_id)
posts = await Post.execute_async(Post.select().where(
    Post.is_published == True,
    Post.author == user))

-- well, not ideal but simpler as we don't need to pass around objects and keep in mind that we need it every time.

I hope to hear some critics and suggestions on that, so we could refine the idea or left it out.

rudyryk avatar May 08 '16 09:05 rudyryk

It seem more like standard peewee usage

julien-duponchelle avatar Aug 29 '16 11:08 julien-duponchelle

@noplay Yes, I'm thinking on more "peewee-like" interface, but I'm not a big fan of reusing by subclassing - so the ideal solution should work without modification of models.

rudyryk avatar Aug 29 '16 12:08 rudyryk

Another approach is to add optional mixin class, so we don't always need to patch models but just add mixin via inheritance. Not perfect, but still better than patching.

Unfortunately, thare are no established standards yet for providing both sync and async interfaces in classes. I'm still not sure about suffixing for methods with _async.

Probably, it's possible to override base query classes so they will support await and we could just run await MyModel.select().where(MyModel.id == 1). But this is potentially possible only for lazy query methods, e.g. get() is not lazy and will just run synchronously.

rudyryk avatar Sep 20 '16 12:09 rudyryk