Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion reflex/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import re
from collections import defaultdict
from contextlib import suppress
from typing import Any, ClassVar, Optional, Type, Union
from typing import Any, Awaitable, ClassVar, Optional, Type, Union

import alembic.autogenerate
import alembic.command
Expand All @@ -19,6 +19,7 @@
import sqlalchemy.ext.asyncio
import sqlalchemy.orm
from alembic.runtime.migration import MigrationContext
from sqlalchemy.util.concurrency import greenlet_spawn

from reflex.base import Base
from reflex.config import environment, get_config
Expand Down Expand Up @@ -243,6 +244,18 @@ def get_metadata(cls) -> sqlalchemy.MetaData:
return metadata


class _AsyncAttrGetitem:
__slots__ = "_instance"

def __init__(self, _instance: Model):
self._instance = _instance

def __getattr__(self, name: str) -> Awaitable[Any]:
if name.startswith("_"):
return getattr(self._instance, name)
return greenlet_spawn(getattr, self._instance, name)


class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssues,reportIncompatibleVariableOverride]
"""Base class to define a table in the database."""

Expand All @@ -261,6 +274,15 @@ def __init_subclass__(cls):

super().__init_subclass__()

@property
def awaitable_attrs(self) -> _AsyncAttrGetitem:
"""Provide a namespace of all attributes on this object wrapped as awaitables.

Returns:
An awaitable attribute namespace.
"""
return _AsyncAttrGetitem(self)

@classmethod
def _dict_recursive(cls, value: Any):
"""Recursively serialize the relationship object(s).
Expand Down
Loading