Skip to content

Instantly share code, notes, and snippets.

@hgrecco
Last active February 7, 2024 05:06
Show Gist options
  • Save hgrecco/c33a1596044cd65b886dc55bc339dcaf to your computer and use it in GitHub Desktop.
Save hgrecco/c33a1596044cd65b886dc55bc339dcaf to your computer and use it in GitHub Desktop.
Dynamic reflex pages based on models
from dataclasses import dataclass
from typing import Iterable, Any, Sequence, NamedTuple
import stringcase
import reflex as rx
from sqlmodel import select
class FormField(NamedTuple):
field_name: str
model_field: rx.base.ModelField
@property
def placeholder(self) -> str:
return stringcase.sentencecase(form_field.field_name)
def render(self):
# TODO: Create specific input fields according to the annotation
# and field information.
return rx.input(
name="abc",
placeholder="def",
)
class DynamicFormState[T: rx.Model]:
form_data: dict[str, Any]
_include_fields: tuple[str] | None = None
_exclude_fields: tuple[str] = tuple[str]()
_model: type[rx.Model]
records: list[T] = list[T]()
def load_records(self):
with rx.session() as session:
self.records = session.exec(select(self._model)).all()
yield None
@classmethod
def model_fields(cls) -> list[tuple[str, rx.base.ModelField]]:
return list(cls._model_fields())
@classmethod
def _model_fields(cls) -> Iterable[tuple[str, rx.base.ModelField]]:
for field_name, model_value in cls._model.get_fields().items():
if field_name in cls._exclude_fields:
continue
if cls._include_fields is not None and field_name not in cls._include_fields:
continue
assert isinstance(model_value, rx.base.ModelField)
yield field_name, model_value
@classmethod
def form_fields(cls) -> list[FormField]:
print(cls.model_fields())
print(list(cls._model.get_fields().items()))
return list(cls._form_fields())
@classmethod
def _form_fields(cls) -> Iterable[FormField]:
for field_name, model_field in cls._model_fields():
yield FormField(field_name, model_field)
@classmethod
def handle_submit(cls, form_data: dict[str, Any]):
cls.form_data = form_data
# TODO: store in database
return rx.redirect("/")
########
# Pages
########
@classmethod
def list_page(cls, columns: list[str]) -> rx.Component:
return rx.vstack(
rx.hstack(
rx.heading(cls._model.__name__, font_size="3em"),
),
# Auto columns
rx.data_table(
columns=columns,
data = cls.records,
pagination= True,
search= True,
sort= True,
)
)
@classmethod
def add_page(cls) -> rx.Component:
return rx.vstack(
rx.form(
rx.vstack(
rx.foreach(
cls.form_fields(),
FormField.render
),
rx.button("Submit", type_="submit"),
),
on_submit=cls.handle_submit,
reset_on_submit=True,
),
)
# TODO: edit and delete pages
"""The dashboard page."""
from typing import Any, Iterable, Sequence
import reflex as rx
from myapp.templates import template
from ..state import State
from sqlmodel import Field, select
from . import _dynamic_form as dnf
class Person(rx.Model, table=True):
last_name: str = Field()
first_name: str = Field()
class PersonState(State, dnf.DynamicFormState[Person]):
"""The app state."""
_model = Person
@template(route="/persona/list", title="Personas", on_load=PersonState.load_records)
def persona_list() -> rx.Component:
"""The dashboard page.
Returns:
The UI for the dashboard page.
"""
return PersonState.list_page(columns=["last_name", "first_name"])
@template(route="/persona/add", title="Agregar Persona")
def persona_add() -> rx.Component:
"""The dashboard page.
Returns:
The UI for the dashboard page.
"""
return PersonState.add_page()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment