aim of this book is to give | My Assignment Tutor

Google App Engine 3 Python byexampleIntroduction:The aim of this book is to give you some simple examples to follow that showcase what google appengine with python 3 is capable of doing. It is recommended that rather than copy paste out theprograms within they should be typed out by hand. The reason for this is to enable the learner to getused to the structures of a google app engine application before attempting the assignments set outin the course.The documentation and programs you see here is heavily based on the documentation alreadyavailable on http://cloud.google.com/ However this is much expanded and contains explanatorymaterial that can be used to further understanding the workings of google app engine 3.I would recommend that you go through all of the programs here. Understand in depth what ishappening and then proceed to work with assignments after this. As a first step however you willneed to get your environment setup to work with google app engine python.Working Environment:There are a number of things that should be installed and setup before you attempt to run any of theprograms here.• A decent syntax highlighting text editor with support for: Python, HTML, CSS, JS, Yaml,JSON and Git integration. The recommendation here is to use the Github editor Atom whichis highly customisable and has many extensions available.• An installation of python 3.8 that is accessible from anywhere on the command line. i.e.your PATH variable has been modified to find it• An installation of google app engine with the app-engine-python and app-engine-pythonextras installed that is also accessible from anywhere on the command line. i.e. your PATHvariable has been modified to find itOnce you have all of the above installed and setup then you are ready to go with building GoogleApp Engine applicationsChanges since python 2.7The python 2.7 version of App Engine now no longer exists. For those of you who came from suchan environment there will be many adjustments that will be seen here. No longer are you restrictedto webapp2 there is the flexibility to use many Python based web frameworks. The main one usedhere is Flask but Django is also an option. Also the google login/logout service has now beenremoved and in its place is the use of Firebase Authentication using OAuth2 as the authenticationmechanism. The datastore has also been replaced in favour of using Firebase as it is an established,and robust NoSQL database.With all of the above sorted its time to dive into our first example which is the basic hello worldapplication just to get off the ground and started developingExample 01: Hello World in Google App Engine Python3First before we can start with anything we will need to create a python virtual environment as wewill need to install things into it without messing with the local python environment. Theinstructions you see here are taken from:https://cloud.google.com/appengine/docs/standard/python3/quickstartSpecifically the Linux/OSX version. There are Windows versions of these as well on the same page.First open a command line and navigate to the directory where you will write the code for theseexamples. When you get there run the following command:python3 -m venv envThis will create a directory called “env” that contains a python virtual environment that is seperatefrom the regular python environment. After this we will need to run the following commandsource env/bin/activateThis will modify your terminal PATH and other variables to reference the newly created envdirectory first. You may notice after this command that you will see “(env)” before at the start ofyour command line to indicate that you are in a python virtual environment. You will need tonavigate to this directory and run the above two commands everytime you start a terminal to setupthe virtual environment before you can start running app engine applications. The only way to getout of the virtual environment is to exit the terminal when you are finished. Now we can get todeveloping the application by following the given steps01) create a new directory for this example02) inside that directory create a file called requirements.txt and add the following text into itFlask==1.1.2The requirements.txt file is there to tell Google App Engine what additional libraries are needed inorder to run your application. In this case we are stating that we need the Flask web framework andspecifically version 1.1.2 in order to run our application. If we are developing the applicationlocally we will need to install the requirements by hand which will be covered in a later step. Anyadditional libraries you will need must be specified in this file.Note that it is not possible to add any Python library of your choosing to this as Google App Engineruns a restricted version of Python3. Google App Engine may also have a deny-list of libraries thatare not permitted to be installed as well and your chosen library(ies) may appear on that list.03) Create a file called ‘app.yaml’ and place the following code into itruntime: python38The app.yaml file tells google app engine how to setup and initialise your application inpreparation for running. For our first application this is very simple as we are stating that we willuse the Python 3.8 runtime to run our application. In later examples we will have more detailedversions of this file as we will start to add additional details for more complex applications04) create a file called ‘main.py’ and add the following code into itfrom flask import Flaskapp = Flask(__name__)@app.route(‘/’)def hello():return ‘Hello world’if __name__ == ‘__main__’:app.run(host=’127.0.0.1′, port=8080, debug=True)This is the basis of our hello world application. The first line is an import statement that will give usaccess to the Flask framework so we can build our application. The next line defines a Flaskapplication object. This object will be needed to run our application as it contains all the necessarysupport to receive requests and to generate and send replies. The __name__ part takes the name ofthe current file and passes it as part of the Flask constructor.The line that follows this @app.route(‘/’) is a Flask annotation that specifies the following methodshould be called if the URL of the host is called without anything after the / the so called root URL.When you run this application locally and point your browser to http://localhost:8080/ thisannotation will be triggered and the hello() function below it will be called to serve a response.The hello() function is very simple in nature. It will return the string “hello world” to the browser.Note that whatever the value of the return statement is in a function tagged by “@app.route” thatwill be the response that the user will see.Finally we have an if statement that says if this file has been mentioned as part of a call to pythonon the command line the code guarded by this if statement will be run. In this case the app.run()function here will run the application and will set the server at 127.0.0.1 (localhost) on port 8080and debug functionality will be enabled. When you are developing applications debug shouldalways be set to true so you can attach a debugger if needed and also debugging messages will belogged to console.05) open a console and navigate to the project directory. Make sure you have created yourenvironment and sourced it as shown above and run the following commandpip install -r requirements.txtThis will read in the requirements file and will install the necessary libraries06) run your application by executing the following commandpython main.pyThis will start your Flask application and if you navigate to localhost:8080 in your web browseryou will see the message “Hello world” in plain text if everything is working correctly.Example 02: A more complex app with userauthentication and NoSQL storage. Version 1: settingup a file structure and some basic componentsThe second application will be more extensive than the first. It will introduce two core componentsthat are needed in every application that is cloud facing: user authentication and data storage. Bothcomponents will use the external service Firebase. User authentication will use OAuth2 (a standardauthentication method) while data storage will be handled by the NoSQL Firebase database. Notethat we won’t get to that yet but there will be multiple versions of this example that will buildtowards itIf you have not used a NoSQL database before and you are familiar with SQL databases there are afew things you will need to be aware of:• Firebase’s NoSQL database is key-value pair system not a relational table. It is designed forspeed and to support cloud scale applications• As a result of the point above you will not have queries on the same expressive power thatSQL provides. This means you will have to think about how you structure your data as thiswill have an effect on the kind of queries you can perform.• The advantage however though is if you can structure your data to use less queries and usemore direct key access on average your application data access will be quicker than if youused an SQL databaseWe will expose a lot of these point in the development of this application when we get to thedatabase but for now we will follow along with the following google documentation examplehttps://cloud.google.com/appengine/docs/standard/python3/building-app/writing-web-serviceand we will provide detailed notes along the way.01) create a new directory for this application and inside that directory create the followingdirectory and file structure• app.yaml• main.py• requirements.txt• static/◦ script.js◦ style.css• templates/◦ main.htmlThe first three files should be familiar to you from the previous example. However there are nowtwo extra directories. The static directory is there for any static files that you will serve as part ofyour application. Generally we will store JavaScript, CSS, and any static HTML files here alongwith all other static assets. The templates directory will contain HTML templates that are used togenerate HTML pages on the fly. These can be customised with data from your python code to alterthe display of the template each time a request for that template is required. We will fill in code forthese additional files in the next steps and will explain what is happening as we go along.01) first we will define the text in requirements.txt. Like the previous application you should addin the following:Flask==1.1.202) next we will define the code in app.yaml which will be the followingruntime: python38handlers:– url : /staticstatic_dir : static– url : /.*script : autoThe first line is the same as the previous example but we have some additional information here inthe form of handlers. Handlers specify what your application should do in certain scenarios. Herewe have two handlers defined by url. This hander will state what will happen if certain URLs aretriggered by either the user or the application. The first handler will state what will happen if theURL http://localhost:8080/static is triggered. The line below it states that anything that hits thisURL should serve its content directly from the static content directory called static. We will use thisto serve up JS and CSS files. Finally the second of the URL handlers states what happens to anyother URL that is not served by the static handler above. The script : auto command will redirectFlask to find the python script with the appropriate @app.route annotation for the URL provided.Note that this last catch all handler is always required if you are going to serve static content of anydescription. If it is omitted then your application will not work.03) next we will define main.pyimport datetimefrom flask import Flask, render_templateapp = Flask(__name__)@app.route(‘/’)def root():dummy_times = [datetime.datetime(2018, 1, 1, 10, 0, 0),datetime.datetime(2018, 1, 2, 10, 30, 0),datetime.datetime(2018, 1, 3, 11, 0, 0),]return render_template(‘index.html’, times=dummy_times)if __name__ == ‘__main__’:app.run(host=’127.0.0.1′, port=8080, debug=True)At the beginning of the script we will import the datetime library which will be used later in thescript. We also have our definition of the application object as before. Note that we have anadditional import from Flask called render_template. What render_template is designed to do isto trigger HTML templates for dynamic rendering. What this permits us to do is pass python objectsto a HTML template that will be dynamically rendered with whatever data is present in thosepython objects. The templates can be parameterised so you can serve up dynamic content ondifferent requests.In the root() function we have defined some sample data of the datetime class type. We will do thisjust to show how template rendering will work as we have yet to define a datastore of anydescription. Finally on the last line of this function we will call render_template to render thetemplate called index.html (which it will look for in the templates/ directory) and we will pass atimes variable with the dummy_times we defined on the previous line. These times will be used todynamically render the index.html template.04) in templates/index.html add in the following HTMLDatastore and Firebase Auth ExampleLast 10 visits{% for time in times %}{{ time }}{% endfor %}This is your first HTML template. Note that this is a Jinja template we will use as it is thetemplating engine favoured by Flask. Most of the HTML will be familiar to you so we will onlyfocus on the templated parts. First we will start off with the template defined in the script and linktags. Note the form {{ url_for(‘static’, filename=’script.js’) }} and similar for thestyle.css file. This asks the Jinja templating engine to run the url_for() function that will generate aurl out of the base url http://localhost:8080/ (or whatever URL your app is hosted on when runningthe app on GAE in the cloud) it will add static/ to it and it will finally add the filename script.js toit. The resulting URL will be http://localhost:8080/static/script.js and this gets sent back to the Flaskserver. Flask will see the static part and will use the static handler defined in app.yaml to serve thatcontent from the static/ directory you defined as part of your application structure back in step 01).It will do the same for the style.css file as well.The other templating part we will look at is the {% for time in times %} … {% endfor %} part.This asks Jinja to perform a standard python loop for everything defined between for and endfor.Here we are defining a loop variable time which will take a reference to each object in the times listthat was pass as part of the render_template call earlier. It will then run that loop body for eachobject. In this case the loop body is pretty simple we have a paragraph tag with {{ time }} inbetween it. This asks the Jinja to call the __str__() function of the python object and display thestring that it returns in this place. As we have defined three time objects in our times list this loopwill iterate three times.05) put the following code in script.js‘use strict’;window.addEventListener(‘load’, function () {console.log(“Hello World!”);});06) put the following code in style.cssbody {font-family: “helvetica”, sans-serif;text-align: center;}And run the application. It should display a message in the top centre of the view and then threedates and times underneathExample 03: Adding to the previous example to includeaccess to the cloud datastore.In this example we will add access to the datastore as a place to store our data for our application.Here we will use the google cloud datastore which is a NoSQL Key-Value pair database to store allof our application data. Before carrying on with this example the first thing you will need to do iscreate an application instance on Google Cloud by following the details below. At the end of thisyou should have a JSON file that will contain all of your authentication details for the applicationinstance you just created. This JSON file will be required to enable your application to access thedatabasehttps://cloud.google.com/docs/authentication/getU+0074ing-startedIn this example we will introduce some of the most basic operations you will require as part of anyGoogle App Engine application. The ability to write and retrieve data from a database. Here we willdo something very simple. Everytime a visit is made to the root page we will take the currentsystem time and will store it in the datastore. We will also retrieve the last 10 visit times from thedatastore and will display them as part of the template.01) take a copy of the complete code of the previous example into a new directory02) add the following line into requirements.txtgoogle-cloud-datastore==1.15.0This is the python library that will allow our application to interact with, store and retrieve datafrom the datastore.03) in main.py add the following import to the list of importsfrom google.cloud import datastoreThis will bring in the module we will need to access in order to be able to interact with thedatastore. Anytime we want to store or retrieve data we will need to use this datastore module04) in main.py add the following code after the declaration of the app variabledatastore_client = datastore.Client()This will create a datastore client through which we can make requests to do CRUD operations onthe datastore05) in main.py add the following function after the code in 04) but before the definition of root()def store_time(dt):entity = datastore.Entity(key = datastore_client.key(‘visit’))entity.update({‘timestamp’ : dt})datastore_client.put(entity)This method takes in a single parameter dt which will be a python datetime object. We will use thetime defined in this object to store a timestamp of when the user last accessed the page. The firstline of the method defines a datastore Entity object. Entity objects are used to store a collection ofattributes together that belong to one entity. In this case we are defining an entity object of type visitthat will be stored in our datastore. In the following line we are adding an attribute to the entitycalled timestamp that will have the value of dt that was passed into the function. Note that updatewill add attributes if they are not defined in the entity. If the attribute already exists in the entity itwill overwrite the value for that attribute. Finally in the last line we are asking the datastore client tostore the entity in the datastore.06) in main.py add the following function after the code in 05) but before the definition of “root()”def fetch_times(limit):query = datastore_client.query(kind=’visit’)query.order = [‘-timestamp’]times = query.fetch(limit=limit)return timesThis function will retrieve the last limit number of visit entities from the datastore using a verybasic query and will return that set of visit entities back to the caller in a regular python list object.The function starts by asking the datastore client to create an empty query on visit entities. Thiskind of query will return back all visit entities in the datastore. The line immediately after this statesthe order in which we want the visit entities to be returned. The -timestamp part states that we wishto first order by the timestamp attribute with the – indicating that we wish to order in decreasingorder. The next line will then ask the query object to fetch our visit entities from the datastore to thelimit defined by the limit integer that was passed into the function. Finally we return the list of visitentities from the query to the caller of the function.07) replace the “root()” function to have the following codedef root():store_time(datetime.datetime.now())times = fetch_times(10)return render_template(‘index.html’, times=times)Note how we have gotten rid of the dummy times variable we can use the Datastore to get the list oftimes that we wish to display to the user. We start by first calling the store_time() function wedefined in 05) above with the current system time. What this will do is store a new visit entity in thedatastore with the current system time set as the timestamp. The next line will then call ourfetch_times() function that we defined in 06) and it will fetch a maximum of the 10 most recentvisit times. Finally those fetched times are then passed to the render_template() function forindex.html and the times are passed under the variable times.08) Before you go to run this project you will need the JSON file nearby to access the datastore.Before you run your application in your command line you will need to set the session variableGOOGLE_APPLICATION_CREDENTIALS with the location of this JSON file. In my case I havethe JSON file above the directory this project runs in so in Linux I would run the followingcommand to set that session variableexport GOOGLE_APPLICATION_CREDENTIALS=”../app-engine-3-testing.json”where app-engine-3-testing.json is my JSON file. After this run the project as normal andevery time you visit the root page now a new visit time will be added and the list will bedynamically updated.Example 04: adding Firebase authentication to thebasic template.Being able to dynamically store and modify data is one requirement of PaaS applications. However,there will be a need to distinguish information between different users and to be able to segregateaccess and data between different users. In order to do this we will need to have access to anauthentication mechanism for our application. For this we will use Firebase as the authenticationmechanism as it supports a standard authentication method in OAuth2. The first thing you will needto do is add your application to the Firebase console and permit user access to it using the “AddFirebase to your Cloud Application” section of the following page:https://cloud.google.com/appengine/docs/standard/python3/building-app/adding-firebaseOnce you have this done follow the steps below. Once this is setup go to the project settings pageand under the general tab you will find your API key and other parameters that you will need to linkup with Firebase authentication. It will also provide sample javascript that has all of theseparameters filled in so you can copy paste into your code later.01) take a complete copy of the previous application and put it in a new directory02) first we are going to modify templates/index.html and add the following code into the taghttps://www.gstatic.com/firebasejs/ui/4.4.0/firebase-ui-auth.jsThis will pull in the necessary JavaScript and CSS files to allow your user interface to map onto theFirebase authentication services. We will need to add some more to the template however to displaythese services and allow a user to use them.03) add a new file to the static directory called app-setup.js and add in the following codevar firebaseConfig = {apiKey: “”,authDomain: “.firebaseapp.com”,databaseURL: “https://.firebaseio.com”,projectId: “”,storageBucket: “.appspot.com”,messagingSenderId: “”,};firebase.initializeApp(firebaseConfig);This is necessary for attaching firebase to your application. The template above can be autofilled foryou by looking for the sample code in the General tab of the Settings page of the application youdefined in the Firebase console earlier. This segment of code contains your API key and all thenecessary links to enable your application to use Firebase to create and authenticate users. You willneed this for every template you have that requires the use of firebase authentication however, wehave refactored it out here such that we can include it in any template that requires it without havingto copy paste the code each time04) at the bottom of the tag add in the following lineshttps://www.gstatic.com/firebasejs/7.14.5/firebase-app.jshttps://www.gstatic.com/firebasejs/7.8.0/firebase-auth.jshttp://%20url_for(‘static’,%20filename=’app-setup.js’)%20This pulls in additonal scripts that are needed to get firebase authentication up and running. The lastscript import here is to pull in the additonal script we defined in 03) above05) replace the HTML/template part for the last 10 visits with the following HTML/template codeSign outLogin Info{% if user_data %}Name:{{ user_data[‘name’] }}Email:{{ user_data[’email’] }}Last 10 Visits:{% for time in times %}{{ time[‘timestamp’] }}{% endfor %}{% elif error_message %}Error Message: {{ error_message }}{% endif %}There is a lot to process here but most will not be visible until the later steps. The firebase-authcontainer will be used by the firebase scripts for showing login options in a later step. There is alsoa sign-out button that is currently hidden until we get the rest of the firebase authentication sorted.In the following templated code we have now given the template the ability to react to different setsof data being passed to the template. There is a {% if user_data %} that will display the first partof the template between here and {% elif … %} if a user_data object has been passed to thetemplate. If no user_data has been provided but an error_message has been provided then thetemplate under {% error_message %} will be displayed instead. Note that in the user_datatemplate we are going to parameterise the template with the user_data[‘name’] anduser_name[‘email’] parts. This will allow us to show the name and email of the user who is loggedin.06) in script.js clear out the code currently in the function attached to load and replace it with thefollowing codedocument.getElementById(‘sign-out’).onclick = function() {// ask firebase to sign out the userfirebase.auth().signOut();};This will attach a function to the onclick property of the sign-out button defined in our templateearlier. If that button is clicked then it will ask firebase authentication to sign out the currentlylogged in user.07) add the following code to the function attached to load under the code defined in 05) abovevar uiConfig = {signInSuccessUrl: ‘/’,signInOptions: [firebase.auth.GoogleAuthProvider.PROVIDER_ID,firebase.auth.EmailAuthProvider.PROVIDER_ID]};This variable will be used to configure the firebase authentication widget in a later step. But thereare some important parameters that can be explained now. SignInSuccessUrl will tell firebasewhere to redirect to if the sign in was successful. In this case we will redirect tohttp://locahost:8080/ which will be picked up by the root() function in main.py. Secondly thesignInOptions variable contains a list of authentication methods that we will accept for login. Herewe state we can use the Google login mechanism or standard email addresses as a login mechanism.08) add the following code to the function attached to load under the code defined in 06) above.firebase.auth().onAuthStateChanged(function(user) {if(user) {document.getElementById(‘sign-out’).hidden = false;document.getElementById(‘login-info’).hidden = false;console.log(‘Signed in as ${user.displayName} (${user.email})’);user.getIdToken().then(function(token) {document.cookie = “token=” + token;});} else {var ui = new firebaseui.auth.AuthUI(firebase.auth());ui.start(‘#firebase-auth-container’, uiConfig);document.getElementById(‘sign-out’).hidden = true;document.getElementById(‘login-info’).hidden = true;document.cookie = “token=”;}}, function(error) {console.log(error);alert(‘Unable to log in: ‘ + error);});There is a lot of code to discuss here. First however is that this code will only run if theonAuthStateChanged() function is called by firebase. This will only get called if a user has justlogged in or logged out. The attached code is reacting to those events. In function(user) we havetwo branches. The first reacts to a user who just logged in and the second reacts to a user who justlogged out.In the first branch we start by making visible the sign-out button and the login-info divider. Wethen log a message to the console stating which user was logged in. Finally we get the token thatrepresents the user and attach it to a cookie for this document. We will use this authentication tokenfor two purposes: 1. to identify the currently logged in user. 2. to maintain session information forthis user.In the second branch we start by creating a new firebase authentication widget and set it with theparameters defined in 06) above. We then ask firebase to start that UI widget and display it to theuser. In the final part we hide the sign-out button and the login-info divider before clearing thecookie that was previously set.Finally we have an error handling function that will display an alert box if login does not work. Thealert will show the error returned by firebase and that error will also be logged to console.09) in main.py add the following importsimport google.oauth2.id_tokenfrom flask import Flask, render_template, requestfrom google.auth.transport import requestsThis will be needed to read and use the token that we attached as part of the cookie in theindex.html template we recently modified. The requests module will enable us to link up withfirebase authentication in our code. Be very careful as you have two similar imports here requestfrom flask and requests from google.auth.transport. We can’t do much about the naming so youwill need to be careful about the use of either in code.10) add the following code after code for retrieving a datastore clientfirebase_request_adapter = requests.Request()This will be needed to interact with firebase directly so we can verify users at any stage in ourapplication. It is recommeneded that at all times throughout your application requests should bechecked to see what user it relates to at all times.11) Add the following code into the root() function before the call to store timeid_token = request.cookies.get(“token”)error_message = Noneclaims = Nonetimes = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)except ValueError as exc:error_message = str(exc)This code will first request the user token from the cookies in the request object that was sent withthis request. It will also set some other variables to None as not all of these will be set at the sametime. The following if statement will only be run if there was a token in the cookies. What the trycatch block will do is contact the firebase authentication service and will verify that the tokenbelongs to a user of that application. If this is successful it will return us identification of the userwho has that token. Otherwise it will only throw back an error message.12) change the call to render_template in the root() function with the following codereturn render_template(‘index.html’, user_data=claims, times=times,error_message=error_message)This will now render index.html with some additional parameters user_data and error_message.These variables correspond to the calls to user_data and error_message in index.html13) add the following dependencies to requirements.txt install them and run the applicationgoogle-auth==1.21.2requests==2.24.0When you run the application you should be able to register and login to the application. If you arelogged out you should only have an option to login. When you login you should see your userinformation (as put in on registration) along with the last 10 access times and a sign out button.Example 05: tagging visit times to individual users byusing connecting multiple entity types together.In the previous example we got to the point where we could use firebase authentication to registerand login users. We were also able to store data, however the data was not tied down to any specificuser. In this example we will show how using multiple keys connected together we can connectmultiple entity types together. Here will we connect a visit entity to a user entity such that we cantrack the visit times specifically for that user.01) take a complete copy of the previous examples code02) replace the code of the store_time() function with the following codedef store_time(email, dt):entity = datastore.Entity(key = datastore_client.key(‘User’, email, ‘visit’))entity.update({‘timestamp’ : dt})datastore_client.put(entity)This will now tie down a visit entity to a user entity by using an ancestor key as defined in the callto datastore_client.key(). The call to this function states that the first part of the key will identify aUser entity by an email address, while the second part of the key will be an autogenerated key thatwill be used to identify the visit object of that user. The rest of the function is pretty much the samebar there is an additonal email parameter provided as part of the function call.03) replace the code of the fetch_times() function with the following codedef fetch_times(email, limit):ancestor_key = datastore_client.key(‘User’, email)query = datastore_client.query(kind=’visit’, ancestor=ancestor_key)query.order = [‘-timestamp’]times = query.fetch(limit=limit)return timesSimilar to store_times() above we have an email parameter added to the function. But note how atthe start we define an ancestor key for the User entity with an email address. We then modify thequery to say that we only wish to pull back the visit objects that have our ancestor key set, i.e. it willonly pull back the visit entities belonging to this user.04) replace the code in root() with the following codeid_token = request.cookies.get(“token”)error_message = Noneclaims = Nonetimes = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)store_time(claims[’email’], datetime.datetime.now())times = fetch_times(claims[’email’], 10)except ValueError as exc:error_message = str(exc)return render_template(‘index.html’, user_data=claims, times=times,error_message=error_message)Here we have moved the calls to store_time() and fetch_times into the try catch block the reasonfor this is that if we don’t get claims for the user then this code would fail each time.05) define a new file index.yaml and add this code to itindexes:– kind: visitancestor: yesproperties:– name: timestampdirection: descThis file will define an index for the visit entities. Be aware that any entity that has an ancestorrelationship you must define indexes for. In this case we are declaring that the visit entity has anancestor connected to it. We also state that it should be indexed in descending order of timestamp.The reason we do this is that is the main query we will run on visit entities will be timestamps indescending order. This will vastly speed up those queries. You will need to set this on your clouddatastore before your application will function properly. When you run this application you willstart to see visit times belonging to a single user.Example 06: Properties supported by Datastore EntitiesIn this example we will explore the entities in the basic template examples in a lot more detail. Wewill show what properties and datatypes they support. When a user signs in to this simple example aUser object will be created for them and it will be prepopulated with sample data. At a later stagewe will also show how to update some values of an entity by showing how to handle a POSTrequest and entity updates.01) take a copy of the stripped out template and fill in the details for the firebase authenticationscript02) in main.py add in the follow functiondef retrieveUserInfo(claims):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore_client.get(entity_key)return entityThis function will take in a user claims object similar to the previous examples. It will use the emailaddress from this claims object as the identifier for a key for a UserInfo entity. This will tie down aUserInfo object to a specific user login. The following line will then attempt to retrieve the userinfo object from the datastore. One of two things will happen here:• If the entity exists in the datastore then it will be retrieved and returned to you.• If the entity does not exist in the datastore the default value of None will be returned.Finally we return whatever was returned by the get() function to the caller03) in main.py add in the following functiondef createUserInfo(claims):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore.Entity(key = entity_key)entity.update({’email’: claims[’email’],‘name’: claims[‘name’],‘creation_date’: datetime.datetime.now(),‘bool_value’: True,‘float_value’: 3.14,‘int_value’: 10,‘string_value’: ‘this is a sample string’,‘list_value’: [1, 2, 3, 4],‘dictionary_value’: {‘A’ : 1, ‘B’: 2, ‘C’: 3}})datastore_client.put(entity)This function will first create a key for the UserInfo that is tied down to the currently logged inuser’s email address. It will then create a UserInfo Entity using that key. The update methodprovides a dictionary of values to add to this UserInfo object. Here we show most of the datatypessupported by Entity objects. The first two are strings with the user email and name. The next is adatetime object that takes the current system time. Then we have a boolean, float, integer, anotherstring value. These are the basic datatypes supported. The last two values are a python list and apython dictionary which can be composed of the basic datatypes or keys or entities. We will dealwith the latter two in a later example.04) replace the code of the root() function with the following code.def root():id_token = request.cookies.get(“token”)error_message = Noneclaims = Noneuser_info = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)user_info = retrieveUserInfo(claims)if user_info == None:createUserInfo(claims)user_info = retrieveUserInfo(claims)except ValueError as exc:error_message = str(exc)return render_template(‘index.html’, user_data=claims, error_message=error_message,user_info=user_info)In the first section we are declaring our variables as before but now we have removed the timesvariable and replaced it by user_info. Like before we have an if statement checking if there is atoken attached to a cookie and will try to verify the claims of that cookie. However, this is wherethe major difference from the previous example begins. We first try to retrieve the the userinformation from the datastore using the function defined in 03) above. If the user has logged inbefore then that entity will be returned and the code will skip the two lines below the if. However ifthe user is logging in for the first time then we will create a UserInfo object store it in the datastoreand retrieve it again for the purposes of the template. Note how for the template we are passing theuser_info variable to the template instead of the times like the previous examples.05) in index.html change the templated code between the login-info tags to the following:{% if user_data %}Name:{{ user_data[‘name’] }}Email:{{ user_data[’email’] }}Creation Date:{{ user_info[‘creation_date’] }}Boolean Value:{{ user_info[‘bool_value’] }}Float Value:{{ user_info[‘float_value’] }}Int Value:{{ user_info[‘int_value’] }}String Value:{{ user_info[‘string_value’] }}List Value:{{ user_info[‘list_value’] }}Dictionary Value:{{ user_info[‘dictionary_value’] }}{% elif error_message %}Error Message: {{ error_message }}{% endif %}Like the previous example we will render two different templates depending on if a user is loggedin or not. In the logged in section all we are doing here is outputting the values of the user object wecreated for the user as pulled back from the datastore. When you run this application at this pointand login as a user you should see all 8 values output correctly.06) in main.py add in the following functiondef updateUserInfo(claims, new_string, new_int, new_float):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore_client.get(entity_key)entity.update({‘string_value’: new_string,‘int_value’: new_int,‘float_value’: new_float})datastore_client.put(entity)This function has the same form as the createUserInfo function where we create a key and pull inthe entity attached to the current user. However, when it comes to updating the entity we will onlyupdate the values we have new information for. We do not need to reset any of the other values inthe entity that are not changing. Finally we put the new values in the datastore.07) add a new function in main.py with the following code@app.route(‘/edit_user_info’, methods=[‘POST’])def editUserInfo():id_token = request.cookies.get(“token”)error_message = Noneclaims = Noneuser_info = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)new_string = request.form[‘string_update’]new_int = request.form[‘int_update’]new_float = request.form[‘float_update’]updateUserInfo(claims, new_string, new_int, new_float)except ValueError as exc:error_message = str(exc)return redirect(“/”)This method will be responsible for updating the UserInfo entity when a form we will define inindex.html has been submitted. The first thing of note here is the application route. The first part ofthe application route states that this function will be called if the URL points to edit_user_info (ifrunning locally this would be http://localhost:8080/edit_user_info). The second part of thisannotation states that this method will only accept the “POST” HTTP verb. Meaning the functionwill only be triggered if the URL is contacted with a post request.The first part of the function will pull the user claims as before while in the try catch block after theuser has been verified we then use the request.form object to pull back the text associated with thestring_update, int_update, and float_update parts of the form (we will define this in theindex.html template in the next step). Finally we pass this information to the updateUserInfo()function to update the user info with those new values. Once the values have been updated the verylast thing that will happen is a redirect will be called to / which will force update the main page toreflect the new content that was just entered into this user object.08) in index.html add the following code after the tag in the login info section of index.htmlInt update:Float update:String update:This will define a standard HTML form that will provide a place for the user to update the stringvalue, integer value, float value. The three input fields here will be the fields retrieved by therequest.form object mentioned in 07 above. When you run this application now you will see thatyou have additional fields for entering in user data and when you update them they will update notonly in the datastore but also on the page.Example 07: Linking Multiple Entities through the useof Keys.One of the main advantages of a key-value pair database is that access to entities is extremely fast ifyou have the key to the object. Direct Key access tends to be much quicker than using queries. Inthis example we will show a simple address book application where a user will be able to add anddelete addresses from an address book. We will link a user entity and an address entity through a listof keys in the user object. We will also deal with entity deletion here.01) take a complete copy of the stripped out template02) add the following function to main.py.def retrieveUserInfo(claims):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore_client.get(entity_key)return entityThis is the same as the retrieveUserInfo() function in previous examples03) add the following function to main.pydef createUserInfo(claims):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore.Entity(key = entity_key)entity.update({’email’: claims[’email’],‘name’: claims[‘name’],‘address_list’: []})datastore_client.put(entity)This is the same as the createUserInfo() function in previous examples. The only exception here isthat we have an empty list initialised for the address_list property of the UserInfo entity. We willbe storing keys for Address entities in this list04) add the following function to main.pydef retrieveAddresses(user_info):# make key objects out of all the keys and retrieve themaddress_ids = user_info[‘address_list’]address_keys = []for i in range(len(address_ids)):address_keys.append(datastore_client.key(‘Address’, address_ids[i]))address_list = datastore_client.get_multi(address_keys)return address_listThis function will pull out the list of Address entities referenced by the passed in UserInfo entity(user_info). The first two lines will pull the list of keys directly from the user_info entity provided.It will also initalise an empty list where we will store all of our key objects. The reason why we dothis is we will do a single retrieve request with all the keys rather than do individual retrieves foreach key. The reason for this is a single pull with multiple keys is more efficient at this task.The for loop that follows will pull out each key from the list and create a datastore key out of itbefore appending it to the address_keys list. In the final two lines we provide the list of key objectsto get_multi() which will pull back the entities associated with each key. It will then return the listof retrieved entities back to the caller.05) rewrite the root() function to have the following code@app.route(‘/’)def root():id_token = request.cookies.get(“token”)error_message = Noneclaims = Noneuser_info = Noneaddresses = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)user_info = retrieveUserInfo(claims)if user_info == None:createUserInfo(claims)user_info = retrieveUserInfo(claims)addresses = retrieveAddresses(user_info)except ValueError as exc:error_message = str(exc)return render_template(‘index.html’, user_data=claims, error_message=error_message,user_info=user_info, addresses=addresses)This root function is very similar to previous examples. The only changes of note are the following:• In the variable declarations at the start of the function we have added an extra variable for anaddress list called addresses• In the call to render_template at the end we also pass this list of addresses to the template(defined in a future step)• In the try block where we verify our user and pull back their user information we make acall to the retrieveAddresses() function defined in 04) above with the retrieved user_infoobject. We will need the address entities for templating at a later stage06) add the following function to main.pydef createAddress(claims, address1, address2, address3, address4):# 63 bit random number that will serve as the key for this address object. not surewhy the data store doesn’t like 64 bit numbersid = random.getrandbits(63)entity_key = datastore_client.key(‘Address’, id)entity = datastore.Entity(key = entity_key)entity.update({‘address1’: address1,‘address2’: address2,‘address3’: address3,‘address4’: address4})datastore_client.put(entity)return idThis function will be tasked with creating an address entity, persisting it in the datastore andreturning the id of the created entity to the caller of the function. We will need to return this id tothe caller so we can store the id in the relevant UserInfo entity and link the entities together. Tostart with the function will take in five parameters. We have a claims object and four stringsrepresenting a four line address.In the first line of the function we are using a random number generator to generate a 63-bit integer.From what I have read of the google documentation it appears entities support 64-bit keys.However, during testing if I used a 64-bit key there would be buggy behaviour where by it wouldonly sometimes accept an entity with a 64-bit key and store it in the datastore. Other times nothingwould get stored. 63-bit keys appear to get around this issue. We will also return this key at the endof the function to the caller for storage in a UserInfo entity. In the following lines we generate anAddress key with the random id. We then add the four address lines to that entity and store it in thedatastore, before returning the generated id to the caller.07) add the following function to main.pydef addAddressToUser(user_info, id):address_keys = user_info[‘address_list’]address_keys.append(id)user_info.update({‘address_list’: address_keys})datastore_client.put(user_info)This function is used to add an id to an Address entity to the provided UserInfo entity (user_info).In the first two lines we pull the current list of keys from the user and append the new id to this listof keys. After this we update the address_list property of the entity before commiting the updatedUserInfo entity to the datastore.08) add the following function to main.py@app.route(‘/add_address’, methods=[‘POST’])def addAddress():id_token = request.cookies.get(“token”)claims = Noneuser_info = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)user_info = retrieveUserInfo(claims)id = createAddress(claims, request.form[‘address1’],request.form[‘address2’], request.form[‘address3’], request.form[‘address4’])addAddressToUser(user_info, id)except ValueError as exc:error_message = str(exc)return redirect(‘/’)This function will be responsible for taking in an address through a HTML form, creating anAddress entity out of it, and link that entity to the UserInfo entity of the current user. It will listenfor a POST verb on the /add_address relative URL. Like other handlers we will get the id_tokenfrom the user and verify the user before proceeding any further. After the user is verified we usesome of the functions we defined in previous steps. First we pull the UserInfo entity for the currentuser. We then create an Address entity out of the address lines provided in the form (defined in alater step) we then take the id returned to us for the Address entity and attach it to the current user.Once all that is done we then redirect back to the root URL to refresh the list of addresses the userhas.09) add the following function to main.pydef deleteAddress(claims, id):user_info = retrieveUserInfo(claims)address_list_keys = user_info[‘address_list’]address_key = datastore_client.key(‘Address’, address_list_keys[id])datastore_client.delete(address_key)del address_list_keys[id]user_info.update({‘address_list’ : address_list_keys})datastore_client.put(user_info)This function will be used for deleting an address from the current user. The function takes in a userclaims object along with an index to the address key that needs to be removed from the datastore. Inthe first pair of lines we pull the UserInfo entity and the list of address keys from the datastore. Inthe next pair of lines we make a Key object out of the key stored in the list. When that key isprovided to the delete() function it instructs the datastore to remove that Address entity with thegiven key. In the final set of lines we remove the key we just deleted from the UserInfo’s list ofkeys before updating it in the UserInfo object and persisting it to the datastore.10) add the following function to main.py@app.route(‘/delete_address/’, methods=[‘POST’])def deleteAddressFromUser(id):id_token = request.cookies.get(“token”)error_message = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)deleteAddress(claims, id)except ValueError as exc:error_message = str(exc)return redirect(‘/’)This function will be called whenever we wish to delete an address from the current user. The firstthing to note is the @app.route annotation as it looks a little different to the ones you have seenpreviously. Here we have /delete_address/ what this means is that the number that appearsimmediately after /delete_address/ will be treated as an integer and will be passed into the attachedhandler with a variable name of id as seen in the function definition on the line below.Like the other handlers we will first verify our user before proceeding any further. But when theuser is verified we will call our deleteAddress() function with the index that was passed in throughthe URL. e.g. if /delete_address/0 is called id will be 0 if /delete_address/1 is called then id willbe 1 etc. Finally we will redirect to the root to update the list of addresses once the current addresshas been removed.11) In index.html replace the template code between the login-info tags with the following:Login Info{% if user_data %}Name:{{ user_data[‘name’] }}Email:{{ user_data[’email’] }}Address line 1:Address line 2:Address line 3:Address line 4:{% for address in addresses %}Address {{ loop.index – 1 }}Address line 1:{{ address.address1 }}Address line 2:{{ address.address2 }}Address line 3:{{ address.address3 }}Address line 4:{{ address.address4 }}{% endfor %}{% elif error_message %}Error Message: {{ error_message }}{% endif %}The error message section is the same as previous examples howver the user_data section haschanged. Like previous examples the first part uses the provided user data to print out the name andemail address of the currently logged in user. In the second part we have a HTML form that willtake in four lines of text that will form an address. When the submit button on this form is clicked itwill trigger the /add_address URL which will add the address into the current user’s address book.Finally we have a looping template block that has two jobs: to display the addresses that the userhas added to their address book. And also to add delete buttons to each of the addresses should auser wish to delete that address. In both take note of the {{ loop.index – 1 }} part. loop.index is aspecial variable maintained by Jinja noting the current iteration of the for loop. It starts at 1 ratherthan zero, hence the -1 part to bring it back to zero. We use this loop index to setup the dynamicaddresses for deletion. On the first pass /delete_address/{{ loop.index – 1 }} will become/delete_address/0. On the second pass this becomes /delete_address/1 and so on.When you run this application you should be able to add and remove addresses from theapplication. The data should also be segregated between users. While it looks like a lot of effort tosetup keys the speed up is worth it as direct key access is always quicker than running a query. Thiskind of direct key access is useful in many situations particularly where you may have a recursiverelationship between entities of the same type.Example 08: Linking multiple entities together throughsub entitiesIn this example we will take the previous example and implement it through the use of sub entitiesrather than through keys. The advantage of an approach like this is that when the UserInfo entity isretrieved, it will also retrieve all of the address entities at the same time. The downside is thatentities will take longer to retrieve and update as you are pulling back entire entities at the sametime rather than keys. The decision to use subentities or keys to link multiple entities togethershould be on a case by case basic. You will need to think carefully about how data is accessed andqueried to determine which is the best approach to use.A lot of the code in this example will be very similar to the previous example and we will note anychanges as we go along.01) take a complete copy of the stripped out template02) in main.py add the following functiondef retrieveUserInfo(claims):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore_client.get(entity_key)return entityThis is the same as the previous example03) in main.py add the following functiondef createUserInfo(claims):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore.Entity(key = entity_key)entity.update({’email’: claims[’email’],‘name’: claims[‘name’],‘address_list’: []})datastore_client.put(entity)This is the same as the previous example04) in main.py rewrite the root() function to have the following code@app.route(‘/’)def root():id_token = request.cookies.get(“token”)error_message = Noneclaims = Noneuser_info = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)user_info = retrieveUserInfo(claims)if user_info == None:createUserInfo(claims)user_info = retrieveUserInfo(claims)except ValueError as exc:error_message = str(exc)return render_template(‘index.html’, user_data=claims, error_message=error_message,user_info=user_info)The big difference here between this and the previous example is that we no longer use a function toretrieve the addresses of the user. As they will be sub entities contained within the UserInfo entitythey will be automatically retrieved when we retrieve the UserInfo entity.05) add the following function to main.pydef createAddress(address1, address2, address3, address4):entity = datastore.Entity()entity.update({‘address1’: address1,‘address2’: address2,‘address3’: address3,‘address4’: address4})return entityThis createAddress() function is a little different to the createAddress() function in the previousexample. The first difference is that we no longer create a key for or store the entity directly in thedatastore. Here we create a simple entity with all the address fields we require and will return it tothe caller. The intention here is that when the entity is added to the UserInfo object we do not needto do a seperate put command for the Address or need a key for it as it will be put into the datastorewhen we put the UserInfo entity into the datastore.06) add the following function to main.pydef addAddressToUser(user_info, address_entity):addresses = user_info[‘address_list’]addresses.append(address_entity)user_info.update({‘address_list’: addresses})datastore_client.put(user_info)This function will take in two parameters: the retrieved user information from the data store, and thenew address object that is to be added to this user. The function will then first retrive the list ofaddress entities from the current user info and will then append the new address entity to it. As thelist has been updated the updated user info object must then be put in the datastore to store thechanges that have been made.07) add the following function to main.py@app.route(‘/add_address’, methods=[‘POST’])def addAddress():id_token = request.cookies.get(“token”)claims = Noneuser_info = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)user_info = retrieveUserInfo(claims)address = createAddress(request.form[‘address1’], request.form[‘address2’],request.form[‘address3’], request.form[‘address4’])addAddressToUser(user_info, address)except ValueError as exc:error_message = str(exc)return redirect(‘/’)This function will be responsible for taking a new address object from the user and appending it tothe current user’s information in the datastore. It is set to listen for POST requests on the/add_address relative URL. Like the root() function defined earlier the first things the function willdo is pull the access token and verify the user’s claims before attempting anything else. Should theuser claims work out we will then retrieve the user’s data from the datastore and then create anaddress object out a form (to be defined later) that has been filled in by the user. Normally thiswould be a good place to do data validation on any input that comes in through a form but this isomitted for the sake of the example. We then add this address using the addAddressToUser()function defined in 06) above. Assuming all is ok we will then redirect back to the root page toforce the user display to update with the new address.08) in main.py add in the following functiondef deleteAddress(claims, id):user_info = retrieveUserInfo(claims)address_list = user_info[‘address_list’]del address_list[id]user_info.update({‘address_list’ : address_list})datastore_client.put(user_info)This function will be used to delete an address from a user object. It takes in two parameters onebeing the claims of the user so we can retrieve the user object and the other being an index into thelist of addresses that the user wants to remove. The first two lines will pull out the user info objectand will then get the address list of the user. The del command on the next line is the command thatwill actually remove the address from the address list. Note that this matches standard pythonsyntax for removing an element from a python list. We then update the address list of the user andput it back in the datastore to store the updated changes.09) in main.py add in the following function@app.route(‘/delete_address/’, methods=[‘POST’])def deleteAddressFromUser(id):id_token = request.cookies.get(“token”)error_message = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)deleteAddress(claims, id)except ValueError as exc:error_message = str(exc)return redirect(‘/’)This function will be responsible for removing an address when it is requested by the user. It is alsoan example of something called a dynamic route in Flask. Note the URL of the route in the attachedannotation /delete_address/ What this means to Flask is that if a URL is passed to/delete_address/ with an integer added after it then that request will be redirected to this functionregardless of what integer is place there. The states that an integer should be expected andif found should be assigned the variable name id. This variable id then gets passed into theparameter list of the function itself.Like previous routes that we have defined we will first check the user claims before doing anyfunctionality. Once verified we will call the deleteAddress() function defined in 08) above toremove the address from the user before redirecting to the root URL to refresh the pageautomatically for the user.10) In index.html replace the template code between the login-info tags with the followingLogin Info{% if user_data %}Name:{{ user_data[‘name’] }}Email:{{ user_data[’email’] }}Address line 1:Address line 2:Address line 3:Address line 4:{% for address in user_info[‘address_list’] %}Address {{ loop.index – 1 }}Address line 1:{{ address.address1 }}Address line 2:{{ address.address2 }}Address line 3:{{ address.address3 }}Address line 4:{{ address.address4 }}{% endfor %}This code is pretty similar to the code for the template as the last example. There are no significantchanges here.Example 09: Different ways of adding and deletingmultiple entities to/from the data store.Up to now we have only dealt with adding, updating, or deleting one entity at a time with thedatastore. However, to save on API calls and also to offer the potential for speedup there aremultiple different ways that can be used to add or remove entities. The last two Batching andTransactions allow you to combine multiple operations of different types either for speedup orconsistency purposes respectively.In this example we will show with the use of dummy data how all of thee operations function.Please note that in order to take advantage of these operations you will need to have the physicalentities themselves or have the keys for accessing those entities.NOTE: that for this example you will need to have a look at the datastore directly to see thechanges being made as we will not display them directly on the returned page.01) take a complete copy of the stripped out template02) in main.py add the following functiondef createUserInfo(claims):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore.Entity(key = entity_key)entity.update({’email’: claims[’email’],‘name’: claims[‘name’],‘address_list’: []})datastore_client.put(entity)This is the same as previous examples03) in main.py add the following functiondef retrieveUserInfo(claims):entity_key = datastore_client.key(‘UserInfo’, claims[’email’])entity = datastore_client.get(entity_key)return entityThis is the same as previous examples04) in main.py add the following function@app.route(‘/’)def root():id_token = request.cookies.get(“token”)error_message = Noneclaims = Nonetimes = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)user_info = retrieveUserInfo(claims)if user_info == None:createUserInfo(claims)user_info = retrieveUserInfo(claims)except ValueError as exc:error_message = str(exc)return render_template(‘index.html’, user_data=claims, error_message=error_message)This is very similar to previous examples05) add the following function to main.pydef createDummyData(number):entity_key = datastore_client.key(‘DummyData’, number)entity = datastore.Entity(key = entity_key)entity.update({‘number’: number,‘squared’: number ** 2,‘cubed’: number ** 3})return entityThis function will be responsible for creating an entity that we will use as part of our batchoperations. It’s a simple entity that will store a number it’s square and also its cube. We will use thenumber itself as the id for the entity. This will allow us to create multiple independent entities thatcan be used as part of a batch or a transaction. This will be an important detail later on.06) add the following function to main.py@app.route(‘/multi_add’, methods=[‘POST’])def multiAdd():id_token = request.cookies.get(“token”)error_message = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)entity_1 = createDummyData(1)entity_2 = createDummyData(2)entity_3 = createDummyData(3)entity_4 = createDummyData(4)datastore_client.put_multi([entity_1, entity_2, entity_3, entity_4])except ValueError as exc:error_message = str(exc)return redirect(‘/’)This is similar in structure to the root function but there are some small differences. For a start thiswill listen for a post action on the /multi_add relative URL. After checking user claims this willgenerate four DummyData entities. Once the entities are created it will then create a list out of allfour entities before passing this list to the put_multi() function. This function will take a list ofentities (they don’t all have to share the same type) and in a single API call submit them to thedatastore. This will be quicker than sending four seperate put requests and will save on API callstoo.07) add the following function in main.py@app.route(‘/batch_add’, methods=[‘POST’])def batchAdd():id_token = request.cookies.get(“token”)error_message = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)entity_5 = createDummyData(5)entity_6 = createDummyData(6)entity_7 = createDummyData(7)entity_8 = createDummyData(8)batch = datastore_client.batch()with batch:batch.put(entity_5)batch.put(entity_6)batch.put(entity_7)batch.put(entity_8)except ValueError as exc:error_message = str(exc)return redirect(‘/’)This is similar to 06) above however the difference here is that we are using a batch operation tostore the entities instead of put_multi(). We also have the additional with syntax to be aware of.Prior to this syntax we use the batch() function of the datastore client to prepare a batch operationfor us. The with batch: syntax is used to setup a batch operation without having to write thenecessary try catch functionality as it is handled by this syntax through the use of aContextManager in the background. Whatever operations are put here (and this can be a mix ofput, delete, or other operations) will be collected into on operation. When the end of the with blockis reached the batch operation is then sent to the datastore to be completed. Like put_multi this willreduce the time needed and the number of API calls needed to commit these changes to thedatastore. However there is one additional benefit to this. In a batch operation if there are multipleobjects that are independent of each other (are not linked or tied to each other) then they will becommitted in parallel and the overall set of operations will complete in a shorter timeframe.08) in main.py add the following function@app.route(‘/transaction_add’, methods=[‘POST’])def transactionAdd():id_token = request.cookies.get(“token”)error_message = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)entity_9 = createDummyData(9)entity_10 = createDummyData(10)entity_11 = createDummyData(11)entity_12 = createDummyData(12)transaction = datastore_client.transaction()with transaction:transaction.put(entity_9)transaction.put(entity_10)transaction.put(entity_11)transaction.put(entity_12)except ValueError as exc:error_message = str(exc)return redirect(‘/’)This is similar to 07) above however the key difference here is that we are using a transaction tostore the entities instead of a batch operation. The difference being is that a transaction will takeadditional steps to ensure all data is safely and consistently committed to the datastore. Should oneof the operations fail the entire transaction will be rolled back to ensure the datastore remains in aconsistent state.09) add the following function into main.py@app.route(‘/multi_delete’, methods=[‘POST’])def multiDelete():id_token = request.cookies.get(“token”)error_message = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)entity_1_key = datastore_client.key(‘DummyData’, 1)entity_2_key = datastore_client.key(‘DummyData’, 2)entity_3_key = datastore_client.key(‘DummyData’, 3)entity_4_key = datastore_client.key(‘DummyData’, 4)datastore_client.delete_multi([entity_1_key, entity_2_key, entity_3_key,entity_4_key])except ValueError as exc:error_message = str(exc)return redirect(‘/’)This is similar to 06) above with a couple of significant differences. The first being that instead ofgenerating entities for the dummy data we are only generating keys instead. Subsequently we arethen calling the delete_multi() function with that list of keys to remove all four entities at once.Again this will save on API calls and will be quicker than issuing four seperate delete operations atonce.10) add the following code into main.py@app.route(‘/batch_delete’, methods=[‘POST’])def batchDelete():id_token = request.cookies.get(“token”)error_message = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)entity_5_key = datastore_client.key(‘DummyData’, 5)entity_6_key = datastore_client.key(‘DummyData’, 6)entity_7_key = datastore_client.key(‘DummyData’, 7)entity_8_key = datastore_client.key(‘DummyData’, 8)batch = datastore_client.batch()with batch:batch.delete(entity_5_key)batch.delete(entity_6_key)batch.delete(entity_7_key)batch.delete(entity_8_key)except ValueError as exc:error_message = str(exc)return redirect(‘/’)This is similar to 07) above however like the previous step we are using the batch to delete multipleentities in the same batch. Similar to addition if these entities are independent of each other then thedeletions will occur in parallel.11) add the following function into main.py@app.route(‘/transaction_add’, methods=[‘POST’])def transactionAdd():id_token = request.cookies.get(“token”)error_message = Noneif id_token:try:claims = google.oauth2.id_token.verify_firebase_token(id_token,firebase_request_adapter)entity_9 = createDummyData(9)entity_10 = createDummyData(10)entity_11 = createDummyData(11)entity_12 = createDummyData(12)transaction = datastore_client.transaction()with transaction:transaction.put(entity_9)transaction.put(entity_10)transaction.put(entity_11)transaction.put(entity_12)except ValueError as exc:error_message = str(exc)return redirect(‘/’)This is similar to 08) above and like the previous two steps we are using the transaction to delete theentities here. If one of the deletes fails along the way the transaction will detect this and willrollback the transaction to the last known good consistent state.12) change the contents of the login-info tags to have the following HTML codeLogin Info{% if user_data %}Name:{{ user_data[‘name’] }}Email:{{ user_data[’email’] }}Batch add four entities through put_multi: Batch add four entities through batch: Batch add four entities through transaction: Batch delete four entities through delete_multi: Batch delete four entities through batch: Batch delete four entities through transaction: {% elif error_message %}Error Message: {{ error_message }}{% endif %}This will simply add 6 buttons to trigger the add and delete functionality of the 6 functions weadded in the previous steps. After you click each one make sure you check the datastore to see ifthose changes occurred.Example 10: Basic queries with the datastoreIn this example we will show how some basic queries can be performed with the datastore. Onething you should be aware of in advance is that the expressive power you may be used to with SQLwill not be available here. The queries can only be done on a single entity type and only permit theoperators of >, =, and =,

QUALITY: 100% ORIGINAL PAPER – NO PLAGIARISM – CUSTOM PAPER

Leave a Reply

Your email address will not be published. Required fields are marked *