Creating my first Transaction

Hi
I am trying to create a transaction that updates various tables. I have not yet tested this, because I wanted to clarify a few points. Here is the code:-


#Transaction for Policy
@anvil.server.callable
@tables.in_transaction
def get_next_value_in_sequence():
  row = app_tables.counter.get()
  row['value'] += 1
  return row['value'] 
def do_update():
#Add Travellers  
   for traveller in self.cp2.get_components():  
      row = app_tables.travellers.add_row(
        TravellerFirstname=traveller.traveller_firstname.text.title(),
        TravellerSurname=traveller.traveller_surname.text.title(),
        TravellerDateofBirth=traveller.travellerform_dob.date,        
        User=anvil.users.get_current_user(),
        PolicyNumber='value')
#Add  Policy
      row = app_tables.policies.add_row(
        PolicyStartDate=self.trip_start_date,
        PolicyEndDate=self.trip_end_date,
        PolicyPremium=self.totalpremium,
        PolicyIPT=self.totalipt,
        User=anvil.users.get_current_user(),
        PolicyTravellerCount=self.trav,
        Location=self.location_region,
        LocationDetail=self.location,
        Days=self.days,
        Travellers=self.trav,
        Baggage=self.finalbaggage,
        SingleArticle=self.finalarticle,
        Cancellation=self.finalcancellation,
        PolicyDocument='',
        CreatedDate=datetime.date.today().strftime('%Y,%m,%d'),
        PolicyNumber='value'
      )
#Add User Address
      row = app_tables.users.get_current_user(
        UserAddress1=self.address1,
        UserTown=self.town,
        UserCounty=self.county,
        UserPostcode=self.postcode,
        UserMobile=self.mobile,
        UserLandline=self.landline
      )
#Add Trips
      row = app_tables.mytrips.add_row(    
          TripName=self.tripname,
          TripStartDate=self.trip_start_date,
          TripEndDate=self.trip_end_date,
          User=anvil.users.get_current_user(),
          HolidayType=self.holidaytype,
          LocationCountry=self.location,
          LocationCity=self.location_city,
          TripStatus=('Active'),
          PolicyNumber='value'
          )

Here are my questions:-

  1. Should the part with the Counter for the transaction reference, be server side? At the moment I have set the table counter to view edit delete on form, but not sure if thats right.

  2. I want the reference number created by the counter to be set as PolicyNumber, have I done that right?

  3. In relation to the current user, by the time the person clicks to submit the transaction, they will be logged in. Have I set the line for User correctly?

  4. Can anyone see any other glaring errors that I have made?

EDIT: I’ve already had to comment out this whole transaction on the form, so clearly some has to go server side, so:

Question
5. How do I split it, since all of the fields are on the form

Well to answer #5 first a quick trip to a code editor with find and replace could turn your data into kwargs to be passed to the server side:

for traveller in self.cp2.get_components(): 
    dict_travellers = {
        "TravellerFirstname": traveller.traveller_firstname.text.title(),
        "TravellerSurname": traveller.traveller_surname.text.title(),
        "TravellerDateofBirth": traveller.travellerform_dob.date,
        "User": anvil.users.get_current_user(),
        "PolicyNumber": "value",
    }
    # Add  Policy
    dict_policies = {
        "PolicyStartDate": self.trip_start_date,
        "PolicyEndDate": self.trip_end_date,
        "PolicyPremium": self.totalpremium,
        "PolicyIPT": self.totalipt,
        "User": anvil.users.get_current_user(),
        "PolicyTravellerCount": self.trav,
        "Location": self.location_region,
        "LocationDetail": self.location,
        "Days": self.days,
        "Travellers": self.trav,
        "Baggage": self.finalbaggage,
        "SingleArticle": self.finalarticle,
        "Cancellation": self.finalcancellation,
        "PolicyDocument": "",
        "CreatedDate": datetime.date.today().strftime("%Y,%m,%d"),
        "PolicyNumber": "value",
    }
    # Add User Address
    dict_users = {
        "UserAddress1": self.address1,
        "UserTown": self.town,
        "UserCounty": self.county,
        "UserPostcode": self.postcode,
        "UserMobile": self.mobile,
        "UserLandline": self.landline,
    }
    # Add Trips
    dict_mytrips = {
        "TripName": self.tripname,
        "TripStartDate": self.trip_start_date,
        "TripEndDate": self.trip_end_date,
        "User": anvil.users.get_current_user(),
        "HolidayType": self.holidaytype,
        "LocationCountry": self.location,
        "LocationCity": self.location_city,
        "TripStatus": "Active",
        "PolicyNumber": "value",
    }
    
    anvil.server.call(
        "some_table_interaction_server_function",
        dict_travellers,
        dict_policies,
        dict_users,
        dict_mytrips,
    )

… Then on the server side:

@anvil.server.callable
def some_table_interaction_server_function(dict_travellers, dict_policies, dict_users, dict_mytrips):

    row = app_tables.travellers.add_row(**dict_travellers)
    #Add  Policy
    row = app_tables.policies.add_row(**dict_policies)
    #Add User Address
    row = app_tables.users.get_current_user(**dict_users)
    #Add Trips
    row = app_tables.mytrips.add_row(**dict_mytrips)

Should get you started moving it to the server.

3 Likes

I just posted on another question of yours, then I read this question and realized that I had just answered: Anvil Transactions add a transaction ID - #4 by stefano.menci

1 Like

Started!!! Looks like you got me finished :slight_smile: Thanks so much. Just one thing, where in the server call would I put the counter to get the value (sequential reference no) for all the dicts since it has to be the same value on all the dicts.

Would it be here?

def submit_policy(dict_travellers, dict_policies, dict_users, dict_mytrips):
    row = app_tables.counter.get()
    row['value'] += 1
    return row['value'] 
    row = app_tables.travellers.add_row(**dict_travellers)
    #Add  Policy
    row = app_tables.policies.add_row(**dict_policies)
    #Add User Address
    row = app_tables.users.get_current_user(**dict_users)
    #Add Trips
    row = app_tables.mytrips.add_row(**dict_mytrips)

Thanks, looks like you and Ian had the same thoughts.

If I understand, this should work:

def submit_policy(dict_travellers, dict_policies, dict_users, dict_mytrips):
    row = app_tables.counter.get()
    row['value'] += 1
    pn= row['value']
    row = app_tables.travellers.add_row(PolicyNumber=pn, **dict_travellers)
    #Add  Policy
    row = app_tables.policies.add_row(PolicyNumber=pn, **dict_policies)
    #Add User Address
    row = app_tables.users.get_current_user(**dict_users)
    #Add Trips
    row = app_tables.mytrips.add_row(PolicyNumber=pn, **dict_mytrips)

The **dict_travellers will expand the content of the dictionary into function arguments. Each key-value pare in the dictionary becomes a named argument in the function.

The result is that add_row(PolicyNumber=policy_number, **dict_travellers) passes all the arguments in dict_travellers plus PolicyNumber.

Just make sure the key PolicyNumber is not included inside the dict_travellers dictionary.

1 Like

Thanks, will try that now!

You’d want to use your original function get_next_value_in_sequence(), since that had the get and increment wrapped in a transaction. Without the transaction it’s possible to get two identical policy numbers if two people hit the function in just the right timing.

def submit_policy(dict_travellers, dict_policies, dict_users, dict_mytrips):
    pn= get_next_value_in_sequence()
1 Like

Do you expect

to change during the loop? If not, then I’d do that outside of the loop.

That applies equally well to any calculation or lookup whose value cannot (or should not) change.

1 Like

no, it will be the same all the way through, how would I do that then?

Before the loop begins, do the (expensive) lookup or calculation, then store the result in a local variable. Then use the local variable in place of where the (repeated) lookup was previously being done.

Edit: I should provide a concrete example.

for t in range(1000):
    x = cos(0.5)
   ... (using x)

vs.

x = cos(0.5)
for t in range(1000):
   ... (using x)

For that matter, you probably don’t need to pass anvil.users.get_current_user() from client to server at all. The server can look up the value just as easily.

1 Like

so could I do something similar as stefano suggested for the counter. e.g

usr = anvil.users.get_current_user()

then pass that into the add row on the server side User=usr

???

I dont think I ever did an original function with that in?

Yes.

Although, the server side code could do

on its own, so the value of usr would not have to be passed from client to server. For this one value, it’s guaranteed to be the same value at both ends of the client-server connection.

1 Like

I’m not sure I really understand, so what would the dicts on the client side say “User”: usr???

They could omit “User” altogether.

Wherever server-side code needs the value of anvil.users.get_current_user(), it can compute anvil.users.get_current_user() for itself, and get exactly the same answer.

Edit: Usually, when I have a “slice” (set of rows) of a table that belongs to the current user, I ask Anvil for a Writable View, where User=anvil.users.get_current_user(). (See Views.) That way, Anvil takes care of filling in the user id.

1 Like

That’s from your original post. At one point you had that function in your server.

1 Like

ahh ok, just shows how observant I am!

So I am testing the function now and am getting an error

TypeError: item_fn() got multiple values for keyword argument 'PolicyNumber'

  • at Queries, line 109
  • called from Quotation, line 517

On the server function:-

def submit_policy(dict_travellers, dict_policies, dict_users, dict_mytrips):
    row = app_tables.counter.get()
    row['value'] += 1
    pn= row['value']
    row = app_tables.travellers.add_row(PolicyNumber=pn, **dict_travellers)
    #Add  Policy
    row = app_tables.policies.add_row(PolicyNumber=pn, **dict_policies)
    #Add User Address
    row = app_tables.users.get_current_user(**dict_users)
    #Add Trips
    row = app_tables.mytrips.add_row(PolicyNumber=pn, **dict_mytrips)

I dont know why this would be? If I do a print statement print(pn) it gives me one number 200152 which is the first row in “counter” table.

Ahh do I need to change these rows in the dicts?

"PolicyNumber": "value",