Portable class for a wizard form

Hi there,

I wanted to ask some advice in order to make sure that I am thinking about portable classes in the correct manner and that my abstract explanation sounds like it is heading in the correct direction and if anyone can spot any gotchas in my logic.

I have a wizard style form, so I am having the form update as you go along, filling out the details. I used the super awesome solution from @jshaffstall. Thank you very much for your solution Sir, much appreciated. Link to topic. The current solution is without portable classes, which works great but as the app gets more complex, it might be easier to centralize the logic.

I am creating for instance a portable class named ‘Form’, which has some properties and a couple of methods that handles the data for a form. My thought process is I could reuse the class with setting it as a global and then when required, execute the methods. If the way I am thinking about this is correct, it would help to centralise the logic and possibly allow correct permissions using the capabilities concept. I feel I might run into issues with setting the class object as a Global, so if there is another way I can look at persisting, advice is welcome :smile: I could possibly look at pickling the class and save the stream to the db.

example of my class:

class Form:
  def __init__(self, in_progress, finished, workflow_state, project_description):
    self.form_id = 0
    self.in_progress = in_progress
    self.finished = finished
    self.workflow_state = workflow_state
    self.project_description = project_description
  def save_to_db(self, is_saved):
    data = self.__dict__
    anvil.server.call('save_form_submission', data, is_saved)
  def save_meta_data(self, line_item_totals, line_items):
    line_item_totals = line_item_totals.__dict__
    line_items = line_items.__dict__
    anvil.server.call('save_form_meta', line_item_totals, line_items)

Glad you’re getting some use out of the Wizard component!

Generally speaking, the component is a client side way of collecting data from the user. Because it’s on the client, it’s subject to the usual sorts of tampering by bad actors, so you can’t really rely on any security checks that are part of the wizard process.

I could see using the wizard to populate a portable class, which then would take care of updating itself on the server. The capabilities checks on the server would prevent bad actors from doing things they aren’t supposed to.

But it sounds like you want something more than that, you’d like the steps in the component to be data driven? If that’s the case, I’d look at the InputBox component: Input_box() and alert2() It allows you to easily create individual forms programmatically, instead of needing to hand create them.

Back to the original question:

Generally speaking, client side global variables via modules are fine. I don’t know that I’d recommend a single instance of a portable class serving the entire client, though. That seems counter to how portable classes are intended to work. You’re supposed to grab instances from the server as needed so the server can set the capabilities appropriately.

I wouldn’t recommend pickling and saving to the DB. There’s no reason to save the entire instance, you just want the data needed to recreate the instance (e.g. using a simple object column).

If I’ve completely misunderstood what you’re looking to do, sorry about that!

Hi @jshaffstall thank you so much for your advice. I fully get what you are saying and makes perfect sense.
I think you’ve understood what I am trying to set out to do. This should assist immensely with what I am trying to achieve. Thank you. :smile:

A caution on naming things: Form is almost certainly the name of a base class in the Anvil library. To avoid clashes and misunderstandings, you might want to consider a different name.

Hi @p.colbert. Thanks for the heads up. Yes, that is a silly mistake. :see_no_evil:

Not at all! You’ve hit one of the hardest problems in computing: naming things. Names that work well in one context often run into conflicts in another.

Python’s namespaces lessen the impact, but can’t eliminate it entirely.

1 Like