How to set selection values in a wizard state?

I’m creating a wizard for importing Excel files. After the file has been uploaded (state start, user shall select the sheet to import (state query_sheetname).

How can I set the values to be available for selection in the sniff transition below?

Relevant parts:

class ImportWizardQuerySheetname(ModelView):
    …
    sheet = fields.Selection((), "Sheet to import")

class ImportWizard(Wizard):
    …
    def transition_sniff(self): # the state-transition after "start"
            self.xlsx_reader = my_xlsx.xlsx_reader(self.start.file_)
            if self.xlsx_reader.sheetnames():
                # set the sheet names here so the user can select one.
                self.query_sheetname = [
                    (n, n) for n in self.xlsx_reader.sheetnames()]
                return 'query_sheetname'
            else:
                return 'import_'

I think if you found more than one sheet in transition_sniff you should just display another StateView and use the values from the input. The initial configuration wizard should provide you with some examples.

HTH

Thx. Of course, that’s the idea :slight_smile:

The initial configuration wizard seems to “just” get the next config wizard and executes that. Which does not match my problem (Please correct my if I’m wrong). My specific issue is: How to set the selectable values for this next StateView.

Meanwhile I tried:

  • Passing a method name as first parameter to fields.Selection(). The method depends on a hidden field (which I wanted to use to pass the table names around):

    class ImportWizardQuerySheetname(ModelView):
         …
        sheets2 = fields.Binary('sheets_'')
        sheet = fields.Selection('get_sheetnames', "Sheet to import")
    
        @fields.depends('sheets2')
        def get_sheetnames(self):
            print("========>>>> ", repr(self), flush=1)
            return [(n, n) for n in self.sheets2.decode().split('\0')]
    

    This did not succeed since the value of the hidden field is always None when get_sheetnames() gets called.

  • Setting value and default for sheet in the wizard, using value_… and default_….
    This did not succeed since the value for sheet2 set in the value_… method did not make it to get_sheetnames() either.

The Solution I Found

Thus I ended up with defining a subclass of StateView which hacks the selectable values:

class QuerySheetnameStateView(StateView):
    def get_view(self, wizard, state_name):
        view = super().get_view(wizard, state_name)
        sheetnames = wizard.row_reader.sheetnames()
        if sheetnames:
            view['fields']['sheet']['selection'] = [
                (n, n) for n in sheetnames
            ]
        return view

and using this subclass:

class ImportWizard(Wizard):
            query_sheetname = QuerySheetnameStateView(…)

I someone knows a better solution, I’d be happy to learn about.

My idea to set selectable values was in the way like the selection of languages works. Did you see the according StateView and Wizard extension and integration as wizard item?

Thanks for pointing to the concrete places. In deed I missed them.

This indeed looks tricky and at first glance promissing: A Many2Many field with origin and target set to None. :100:

Anyway, even when using a One2Many (which would match my case of selecting one sheet), would require creating a Model and - even worse - store the names of the sheets there. But the names are ephemeral. Thus this seems to not be the direction to go.

Did I miss something?

Wouldn’t it be possible to create a suitable selection on the fly and just use a selection field?

??? This - receptively the complications with this - is what I’m talking about in How to set selection values in a wizard state? - #3 by htgoebel.

Perhaps I didn’t read close enough, I was somewhat confused by the representation of the code and what belongs to which class.

I don’t know if you have to go over get_view of StateView to address the selection field. If you define the selection field directly on the according ModelView it doesn’t work per by providing a function? At least I think you could address the field directly on the ModelView. If not then I am happy to have learned your solution :wink: .

Why not use trytond.model.fields.Selection.selection_change_with and create the selection list on the fly in the same view where you upload the file?

Thanks for this tip. The main reason I did not use it is that I didn’t know about it and there ist no tutorial. (BTW: The selection_change_with parameter is deprecated.)

I made a test with this and it works quite well. I really like it and it’s very useful.

But for this use-case it has one major deficit for this use-case: The file will be transferred several time from the client to the server: once when the selection_change_with triggers and the selection gets updated. And a second time when the from is completed.

This is not an issue if the file is rather small and the server is in the LAN. Anyway, I’m afraid performance will collapse if the file is 1 MB and the server is somewhere remote.

WDYT?

IIUC you are looking for the example in the documentation.

No. I know this example, which shows a basic wizard only. By no means it covers any of the questions in this thread.

Look up line

    self.result.file = Translation.translation_export(

There in the transition we set the variable file in the coming StateView() named result.
The variable is re-used in the method:

    def default_result(self, fields):

IMHO it should be possible to set the selection function or list of tuples of your selection also in a similar way.
Maybe there is an invisible fields.Char needed to utilize the default_ mechanism in combination with selection on_change_with, but I am not sure.

Which of the issue in this thread you are referring to?

I assume you referring to

The wizard example is missing this point, as it creates a file server-side which it server-side forward to the next state where it is transferred once to the client.

My quote refers to selection_change_with transferring the file from the client to the server. And the same file is transferred again from the client to the server again when finishing the view.

If you are suggesting to just a second stateview for asking the user for the sheetname: This is what I already implemented at the very beginning,

@yangoon I published the code and a first release. The code we have been taking about is in this commit. Meanwhile I moved forward to using selection_change_with as Udo suggested. Thanks for sharing your thoughts.

1 Like

Congrats! So it seems that a selection method works together with an ordinary :wink: on_change_with. Good to know it works.