Understanding state types

Hi there, I’m trying to understand the state flow of the customer_data_app example.

In the State class, current_user is defined as a Customer type (a table) with current_user: Customer = Customer()

However, everywhere in the backend self.current_user is treated as a dict, for example: select(Customer).where(Customer.email == self.current_user["email"])

Then in this method, print(type(user)) returns <class 'dict'> too:

    def get_user(self, user: Customer):
        print(type(user))  # <class 'dict'>` ?
        self.current_user = user

But in the components we use Customer attributes directly

form_field(
    "Name",
    "Customer Name",
    "text",
    "name",
    "user",
    user.name,
),

Could someone shed some light, please?

This is a bit of wrinkle that probably should be resolved in the framework.

For event handlers, the arguments received are always the directly result of un-JSON’ing the payload from the frontend. So the dict is shaped like a Customer and you can do self.current_user = Customer(**user).

I filed a bug on github Cast dict as model type if annotated · Issue #4264 · reflex-dev/reflex · GitHub

We’ll see if the team can work on it for an upcoming release.

Thank you for the clarification, very helpful

Also the simple “cast” self.current_user = Customer(**user) is not so useful as soon as Customer includes some DB relationship and the form json includes nested dicts so it would be really nice to have the framework handle those things in the background

As of 0.6.5, if an event handler arg is annotated as a specific rx.Base, rx.Model or dataclass, then we will internally cast it. For models, we strip out relationship fields, and pydantic seems to handle the nested dict/model just fine.

Give it a shot on 0.6.5 and see if it works for your use case. If there are any bugs we can squash them

It’s working well for a simple model but it loses functionality for relationships.

For this example model:

class FailureReason(rx.Model, table=True):
   type: str
   description: str

   failures: Optional[List["Failure"]] = Relationship(back_populates="reason")


class Failure(rx.Model, table=True):
   simulation_id: str
   simulation_title: str
   date: date
   user: str
   reason_id: int = Field(foreign_key="failurereason.id")
   reason: FailureReason = Relationship(back_populates="failures")
   note: Optional[str]
   status: str = 'Open'

we go from this dict which includes another dict for the relationship reason

{
	'id': 2,
	'simulation_id': '335432',
	'simulation_title': 'my_title',
	'date': '2024-11-01',
	'user': 'my.user',
	'reason_id': 2,
	'note': 'git tag error',
	'status': 'Fixed',
	'reason': {
		'id': 2,
		'type': 'Process',
		'description': 'AMPT process failure'
	}
}

to the following Failure object:

Failure(
	id=2,
	simulation_id='335432',
	simulation_title='my_title',
	date=datetime.date(2024, 11, 1),
	user='my.user',
	reason_id=2,
	note='git tag error',
	status='Fixed'
)

But I understand it might not be feasible to go down the rabbit hole of relationships.

Ah i see, and it’s probably not feasible to load the relationship because the instance is unconnected from any session. Not sure if there’s a good way to solve this. However if you annotate the event handler arg as dict instead of the model type, you will get the original dict back with the relationships intact.