One place for hosting & domains

      Library

      How To Get Started With the Requests Library in Python


      Introduction

      In many web apps, it’s normal to connect to various third-party services by using APIs. When you use these APIs you can get access to data like weather information, sports scores, movie listings, tweets, search engine results, and pictures. You can also use APIs to add functionality to your app. Examples of these are payments, scheduling, emails, translations, maps, and file transfers. If you were to create any of those on your own it would take a ton of time, but with APIs, it can take only minutes to connect to one and access its features and data.

      In this article, we’ll learn about the Python Requests library, which allows you to send HTTP requests in Python.

      And since using an API is sending HTTP requests and receiving responses, Requests allows you to use APIs in Python. We’ll demonstrate the use of a language translation API here so you can see an example of how it works.

      Quick Overview of HTTP Requests

      HTTP requests are how the web works. Every time you navigate to a web page, your browser makes multiple requests to the web page’s server. The server then responds with all the data necessary to render the page, and your browser then actually renders the page so you can see it.

      The generic process is this: a client (like a browser or Python script using Requests) will send some data to a URL, and then the server located at the URL will read the data, decide what to do with it, and return a response to the client. Finally, the client can decide what to do with the data in the response.

      Part of the data the client sends in a request is the request method. Some common request methods are GET, POST, and PUT. GET requests are normally for reading data only without making a change to something, while POST and PUT requests generally are for modifying data on the server. So for example, the Stripe API allows you to use POST requests to create a new charge so a user can purchase something from your app.

      Note: This article will cover GET requests, because we won’t be modifying any data on a server.

      When sending a request from a Python script or inside a web app, you, the developer, gets to decide what gets sent in each request and what to do with the response. So let’s explore that by first sending a request to Scotch.io and then by using a language translation API.

      Install Python Requests

      Before we can do anything, we need to install the library. So let’s go ahead and install requests using pip. It’s a good idea to create a virtual environment first if you don’t already have one.

      Our First Request

      To start, let’s use Requests for requesting the Scotch.io site. Create a file called script.py and add the following code to it. In this article, we won’t have much code to work with, so when something changes you can just update the existing code instead of adding new lines.

      script.py

      import requests
      
      res = requests.get('https://scotch.io')
      
      print(res)
      

      So all this code is doing is sending a GET request to Scotch.io. This is the same type of request your browser sent to view this page, but the only difference is that Requests can’t actually render the HTML, so instead you will just get the raw HTML and the other response information.

      We’re using the .get() function here, but Requests allows you to use other functions like .post() and .put() to send those requests as well.

      You can run it by executing the script.py file.

      And here’s what you get in return:
      script run with output of `Response 200`

      Status Codes

      The first thing we can do is check the status code. HTTP codes range from the 1XX to 5XX. Common status codes that you have probably seen are 200, 404, and 500.

      Here’s a quick overview of what each status code means:

      • 1XX - Information
      • 2XX - Success
      • 3XX - Redirect
      • 4XX - Client Error (you made an error)
      • 5XX - Server Error (they made an error)

      Generally, what you’re looking for when you perform your own requests are status codes in the 200s.

      Requests recognizes that 4XX and 5XX status codes are errors, so if those status codes get returned, the response object from the request evaluates to False.

      You can test if a request responded successfully by checking the response for truth. For example:

      script.py

      if res:
          print('Response OK')
      else:
          print('Response Failed')
      

      The Response 200 output with a follow up of Response OK

      The message “Response Failed” will only appear if a 400 or 500 status code returns. Try changing the URL to some nonsense to see the response fail with a 404.

      You can take a look at the status code directly by adding:

      script.py

      print(res.status_code)
      

      This will show you the status code directly so you can check the number yourself.

      Failure output with 404

      Another thing you can get from the response are the headers. You can take a look at them by using the headers dictionary on the response object.

      script.py

      print(res.headers)
      

      output with headers printed in standard out

      Headers are sent along with the request and returned in the response. Headers are used so both the client and the server know how to interpret the data that is being sent and received in the response/response.

      We see the various headers that are returned. A lot of times you won’t need to use the header information directly, but it’s there if you need it.

      The content type is usually the one you may need because it reveals the format of the data, for example HTML, JSON, PDF, text, etc. But the content type is normally handled by Requests so you can access the data that gets returned.

      Response Text

      And finally, if we take a look at res.text (this works for textual data, like a HTML page like we are viewing) we can see all the HTML needed to build the home page of Scotch. It won’t be rendered, but we see that it looks like it belongs to Scotch. If you saved this to a file and opened it, you would see something that resembled the Scotch site. In a real situation, multiple requests are made for a single web page to load things like images, scripts, and stylesheets, so if you save only the HTML to a file, it won’t look anything like what the Scotch.io page looks like in your browser because only a single request was performed to get the HTML data.

      script.py

      print(res.text)
      

      Printed HTML data on the command line

      Using the Translate API

      So now let’s move on to something more interesting. We’ll use the Yandex Translate API to perform a request to translate some text to a different language.

      To use the API, first you need to sign up. After you sign up, go to the Translate API and create an API key. Once you have the API key, add it to your file as a constant. Here’s the link where you can do all those things: https://tech.yandex.com/translate/

      script.py

      API_KEY = 'your yandex api key'
      

      The reason why we need an API key is so Yandex can authenticate us every time we want to use their API. The API key is a lightweight form of authentication, because it’s added on to the end of the request URL when being sent.

      To know which URL we need to send to use the API, we can look at the documentation for Yandex.

      If we look there, we’ll see all the information needed to use their Translate API to translate text.

      Request syntax for using the API

      When we see a URL with ampersands (&), question marks (?), and equals signs (=), you can be sure that the URL is for GET requests. Those symbols specify the parameters that go along with the URL.

      Normally things in square brackets ([]) will be optional. In this case, format, options, and callback are optional, while the key, text, and lang are required for the request.

      So let’s add some code to send to that URL. You can replace the first request we created with this:

      script.py

      url = 'https://translate.yandex.net/api/v1.5/tr.json/translate'
      res = requests.get(url)
      

      There are two ways we can add the parameters. We can either append it to the end of the URL directly, or we can have Requests do it for us. To do the latter, we can create a dictionary for our parameters. The three items we need are the key, the text, and the language. Let’s create the dictionary using the API key, 'Hello' for the text, and 'en-es' as the lang, which means we want to translate from English to Spanish.

      If you need to know any other language codes, you can look here. You are looking for the 639-1 column.

      We create a params dictionary by using the dict() function and passing in the keys and values we want in our dictionary.

      script.py

      params = dict(key=API_KEY, text='Hello', lang='en-es')
      

      Now we take the parameters dictionary and pass it to the .get() function.

      script.py

      res = requests.get(url, params=params)
      

      When we pass the parameters this way, Requests will go ahead and add the parameters to the URL for us.

      Now let’s add a print statement for the response text and view what gets returned in the response.

      script.py

      print(res.text)
      

      output dictionary with the values inputted

      We see three things. We see the status code, which is exactly the same status code of the response itself, we see the language that we specified, and we see the translated text inside of the list. So you should see 'Hola' for the translated text.

      Try again with en-fr as the language code, and you should see 'Bonjour' in the response now.

      script.py

      params = dict(key=API_KEY, text='Hello', lang='en-fr')
      

      French translated text

      Let’s take a look at the headers for this particular response.

      script.py

      print(res.headers)
      

      Headers printed in output

      Obviously the headers should be different because we’re communicating with a different server, but in this case the content type is application/json instead of text/html. What this means that the data can be interpreted as JSON.

      When application/json is the content type of the response, we are able to have Requests convert the response to a dictionary and list so we can access the data easier.

      To have the data parsed as JSON, we use the .json() method on the response object.

      If you print it, you’ll see that the data looks the same, but the format is slightly different.

      script.py

      json = res.json()
      print(json)
      


      The reason why it’s different is because it’s no longer plain text that you get from res.text. This time it’s a printed version of a dictionary.

      Let’s say we want to access the text. Since this is now a dictionary, we can use the text key.

      script.py

      print(json['text'])
      


      And now we only see the data for that one key. In this case we are looking at a list of one item, so if we wanted to get that text in the list directly, we can access it by the index.

      script.py

      print(json['text'][0])
      

      "Bonjour" without the square brackets

      And now the only thing we see is the translated word.

      So of course if we change things in our parameters, we’ll get different results. Let’s change the text to be translated from Hello to Goodbye, change the target language back to Spanish, and send the request again.

      script.py

      params = dict(key=API_KEY, text='Goodbye', lang='en-es')
      

      "Adios" printed to output
      Try translating longer text in different languages and see what responses the API gives you.

      Translate API Error Cases

      Finally, we’ll take a look at an error case. Everything doesn’t always work, so we need to know when that happens.

      Try changing your API key by removing one character. When you do this your API key will no longer be valid. Then try sending a request.

      If you take a look at the status code, this is what you get:

      script.py

      print(res.status_code)
      

      403 error
      So when you are using the API, you’ll want to check if things are successful or not so you can handle the error cases according to the needs of your app.

      Conclusion

      Here’s what we learned:

      • How HTTP requests work
      • The various status codes possible in a response
      • How to send requests and receive responses using the Python Requests library
      • How to use a language translation API to translate text
      • How to convert application/JSON content responses to dictionaries

      If you want to do more, check out this list to see different APIs that are available, and try to use them with Python Requests.



      Source link

      How To Create an OAuth App with the Linode Python API Library


      Updated by Linode

      Contributed by

      Linode

      Linode supports the OAuth 2 authorization protocol. OAuth 2 allows a user to safely grant a third-party app permission to act on their behalf. This means that a user could authorize an app to access data and / or make changes to their Linode account and services that are exposed by the Linode APIv4. For example, an app could create or destroy Linodes, manage a NodeBalancer, or alter a domain.

      This guide will show you how to create a simple OAuth application using Flask and the Linode Python API library. This app allows a user to log in with their Linode account and create a Linode with a StackScript. The complete code for this example is available in the Linode APIv4 Python library example repository.

      Before You Begin

      1. Normally, in order to create an OAuth app with Linode your server must have HTTPS enabled. The only exceptions to this rule are localhost addresses, which can use HTTP. As this guide is just a primer and is not intended to supply production ready code, we will be working with a local workstation, using localhost. If you choose to create an app for production, you will need to generate SSL certificates for HTTPS access.

      2. Ensure that Python 3 is installed on your workstation.

      Obtaining a Client ID and a Client Secret

      In order for Linode to verify the identity of your app, called a client, you will need to generate a set of credentials, specifically a client ID and a client secret.

      1. Log in to the Linode Cloud Manager and navigate to your Account Profile.

        OAuth Account Profile

      2. From there, click on the My Apps tab and select Create My App. You will be prompted to supply a label for your app and a callback URL. We will discuss the role of the callback URL in depth later in this guide. For now you can supply the following URL:

        http://localhost:5000/auth_callback
        

        Leave Public unchecked and click Submit.

        OAuth Account Profile

      3. A window will appear with your client secret. Copy this down somewhere secure, as once you exit this window you will not be able to retrieve the client secret again, and will be forced to generate a new one.

        OAuth Account Profile

      4. Once you exit the client secret window your app will appear as part of a list of apps. Note your client ID, as this is the last piece of information you will need to verify your app’s identity.

        OAuth Account Profile

      In summary, you should have these three bits of information, with values similar to the ones provided here:

      OAuth 2 Authentication Exchange

      The OAuth 2 workflow is a series of exchanges between your third-party app and Linode. Below is an explanation of these exchanges.

      1. The end user visits your client application’s website and attempts to login.
      2. Your client application redirects the end user to the authentication server (https://login.linode.com) with your client application’s client ID and requested OAuth scopes, which appear in the URL of the login page.
      3. The end user inputs their username and password to the authorization server and authorizes the login.
      4. The authorization server redirects the end user back to your client application with a temporary authorization code (sometimes called an exchange code) in the URL.
      5. The client application issues a POST request to the authentication server containing the authorization code and the client application’s client secret.
      6. The authentication server responds to the client application with a newly issued OAuth access token.

      In the following sections you will write the code to perform each one of these steps, using the Linode Python API library.

      Setup Your Development Environment

      1. Create a project folder and move into that folder.

        mkdir ~/linode-oauth-project && cd ~/linode-oauth-project
        
      2. For this project, you will need to use pip to download and install the required Python libraries. Install pip if you do not already have it:

        apt install python-pip
        
      3. Install the required Python libraries:

        pip install flask flask-session linode_api4
        

      Configure Your App

      In a text editor, create a file named config.py. Add the following variables and values, being sure to change the values to your own.

      The StackScript used in this example is for demo purposes. To explore other available StackScripts, visit the Linode StackScript Library. Note that the stackscript_id does not have quotation marks around it. The secret key is used for serializing session data, and should be a value only you know.

      config.py
      1
      2
      3
      4
      5
      
      client_id = 'ce571a8cdad1ba4a0a7d'
      client_secret = 'fab8e2222e83b9b2f50a76012122ec20a5acb005ed088f3fccda2c9c2c4e1cbd'
      stackscript_id = 320826
      application_name = 'my-application-name'
      secret_key = 'my-secret-key'

      Author an OAuth2 App

      In this section, you will write the code for the app.

      Include Imports

      Ensure you are in the linode-oauth-project directory and create and open a file called app.py in the text editor of your choice. Include the following libraries:

      app.py
      1
      2
      3
      4
      5
      6
      
      import re
      from flask import Flask, redirect, request, render_template, session, send_from_directory
      from flask_session import Session
      from linode_api4 import (LinodeClient, LinodeLoginClient, StackScript, Image, Region, Type, OAuthScopes)
      
      import config

      Set Up Flask and Session Key

      Copy in the following code to set up Flask and the session secret key:

      app.py
      1
      2
      3
      4
      
      ...
      
      app=Flask(__name__)
      app.config['SECRET_KEY'] = config.secret_key

      Create a Function to Return the Linode Login Client

      In app.py add the following function to return the LinodeLoginClient class. The LinodeLoginClient class is the library’s OAuth interface. Note that we are passing the client_id and client_secret parameters from our config.py file to the class:

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      
      ...
      
      def get_login_client():
          return LinodeLoginClient(config.client_id, config.client_secret)

      Create an Index Route

      In Flask you can create HTTP endpoints with routes. The index route, defined in the code below at the document root /, will be the route the user will see when they navigate to http://localhost:5000/. This route will be responsible for displaying the available Linode plan types, the available regions, and the StackScript-compatible images that a user will choose from when creating their new Linode.

      To query a list of available plan types and regions you can use the LinodeClient class, which is an interface for Linode’s APIv4. Viewing the Linode plan types and regions does not require any sort of authorization, so you can provide a dummy value of no-token to instantiate the class:

      ~/linode-oauth-project/app.py
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      
      ...
      
      @app.route('/')
      def index():
          client = LinodeClient('no-token')
          types = client.linode.types(Type.label.contains("Linode"))
          regions = client.regions()
          stackscript = StackScript(client, config.stackscript_id)
          return render_template('configure.html',
              types=types,
              regions=regions,
              application_name=config.application_name,
              stackscript=stackscript
          )

      It is important to note that the two API queries in the above code are slightly different from one another. The client.regions method is a top-level method, just as it appears in the Linode API. The client.linode.types method, on the other hand, is part of the Linode group, which is a collection of methods that deal with Linodes. Again, this is because Linode endpoints are grouped that way in the API. Some methods in the Linode Python library are top level, such as domain_create, while others, like networking.ip_assign, are part of a group. For more information on the top-level methods and groupings, consult the library documentation.

      In addition to querying the API, the above route also renders the configure.html template by passing it the types, regions, application name, and StackScript object. The StackScript object contains a list of StackScript compatible images. We will cover templating in a later section.

      Create a Login Route

      Next, create a login route in app.py. This route will perform two functions. First, it will serialize the user’s plan type, region, and image selections into the session.

      Second, this route will redirect the user to Linode’s login page where they will be prompted to authorize your client app and the scopes you have requested for it. Scopes are sets of permissions that define the access level of your client app. For instance, to create a Linode, your end user must authorize the OAuthScopes.Linodes.create scope.

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      ...
      
      @app.route('/', methods=["POST"])
      def start_auth():
          login_client = get_login_client()
          session['dc'] = request.form['region']
          session['distro'] = request.form['distribution']
          session['type'] = request.form['type']
          return redirect(login_client.generate_login_url(scopes=OAuthScopes.Linodes.create))

      When the user returns to your app from the Linode login page, they will be directed to the callback URL.

      Note

      Below is a list of available scopes:

      • OAuthScopes.Linodes
      • OAuthScopes.Domains
      • OAuthScopes.StackScripts
      • OAuthScopes.Users
      • OAuthScopes.NodeBalancers
      • OAuthScopes.Tokens
      • OAuthScopes.IPs
      • OAuthScopes.Tickets
      • OAuthScopes.Clients
      • OAuthScopes.Account
      • OAuthScopes.Events
      • OAuthScopes.Volumes

      Each scope is broken into five permissions: view, create, modify, delete, and all. The all permission encompasses the other four permissions.

      Manage the OAuth 2 Callback URL

      The OAuth 2 callback URL has two main responsibilities. Its first responsibility is to help prove the identity of the client application. When a user attempts to log in to Linode through OAuth, instead of redirecting the user back to the page they came from, Linode’s OAuth implementation matches the client ID to the callback URL you have registered with your app on Linode’s system. This ensures that a nefarious third party can’t just steal the client ID, which is public, and attempt to authorize their own app with it.

      The callback URL’s second responsibility is to kick off the process of exchanging an authorization code for an access token. This second process is done over POST, and so it doesn’t require the user to physically leave the page they are returned to after they log in to Linode. Now you will write the code that satisfies this second responsibility.

      In app.py, add the following lines:

      ~/linode-oauth-project/app.py
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
      ...
      
      @app.route('/auth_callback')
      def auth_callback():
          code = request.args.get('code')
          login_client = get_login_client()
          token, scopes, _, _ = login_client.finish_oauth(code)
      
          # ensure we have sufficient scopes
          if not OAuthScopes.Linodes.create in scopes:
              return render_template('error.html', error='Insufficient scopes granted to deploy {}'
                      .format(config.application_name))
      
          (linode, password) = make_instance(token, session['type'], session['dc'], session['distro'])
      
          get_login_client().expire_token(token)
          return render_template('success.html',
              password=password,
              linode=linode,
              application_name=config.application_name
          )

      Let’s take a look at what each of the parts of this section does.

      First, a route is defined for the callback with @app.route(), then a function called auth_callback is defined that will run whenever this route is accessed:

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      
      ...
      @app.route('/auth_callback')
      def auth_callback():
      ...

      When the user is returned to the callback URL, an authorization code is appended to the URL. The variable code is set to retrieve this value from the URL’s request arguments:

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      ...
          code = request.args.get('code')
      ...

      Then you retrieve an instance of the LinodeLoginClient class:

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      ...
          login_client = get_login_client()
      ...

      Once you have the LinodeLoginClient class, you can pass the authorization code to the finish_oauth method, which is a helper method that will manage the authorization code to OAuth token exchange. This method returns an OAuth token, and the scopes the user has agreed upon.

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      ...
          token, scopes, _, _ = login_client.finish_oauth(code)
      ...

      The next section compares the scopes your app requested from the user to the scopes returned by Linode’s OAuth login page. If the returned scopes do not include the correct scopes, in this case the OAuthScopes.Linode.create scope, then an error template is rendered and an error message is displayed:

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      5
      6
      
      ...
          # ensure we have sufficient scopes
          if not OAuthScopes.Linodes.create in scopes:
              return render_template('error.html', error='Insufficient scopes granted to deploy {}'
                      .format(config.application_name))
      ...

      Once your app has determined that it has the correct permissions, it creates the Linode using the Linode plan type, the region, and the image that the app serialized into session storage. You will create the make_instance function in the next step. The make_instance function returns the linode object, which contains the Linode’s label, group, and IP address, and the function also returns a randomly generated password:

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      ...
          (linode, password) = make_instance(token, session['type'], session['dc'], session['distro'])
      ...

      Once the Linode has been created, the app expires the OAuth access token. Expiring tokens after use is a strong security measure but if your app is performing many actions on behalf of the user, you might find that time-based expiration scheme is more suitable to your needs. The app then renders the success template by passing it the linode object, the password, and application name:

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      5
      6
      7
      
      ...
          get_login_client().expire_token(token)
          return render_template('success.html',
              password=password,
              linode=linode,
              application_name=config.application_name
          )

      Create a Function to Deploy a Linode

      Now, create the make_instance function that you referenced above:

      ~/linode-oauth-project/app.py
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      
      ...
      
      def make_instance(token, type_id, region_id, distribution_id):
          client = LinodeClient('{}'.format(token))
          stackscript = StackScript(client, config.stackscript_id)
          (linode, password) = client.linode.instance_create(type_id, region_id,
                  group=config.application_name,
                  image=distribution_id, stackscript=stackscript.id)
      
          if not linode:
              raise RuntimeError("it didn't work")
          return linode, password

      The make_instance function takes an OAuth access token, the type ID, the region ID, and the image (Linux distribution) ID as parameters. It creates an instance of the LinodeClient class, and unlike the instance of LinodeClient used earlier in the guide, this one requires an OAuth token because you will be using it to create a Linode. The function then creates a Linode using the linode.instance_create method, returning the linode object and the password.

      Finally, if there was an error with the creation of the Linode, the if not linode statement will raise a runtime error.

      Set the name Variable

      At the end of your app.py, paste in the following code to make sure you can run your app:

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      if __name__ == '__main__':
          app.debug=True
          app.run()

      Create App Templates

      Now that you have written the backend code for your app, you’ll need to create a frontend user interface. Begin by creating a templates directory in your project directory and moving into it:

      mkdir ~/linode-oauth-project/templates && cd ~/linode-oauth-project/templates
      

      Using your preferred text editor, create and open base.html. This will be the base template from which your other templates will inherit their stylesheets and JavaScript files:

      ~/linode-oauth-project/templates/base.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      
      <html>
      <head>
          <title>Install On Linode</title>
          <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
              integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
          <style>
              body{
                  text-align: center;
                  background-color: #333333;
              }
              .form-group{
                  display: inline-block;
                  text-align: left;
                  width: 250px;
                  border: 1px solid #cccccc;
                  margin: 5px;
                  padding: 5px;
              }
              .form-group label{
                  color: #337ab7;
              }
              .form-group select{
                  font-size: 16px;
                  outline: none;
                  border: 0px solid #000000;
                  box-shadow: inset 0 1px 1px rgba(0,0,0,0);
                  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0);
              }
              .form-group select:focus{
                  box-shadow: inset 0 1px 1px rgba(0,0,0,0);
                  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0);
              }
              .btn-lg{
                  border-radius: 0px;
                  margin-top: 20px;
              }
              .row{
                  margin-bottom: 20px
              }
              .pop{
                  color: #337ab7;
                  font-weight: bold
              }
              code{
                  color: #337ab7;
                  background-color: #eeeeee
              }
              .boxy{
                  border: 1px solid #cccccc;
                  width: 400px;
                  background-color: #f9f9f9;
                  margin: auto;
                  padding: 10px;
              }
          </style>
      </head>
      <body>
          <div class='container' style='background-color: white; border-left: grey; border-right: grey; height: 100%; padding: 20px;'>
              {% block content %}
              {% endblock %}
          </div>
      
          <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
          <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
              integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
              crossorigin="anonymous"></script>
      </body>

      The important thing to note in the above template is the Jinja2 templating tags. They are:

      {% block content %}
      {% endblock %}
      

      As you will see, any template that extends the base.html template and includes code between the opening and closing content block, will render the code laid out by base.html.

      Create a file called configure.html, which will be the UI a user will see when they reach the document root endpoint (/). Copy in the following code:

      templates/configure.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      
      {% extends 'base.html' %}
      {% block content %}
          <form method="POST">
              <div class='row'>
                  <h1>Deploy <span style="color: #337ab7;">{{application_name}}</span> to a Linode</h1>
                  <p>
                      This will create a brand new Linode running {{application_name}} on your
                      account and give you the credentials.
                  </p>
              </div>
              <div class='row'>
                  <div class='form-group'>
                      <label for='type'>Type</label>
                      <select name='type'i id='type' class='form-control'
                          onblur="blurring(this)" onfocus="focusing(this)">
                          {% for s in types %}
                              <option value="{{s.id}}">{{s.label}}</option>
                          {% endfor %}
                      </select>
                  </div>
                  <div class='form-group'>
                      <label for='region'>Region</label>
                      <select name='region' id='region' class='form-control'
                          onblur="blurring(this)" onfocus="focusing(this)">
                          {% for o in regions %}
                              <option value="{{o.id}}">{{o.id}}</option>
                          {% endfor %}
                      </select>
                  </div>
                  <div class='form-group'>
                      <label for='distribution'>Images</label>
                      <select name='distribution' id='distribution' class='form-control'
                          onblur="blurring(this)" onfocus="focusing(this)">
                          {% for d in stackscript.images %}
                              <option value="{{d.id.id}}">{{d.id.id}}</option>
                          {% endfor %}
                      </select>
                  </div>
              </div>
              <div class='row'>
                  <input type="submit" value="Deploy Linode" class='btn btn-primary btn-lg'/>
              </div>
          </form>
          <script>
              function focusing(ele){
                  ele.parentElement.style.borderColor = "#337ab7";
              }
              function blurring(ele){
                  ele.parentElement.style.borderColor = "#cccccc";
              }
          </script>
      {% endblock %}

      Here the template begins with two statements: {% extends 'base.html' %} and a {% block content %} statement. These two tags tell Jinja2 to extend the code within base.html, and to place everything within {% block content %} ... {% endblock %} in configure.html between the corresponding {% block content %} ... {% endblock %} tags in base.html.

      configure.html includes Jinja2 logic, with the inclusion of for statements like {% for o in regions %}. These statements are like for statements in other languages, and are used to iterate over an array or list. In this example, it is iterating over the regions that we passed to the template from the index route. configure.html also contains variables, which are denoted by double curly brackets: {{ s.id }}.

      Create another file called error.html. This will be the template that appears whenever there is an error in the Linode deployment. Copy in the following code:

      templates/error.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      {% extends 'base.html' %}
      {% block content %}
          <div class='row'>
              <h1 class="pop">Error</h1>
              <p>{{error}}</p>
          </div>
          <div class='row' style='margin-top: 20px'>
              <a href='/' class='btn btn-lg btn-default'>Try Again</a>
          </div>
      {% endblock %}

      This template works the same way that configure.html does, by extending base.html and providing its own content block.

      Lastly, create another file called success.html. This file follows the pattern set by configure.html and error.html, and will present the user with a confirmation message whenever a Linode is successfully created. This message includes the Linode’s label, group, IP address, and password:

      templates/success.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      
      {% extends 'base.html' %}
      {% block content %}
          <div class='row'>
              <h1>Success!</h1>
              <p>{{application_name}} has been deployed to <span class="pop">{{linode.label}}</span> in the {{linode.group}} group.</p>
          </div>
          <div class='row'>
              <div class='boxy'>
                  <p>You can access your Linode with the following command:</p>
                  <code>ssh root@{{linode.ipv4[0]}}</code>
                  <br />
                  <br />
                  <p>Your root password is:</p>
                  <code>{{password}}</code>
              </div>
          </div>
      {% endblock %}

      Run Your App

      You are now ready to run your app. Change back to your project’s main directory:

      cd ~/linode-oauth-project
      

      Run the app.py script:

      python3 app.py
      

      Open your browser to the following URL:

      http://localhost:5000/
      

      You should be greeted with your new app. Select a plan, a region, and an image to deploy a Linode using the Linode API Python library.

      Next Steps

      The app you’ve created shows off some of the aspects of the Linode API Python library. You can use LinodeLoginClient to authorize your OAuth app with the appropriate scopes, and can create Linodes through the use of LinodeClient.

      In extending this app, you might want to add multiple functionalities, like creating NodeBalancers from a list of available Linodes, or managing domains. To achieve this goal you’ll probably want to separate the login logic from the Linode creation logic. One way to do this would be store the OAuth token in the session, implementing a time-based expiration mechanism to expire your tokens instead.

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      Find answers, ask questions, and help others.

      This guide is published under a CC BY-ND 4.0 license.



      Source link