Button.on_click behavior?

Here is my code calling testi_insert -function with no errors in Jupyter Lab. The first cell:

import panel as pn    
from testi_insert import testi_insert

pn.extension()
# Upload Excel files to the database.
upload = pn.widgets.FileInput(accept='.xlsx', multiple=True)
test = pn.bind(testi_insert, upload)
upload

Running the cell allows choosing a file or multiple files into the fileInput. The second cell:

test()

However, when I add a button from which I’d like to call the testi_insert -function, I get

TypeError: testi_insert takes 1 positional argument but 2 were given

when clicking the button. Here is the code for calling testi_insert from a button:

import panel as pn    
from testi_insert import testi_insert

pn.extension()
# Upload Excel files to the database.
upload = pn.widgets.FileInput(accept='.xlsx', multiple=True)
test = pn.bind(testi_insert, upload)
upload_btn = pn.widgets.Button(name='Upload file(s)')
upload_col = pn.Column('# Upload file(s)', upload, upload_btn, align='center')
upload_btn.on_click(test)
upload_col

And here is the testi_insert -function signature (it is printing the sql strings used in the actual insert function to see if they’re correct). I have tried adding an extra input to the signature but it doesn’t have any effect.

def testi_insert(files: list[IOBase]) -> None:

Could someone tell me what is wrong in my approach?

Another attempt to make this work was

# Upload Excel files to the database.
upload = pn.widgets.FileInput(accept='.xlsx', multiple=True)
test = pn.bind(testi_insert, upload)
button = pn.widgets.Button(name='Upload file(s)')
upload_btn = pn.bind(test, button.param.clicks)
upload_col = pn.Column('# Upload file(s)', upload, button, align='center')
upload_col

But with this the button click does not do anything. Changing the button in upload_col to upload_btn results in AttributeError: 'function' object has no attribute 'owner'.

I don’t think you’re supposed to be using pn.bind alongside on_click; it’s either one or the other.

The on_click expects this signature on the callable:

def run_on_click(event):
    ...

Ok. How can I send the FileInput value to the on_click? In this case testi_insert needs that value as an input.

I suppose FileInput should not be used with on_click. Check out this:

The topic @ahuang11 linked above discusses file sizes. There is no reason categorically not to use FileInput and Button.on_click together. Here is an OOP solution for sending FileInput to Button.on_click-callback.

import panel as pn
pn.extension()

class Upload:
    """A class for handling data upload to the database.
    Methods
    -------
    __init__
    upload_files
    upload_button
    """
    
    def __init__(self) -> None:
        """__init__ method for Upload class."""
        
        self.files_in = pn.widgets.FileInput(accept='.xlsx', multiple=True)
        self.button = self.upload_button()
    

    def upload_files(self, event) -> None:
        """A method for calling insert from upload_btn.
        The insert-function handles FileInput.
        """
        
        insert(self.files_in.value)
    
    
    def upload_button(self) -> pn.widgets.Button:
        """A method for creating a Button object and assignng 
        the insert function to it.
        Returns
        -------
        upload_btn: pn.widgets.Button 
                A button for inserting data from the selected Excel file(s)
                to the database.
        """
        
        button = pn.widgets.Button(name='Upload file(s)')
        button.on_click(self.upload_files) #button, watch=True
        
        return button

upload = Upload()
upload_col = pn.Column(
    '# Upload file(s)', 
    upload.files_in, 
    upload.button, align='center'
)
upload_col

Another way of handling data via a Button.on_click-callback is implementing partial from Python’s functools-module. Here is an example of a use case on Stack Overflow.