My Assessments Function¶
Now that we have cached the user data, our web app is fasters, but there are still some places where accessing the database slows it down.
If we look at the assessment_service we will notice two functions which retrieve data from the database:
get_assessment
get_chart
This implies that there are two more sets of data that we can cache. In this tutorial we will look to cache the get_assessment data, and we will deal with the get_chart data next tutorial.
Planning¶
The impact of using the get_assessment method is not as significant as get_user. It is only used in the HomeComponent. Never-the-less, this will be called every time the user goes to the HomeComponent, so it will still have an impact.
We will use the same process that we used for the get_user method:
Create a variable to store assessment data in the data_access module.
Create a frontend method that caches the assessment data if it is not currently cached, and then returns the cached data.
Replace calls to get_assessment with calls to the new frontend method.
Lets get to it.
Code¶
Create the caching method¶
First we need to make a new variable to cache the assessment data.
Open the data_access module,
At the top, under the
cached values
section add the highlighted code
7# cached values
8__user = None
9__assessments = None
Code explaination
line 9 → creates the private variable
__assessments
to store the assessment data
Now to create the method
At the bottom of the data_access module add the following highlighted code.
35def my_assessment():
36 global __assessments
37
38 if __assessments:
39 print("Using cached assessments")
40 return __assessments
41
42 print("Accessing assessments from database")
43 __assessments = anvil.server.call('get_assessment', user)
44 return __assessments
Code explaination
line 35 → creates the my_assessment method
line 36 → allows the method to edit the value of
__assessments
line 38 → checks if the assessment data is already cached
line 39 → informs the developer that cached data is being used
line 40 → returns the cached data and ends the method
line 42 → informs the developed that the database is being accessed
line 43 → retrieves the assessment data from the database and caches it
line 44 → returned the cached data
Replace calls to get_assessment¶
Now we need to replace the calls to get_assessment
Open HomeComponent in Code mode
In the import section, add the highlighted code
1from ._anvil_designer import HomeComponentTemplate
2from anvil import *
3import anvil.server
4import anvil.tables as tables
5import anvil.tables.query as q
6from anvil.tables import app_tables
7import anvil.users
8from .. import data_access
Code explaination
line 8 → gives HomeComponent access to the data_access methods
Insert the highlighted code at the bottom of the
__init__
method
11 def __init__(self, **properties):
12 # Set Form properties and Data Bindings.
13 self.init_components(**properties)
14
15 # Any code you write here will run before the form opens.
16 self.repeating_panel_1.items = data_access.my_assessment()
Code explaination
line 16 → uses our new my_assessment method to provide the data for the repeating panels.
Testing¶
Time to test your web site and see if our changes worked.
Testing:
Launch your web app
Wait for the Home page to load - this should take some time
Go to the Account page - this should load instantly
Go back to the Home page - this should load instantly
Go to the Add page - this should load instantly
Add a new assessment item
Go back to the Home page
Check if the assessment item is there - spoiler, it won’t be
Fixing the add assessment problem¶
The problem with adding assessment, is similar to the one we had with changing user data. When we add a new assessment, it gets written to the database, but the cached assessment data does not change. We need to fix this.
Open the data_access module
Insert the highlighted code into the bottom of the module
46def add_assessment(subject, details, start_date, due_date):
47 global __assessments
48
49 print("Writing assessment details to the database")
50 anvil.server.call('add_assessment', subject, details, start_date, due_date)
51 __assessments = None
52 my_assessment()
Code explaination
line 46 → creates the add_assessment method, which requires subject, details, start_date and due_date to be passed
line 47 → allows the method to edit the value of
__assessments
line 49 → informs the developed that the database is being accessed
line 50 → writes the new assessment details to the database
line 51 → resets the
__assessment
value toNone
line 52 → caches the updated assessment list
Now we need to replace the call to the database, with a call to our new method.
Open AddComponent in Code mode
Replace line 47 with the highlighted code below
31 def button_add_click(self, **event_args):
32 # validation
33 if not self.text_box_subject.text:
34 self.display_error("Subject name needed")
35 elif not self.text_box_details.text:
36 self.display_error("Assessment details needed")
37 elif not self.date_picker_start.date:
38 self.display_error("Start date needed")
39 elif not self.date_picker_due.date:
40 self.display_error("Due date needed")
41 else:
42 self.subject = self.text_box_subject.text
43 self.details = self.text_box_details.text
44 self.start = self.date_picker_start.date
45 self.due = self.date_picker_due.date
46 self.display_save(f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded")
47 data_access.add_assessment(self.subject, self.details, self.start, self.due)
48 self.reset_form()
Code explaination
line 47 → calls our new caching method and passes the required values
Second test¶
Lets check that this worked.
Launch your website and navigate to the Add page.
Add a new assessment
Return to the Home page
Check if your new assessment is shown
Good. But are there more places where our cache can cause problem? When else do we write data to the database? How about when we change assessment details?
Launch your web app
Edit the value of one of the assessments → good, it changes
Navigate to the Add page
Return to the Home → it’s the previous value
Again this is the result of changing the database values without updating the cache. So lets fix that too.
Fix update assessment problem¶
Just like before, we will create a method in the data_access module that writes to the database and then reloads the cache.
Open the data_access module
At the bottom of the module add the highlighted code
54def update_assessment(assessment_id, subject, details, start_date, due_date, completed):
55 global __assessments
56
57 print("Updating assessment details on the database")
58 anvil.server.call('update_assessment',
59 assessment_id,
60 subject,
61 details,
62 start_date,
63 due_date,
64 completed
65 )
66 __assessments = None
67 my_assessment()
Code explaination
line 54 → creates the update_assessment method with all the required data to be passed
line 55 → allows the method to change the
__assessments
valueline 57 → lets the developer know that the database is being accessed
line 58 - 65 → writes the changes to the database
line 66 → resets the
__assessments
value toNone
line 67 → caches the assessments data
Open the AssessmentPanel
In the import section add the highlighted line
1from ._anvil_designer import AssessmentPanelTemplate
2from anvil import *
3import anvil.server
4import anvil.users
5import anvil.tables as tables
6import anvil.tables.query as q
7from anvil.tables import app_tables
8import datetime
9from ... import data_access
Code explaination
line 9 → gives HomeComponent access to the data_access methods
In the button_save_click handler replace line 37 - 44 with the highlighted code below
35 def button_save_click(self, **event_args):
36 # write to server
37 data_access.update_assessment(self.item.get_id(),
38 self.text_box_subject.text,
39 self.text_box_details.text,
40 self.date_picker_start.date,
41 self.date_picker_due.date,
42 self.check_box_completed.checked
43 )
44
45 # update display
46 self.label_subject.text = self.text_box_subject.text
47 self.label_details.text = self.text_box_details.text
48 self.label_start.text = self.date_picker_start.date.strftime('%d/%m/%Y')
49 self.label_due.text = self.date_picker_due.date.strftime('%d/%m/%Y')
50 self.switch_components()
Code explaination
line 37-43 → uses our new caching method to make changes to the assessment data
Third test¶
Lets check our latest iteration of code
Launch you website
Change an assessment’s detail
Navigate to another page
Return to Home
Check if your change is displayed.
It should all be working now.
Final code state¶
By the end of this tutorial your code should be the same as below:
Final data_access¶
1import anvil.server
2import anvil.users
3import anvil.tables as tables
4import anvil.tables.query as q
5from anvil.tables import app_tables
6
7# cached values
8__user = None
9__assessments = None
10
11def the_user():
12 global __user
13
14 if __user:
15 print("Using cached user")
16 return __user
17
18 print("Accessing user from database")
19 __user = anvil.users.get_user()
20 return __user
21
22def logout():
23 global __user
24 __user = None
25 anvil.users.logout()
26
27def update_user(first_name, last_name):
28 global __user
29
30 print("Writing user details to database")
31 anvil.server.call('update_user', first_name, last_name)
32 __user = None
33 __user = the_user()
34
35def my_assessment():
36 global __assessments
37
38 if __assessments:
39 print("Using cached assessments")
40 return __assessments
41
42 print("Accessing assessments from database")
43 __assessments = anvil.server.call('get_assessment')
44 return __assessments
45
46def add_assessment(subject, details, start_date, due_date):
47 global __assessments
48
49 print("Writing assessment details to the database")
50 anvil.server.call('add_assessment', subject, details, start_date, due_date)
51 __assessments = None
52 my_assessment()
53
54def update_assessment(assessment_id, subject, details, start_date, due_date, completed):
55 global __assessments
56
57 print("Updating assessment details on the database")
58 anvil.server.call('update_assessment',
59 assessment_id,
60 subject,
61 details,
62 start_date,
63 due_date,
64 completed
65 )
66 __assessments = None
67 my_assessment()
Final HomeComponent¶
1from ._anvil_designer import HomeComponentTemplate
2from anvil import *
3import anvil.server
4import anvil.tables as tables
5import anvil.tables.query as q
6from anvil.tables import app_tables
7import anvil.users
8from .. import data_access
9
10class HomeComponent(HomeComponentTemplate):
11 def __init__(self, **properties):
12 # Set Form properties and Data Bindings.
13 self.init_components(**properties)
14
15 # Any code you write here will run before the form opens.
16 self.repeating_panel_1.items = data_access.my_assessment()
Final AddComponent¶
1from ._anvil_designer import AddComponentTemplate
2from anvil import *
3import anvil.server
4import anvil.tables as tables
5import anvil.tables.query as q
6from anvil.tables import app_tables
7import anvil.users
8from .. import data_access
9
10
11class AddComponent(AddComponentTemplate):
12 def __init__(self, **properties):
13 # Set Form properties and Data Bindings.
14 self.init_components(**properties)
15 self.subject = ""
16 self.details = ""
17 self.start = None
18 self.due = None
19
20 # Any code you write here will run before the form opens.
21 self.label_message.visible = False
22 if data_access.the_user():
23 self.card_details.visible = True
24 self.card_error.visible = False
25 self.button_add.visible = True
26 else:
27 self.card_details.visible = False
28 self.card_error.visible = True
29 self.button_add.visible = False
30
31 def button_add_click(self, **event_args):
32 # validation
33 if not self.text_box_subject.text:
34 self.display_error("Subject name needed")
35 elif not self.text_box_details.text:
36 self.display_error("Assessment details needed")
37 elif not self.date_picker_start.date:
38 self.display_error("Start date needed")
39 elif not self.date_picker_due.date:
40 self.display_error("Due date needed")
41 else:
42 self.subject = self.text_box_subject.text
43 self.details = self.text_box_details.text
44 self.start = self.date_picker_start.date
45 self.due = self.date_picker_due.date
46 self.display_save(f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded")
47 data_access.add_assessment(self.subject, self.details, self.start, self.due)
48 self.reset_form()
49
50 def display_error(self, message):
51 self.label_message.visible = True
52 self.label_message.foreground = "#ff0000"
53 self.label_message.icon = "fa:exclamation-triangle"
54 self.label_message.bold = True
55 self.label_message.text = message
56
57 def display_save(self, message):
58 self.label_message.visible = True
59 self.label_message.foreground = "#000000"
60 self.label_message.icon = "fa:save"
61 self.label_message.bold = False
62 self.label_message.text = message
63
64 def reset_form(self):
65 self.subject = ""
66 self.details = ""
67 self.start = None
68 self.due = None
69 self.text_box_subject.text = ""
70 self.text_box_details.text = ""
71 self.date_picker_start.date = None
72 self.date_picker_due.date = None
Final AssessmentPanel¶
from ._anvil_designer import AssessmentPanelTemplate from anvil import * import anvil.server import anvil.users import anvil.tables as tables import anvil.tables.query as q from anvil.tables import app_tables import datetime from … import data_access
1class AssessmentPanel(AssessmentPanelTemplate):
2 def __init__(self, **properties):
3 # Set Form properties and Data Bindings.
4 self.init_components(**properties)
5
6 # Any code you write here will run before the form opens.
7 self.check_box_completed.checked = self.item['completed']
8 self.label_subject.text = self.item['subject']
9 self.label_details.text = self.item['details']
10 self.label_start.text = self.item['start_date'].strftime('%d/%m/%Y')
11 self.label_due.text = self.item['due_date'].strftime('%d/%m/%Y')
12
13 def check_box_completed_change(self, **event_args):
14 new_value = self.check_box_completed.checked
15 anvil.server.call('update_assessment_completed', self.item.get_id(),new_value)
16
17 def button_edit_click(self, **event_args):
18 self.text_box_subject.text = self.item["subject"]
19 self.text_box_details.text = self.item["details"]
20 self.date_picker_start.date = self.item["start_date"]
21 self.date_picker_due.date = self.item["due_date"]
22 self.switch_components()
23
24 def button_save_click(self, **event_args):
25 # write to server
26 data_access.update_assessment(self.item.get_id(),
27 self.text_box_subject.text,
28 self.text_box_details.text,
29 self.date_picker_start.date,
30 self.date_picker_due.date,
31 self.check_box_completed.checked
32 )
33
34 # update display
35 self.label_subject.text = self.text_box_subject.text
36 self.label_details.text = self.text_box_details.text
37 self.label_start.text = self.date_picker_start.date.strftime('%d/%m/%Y')
38 self.label_due.text = self.date_picker_due.date.strftime('%d/%m/%Y')
39 self.switch_components()
40
41 def switch_components(self):
42 # display elements
43 self.label_subject.visible = not self.label_subject.visible
44 self.label_details.visible = not self.label_details.visible
45 self.label_start.visible = not self.label_start.visible
46 self.label_due.visible = not self.label_due.visible
47 self.button_edit.visible = not self.button_edit.visible
48
49 # edit elements
50 self.text_box_subject.visible = not self.text_box_subject.visible
51 self.text_box_details.visible = not self.text_box_details.visible
52 self.date_picker_start.visible = not self.date_picker_start.visible
53 self.date_picker_due.visible = not self.date_picker_due.visible
54 self.button_save.visible = not self.button_save.visible