Passing objects and attributes between client and server

Hi everyone…

This question may well be wider than just Anvil, perhaps applicable to all client/server web apps in fact, but I’m running aground repeatedly here in Anvil-land and was hoping for some inspiration from more experienced brains please…

Before Anvil, I had a collection of classes and methods that worked nicely on their own on the desktop, or their own on the server. For example:

class Video_Captions:
    """ Contains helpful methods for handling, downloading, and converting
    and correcting subtitles/captions """
    def convert_vtt_to_srt(self):
        """ VTT is a subtitle format commonly used by YouTube, SRT is another
        commonly used by social media platforms like Twitter and Facebook.
        This method takes self.vtt and creates self.srt in place. """

The nice thing here is that it’s very easy to get and set self.vtt and self.srt etc. with the object being updated in place - no need to supply arguments, no need to return values, no pre-processing, no post-processing. The problem comes with the client code trying to call this server code, because I can’t (I believe?) return a Video_Caption object to the client, only a string, list, dictionary or json value (not a set, strangely?), or bytes as a ‘media’ object. I also can’t call the convert_vtt_to_srt() method from the client without recreating a Video_Caption object each time.

This is causing me to write a whole lot of extra code to strip out the relevant Video_Caption attributes and return them, or create a temporary new object just to run the method, return a json then discard the object, then recreate it.

So… I just wondered if anyone knew of some best practice, or a pattern, or a general approach to this (presumably quite common?) challenge, which doesn’t involve me refactoring all of my original code, converting an object orientated approach back to a pipeline of functions with their inputs and outputs each preceded by @anvil.server.callable?

Other options I’m exploring include:

  1. Use a database - but this creates additional server calls every time the client wants to refresh or update an attribute.
  2. Use a decorator function, and maybe CleverDict to auto-generate json return values e.g. from vars()
  3. Make all my methods Class methods
  4. Make all my methods Static methods
  5. Move these methods into the client code - but makes it really bloated and also exposes all the code making security etc really tricky
  6. Playing with the Anvil media object but I’m tying myself in knots trying to reconstruct a usable Video_Captions object on the client side.
  7. Convert objects to nametuples - curiously Anvil can return those but not sets!
  8. Write a client function to convert dictionaries to objects with attributes

I’ve also read:


If anyone has general thoughts or can signpost me to topics/discussions/tutorials that would be really helpful thank you. Often it’s just a question of knowing that the topic has a specific name already (like “dependency injection” or “dispatcher”) but if you don’t know it’s a “thing” or what that “thing” is called, you don’t know what to search for!

Many thanks in advance,
Peter

I believe the term you’re looking for is “serialisation” (or “serialization” for our US colleagues) - the act of representing an object in a form suitable for streaming across a serial connection where the order of that data matters (think of the days of magnetic tape storage for a good analogy).

The connection between the client and server in any web app is just such a connection and any data sent across must be serialised first. JSON is a common way to handle that and several Python types can be serialised using JSON without any intervention (dicts, lists).

However, for more complex objects, you have to define the serialisation yourself. There’s a very good library called marshmallow for doing that or you can just roll your own.

You can also see an example of me having to serialise data table rows regularly and writing a module to do that dynamically at Dynamic serialisation of data table rows

Many thanks @owen.campbell! You’ve given me the magic incantation to carry on learning :slight_smile:

I’ll also have a peek at your module. Really appreciate you sharing that, thanks.

Is marshmallow available client-side?

No, that’s server side only (and only on a paid account).

For client side, you’d have to roll your own.

I think I should probably mention that there’s a private beta opening up that is…very relevant to this topic. Drop me a PM if you want in :slight_smile:

Edit, three years later: This thread is from 2020, and this feature has long since been released as Portable Classes!

3 Likes

Thanks, @owen.campbell.

More generally, regarding application design, desktop vs. Anvil:
I’ve written desktop applications for literally decades. Anvil app architecture is different from desktop. Each starts from a different foundation, and has different constraints. Classes (and other code) that make sense in one context do not necessarily make sense in the other.

On the desktop,

  1. In compiled languages, code can’t be altered by users, so in-program data is relatively secure.
  2. Instances can live as long as the program is running, preserving state for as long as needed, and serving as a foundation for rich, intelligent, self-managed behavior.
  3. There’s no barrier between UI and application code. A class can (and frequently does) mix UI and application code, data, and objects, willy-nilly. (Not necessarily wise, but certainly expedient, therefore frequent.)
  4. Due to 3, UI-related code can directly call whatever libraries you have handy.

With Anvil,

  1. running parts of the app in the browser means that those parts of the app are (unfortunately) hackable, and visible to end-users (if they wish). For anything non-trivial, you must split logic between client and server.
  2. Server-side objects live only long enough to service a call from the client. To persist longer, an object must be reduced to data-values, and put into a session-level cache, or the database, and re-constructed later, into an object, on demand. (You can pay to have your server program stay running, one instance per browser-session. Other instances are transient, as described.)
  3. Client-side code generally can’t leverage regular third-party libraries directly. They have to be written specifically for Anvil.
  4. Differences like these can invalidate the very design of a desktop-based class, never mind an entire desktop application.

In short, one should not expect desktop-style application-level code to be usable, as-is, in Anvil. The underpinnings of such code – support libraries – may be usable on the Server, or in Uplink code. But it is an entirely different architecture, with significant ramifications for the design of the entire application. Very different design patterns come into play.

This does not make either architecture “wrong” or “right”. It just means that they don’t match up in a lot of places. The constraints are different. Taking that into account, the reasoning one applies in each case must also be different.

Coming from a long, long desktop perspective, this was a big surprise. I had to learn, and “unlearn”, a lot. And that’s still in progress. I’m still picking up new and better patterns for building. And Anvil itself continues to evolve, so new patterns keep turning up.

1 Like

When this debuts, it sounds like a lot of developers will be pickled tink. :wink:

Hi,

Did the Beta make it public?

Can someone tell me what it was?

I am also looking at passing objects back and forth between the server and was wondering about values by reference etc.

I chanced my arm and tried to pass ‘self’ for a client form to get my first serialisation error.

I have been playing with sending self.__dict__ for the client form but not got very far yet.

I know from other languages that JSON doesn’t provide out of the box support for that and that you have to make your own syntax in the JSON for the serialisation layer to take care of objects/attributes by reference.

Thanks

Adrian

Yes! It’s called Portable Classes - check em out :slight_smile:

Thank you.

I am knee deep in that now, trying to work out what is the most efficient way to exchange a client-form full of text boxes with a server module that needs to populate them.

I got distracted by duck-typing v’s inheritance - Which I recognise as A pure python issue and not Anvil related.

If you have a specific question, please create a new post rather than reviving an old one.

I feel like you need to change the way you think.

You don’t exchange a “form form full of text boxes”, and the server doesn’t “populate” a form.

You exchange data between the server and the client, and the client uses that data to populate a form.

Client and server are two different computers and don’t share any memory, so you can’t pass objects “by reference”.

1 Like

“please create a new post” - will do when I have a better grasp of the basics.

and yes I need to think in Anvil.

When the form opens (or even before depending on your use case) call a server module that pulls your data from wherever it happens to be, then store it in something like a dict, then return it to the client and the client then parses the data into the text boxes. How to do so can be found across different pages in the documentation. Something like this maybe:

class app_launcher(app_launcherTemplate):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    text_box_data = anvil.server.call('get_data')
    self.text_box_1.text = text_box_data[0]['example data']
    self.text_box_2.text = text_box_data[1]['example data']

With the data looking something like:
[{'example data': 'this is data'}, {'example data': 'this is also data'}]