šŸ“£ Add code to your data with Model Classes

Yes - batch updates work with models!

Correction: Batch updates will soon work with models. Turns out that PR hasnā€™t actually been merged yet :man_facepalming:

(There is internal ā€œsave these multiple drafts at the same timeā€ machinery, but itā€™s currently an implementation detail and we donā€™t expose it. Can you give us an example of a use case?)

2 Likes

Oh, this is smart! Right now, this is not available (a design decision, because ā€œinstance variables that get purged over client/serverā€ are footguns, especially with idiomatic use of @server_method). Do you reckon something like a @cached_property would be sufficient here? (I guess we might need to specify what its dependencies areā€¦we could clear the cache whenever we update the row cache, something like that? Just spitballing here)

2 Likes

@cached_property should do the trick, I reckon. I donā€™t use those persistence underscore attributes for any other use case.

and yes, having just looked at my code, I do have an example where I reset the internal attribute when the underlying row changes.

I vote for the underscore solution suggested by @owen.campbell.

In the case of cache, I like to manage the cache myself. For example I can introduce cache invalidation based on other things happening or on timeouts.

In other cases, I like to be able to store other data, when subclassing or wrapping would be overkilling. For example I may have a loop going through the row objects and tagging them, or I may want to store a selected/unselected tag or something similar.

EDIT
One more reason why I like the underscore over the decorator: one less thing to learn.

Letā€™s keep it Python, letā€™s introduce new tools only when they really make a difference.

1 Like

A server function returns a search iterator to the client that populates a data grid. The data grid allows the user to update the rows, which are all buffered. The user clicks a Save button after making changes to a number of rows on different pages of the data grid.

Presumably Iā€™ve kept track of which rows have changes, and do a for loop over them to save each. Iā€™d like a single server roundtrip to update all of those rows.

If the batch update context manager does that on the client, then thatā€™s great.

1 Like

The distinction between Portable Class values and regular class values is that the former owns its own data, and can be transformed into a regular dict or list, for transmittal over server calls.

Whereas a Model Class instance owns the table row columnsā€™ data, and transmits only those data values; which, by definition, are not instances of a Portable Class.

Looks like we need a different kind of object, one that does not store its own data, but instead wraps a SimpleObject column value (a dict or list), where the data actually resides, and just provides an object-oriented interface to those data.

Edit: Yes, that wrapper would probably appear as a @property.

Yes, thatā€™d be the right way to go about it, I think!

Also, correcting my earlier post - batching and buffered models are not currently supported at the same time (the PRs are there, just havenā€™t all been merged yet!). Iā€™ve edited a correction into my post above. Sorry about that.

1 Like

Ok, type time to rewrite all my app again. You should stop making our lifes simpler!

Jokes aside, this is awesome! Time to start reading a few pages of documentationā€¦

I would love to see this next! And it doesnā€™t look too far away tooā€¦

1 Like

So much truth in this. It seems like every couple of years a complete rewrite looks pretty good.

2 Likes

Iā€™m migrating an app from persistence to model classes. So far, so good - it mainly consists of deleting lines!

One questionā€¦

If Iā€™m using a buffered class, how can I tell whether it has buffered changes or not?

In persistence, I could check to see if the _delta had any content or not. Is there some equivalent here I can use?

EDIT

Calling dir on a model class instance shows me there is a ā€˜buffered_changesā€™ attribute. Is that what I need to use?

Good news - model classes are explicitly designed so that you donā€™t have to rewrite your app from the ground up!

Because models inherit from the Row type, they supports the full Row API, which means they can be used anywhere you were previously using raw rows. This means that you can create models for your tables without breaking anything, and gradually move functionality from Forms/server functions/etc into your models. (The same goes for buffering.)

2 Likes

yes buffered_changes is a property that returns a dict copy of the currently buffered changes

if youā€™re not buffering changes then buffered_changes returns None

4 Likes

And anvil.users.get_user returns a User model if you have one defined. Nice!

So many referential integrity checks can be bound into the models now. Avoiding orphaned links to rows that get deleted is a big one.

As a side note, this all introduced me to the magic of __init_subclass__. Very cool!

7 Likes

And hopefully now you should see some improved autocompletions for your models :smile:

4 Likes