Tag: APIs

  • Python POST requests three ways with Oracle REST Data Services (ORDS)

    Python POST requests three ways with Oracle REST Data Services (ORDS)

    The simple POST request

    It was bugging me that I couldn’t perform a simple Python POST request to an ORDS REST-enabled table.

    I wanted to send HTTPS POST requests to this table in my Oracle Autonomous Database, ORDS, Oracle REST APIs, Chris Hoina, Product Manager, Database Actions, Database Tools
    This one actually…

    I don’t mean to convey that this isn’t possible. Up until very recently, I wasn’t able to do this. Luckily I had a few hours free, so I took to the docs to do some reading. And wouldn’t you know it, like most things in tech, reading the documentation was a practical and valuable use of my time.

    Side note

    Let me pause here for a minute or two. I should add this disclaimer that none of what I share here uses OAuth 2.0 authentication. Of course, I wish it did, but I’m just not there yet (technical proficiency). In the future, I’d like to update this with security in mind1:

    1We have an API security section in our ORDS Best Practices guide.

    The other thing I’ll mention is that I’m going to include the SQL for creating this table along with all the Python code in my GitHub repo (I’ll also add any code updates to my repo! will also add any updates I make to this code to the repo!).

    Also, did you know that saving DDL with Database Actions is just a mouse click away?

    New User speed run

    This section is a bit of an aside, but I also created a new Python Developer user in one of my Autonomous Databases. It’s straightforward to do as the admin. Here is a “speed run”:

    POST haste

    After creating my new user, I created a “Python_Post” table. Super imaginative, right? And I kept things simple, naming the four columns (wait for it): "col1", "col2", "col3", and "col4".

    Columns in POST table in Oracle Autonomous Database to test requests in Python, ORDS, Oracle REST APIs, Chris Hoina, Product Manager, Database Actions, Database Tools
    Damn…he actually did that.

    Cheat codes

    I auto-REST enabled my table and reviewed a Bash cURL command so I could remind myself of the expected data (aka payload).

    Reviewing a Bash POST cURL command for reference in my Oracle Autonomous Database, ORDS, Oracle REST APIs, Chris Hoina, Product Manager, Database Actions, Database Tools
    A Bash cURL command for POST requests; can be used as later reference in your editor.

    I’ve noticed that if I don’t specify a ‘rowid’ the Autonomous Database automatically does this. SQL-newbies (like me) might appreciate this since we still don’t know all the best practices for database design (or DDL, for that matter)!

    My process might differ from yours, but I’ve used the cURL commands in Database Actions as a cheat. I’ve been copying/pasting this cURL command into my working Python file, so I don’t have to switch screens. Additionally, it helps to remind me what the {‘key’:‘value’} pairs are (even though I just created all this, I STILL can’t remember what I did).

    In this case, I’m referencing a POST request, but you could do this for the other HTTPS methods too:

    • GET ALL
    • GET
    • POST
    • BATCH LOAD
    • PUT
    • DELETE

    Moving on…

    I could omit the ‘rowid’ when making these POST requests. I don’t know if this is typical when working with databases, but this seems to work consistently (at least with the testing I did) with Python and the Requests library.

    If you weren’t taken aback by my imaginative table name and column names, then get ready because I’m about to blow your mind with this next bit. I created a payload for each of these POST request variations:

    Oracle Autonomous Database, ORDS, Oracle REST APIs, Chris Hoina, Product Manager, Database Actions, Database Tools
    And I’m just going to be as literal as possible to avoid confusion…is it lame? Yes. Does it work? Undecided, you tell me.

    In case you didn’t catch it, they were: payload1, payload2, and payload3.

    On feedback…

    Lack of communication is disturbing, Oracle Autonomous Database, ORDS, Oracle REST APIs, Chris Hoina, Product Manager, Database Actions, Database Tools

    I also included some feedback for myself. And I feel like this is a good practice because if you successfully POST something, how would you know? Conversely, the same would be true if you unsuccessfully POSTed something. If I were smart, I’d design a REST API in Database Actions that automatically includes some feedback. But I am not.

    If you want to read about this Implicit Parameter and others, click here.

    Luckily, the Python Requests library includes various feedback methods. I’ve included the following in my Python file:

    • status_code – returns the response status code
    • raise_for_status() – this will display the error message (if applicable); displays "None" if the request was successful
    • headers – returns the server’s response headers as a Python dictionary

    Different payloads

    My Python code is simple enough. It looks more than it is because I include three POST request variations. I’ve also tested payloads, and it seems like we can send a "payload" as:

    • params – query parameters (e.g. https://httpbin.org/get?key2=value2&key1=value1)
    • json – JSON data
    • data – a Python dictionary (in this use case)

    Oracle REST Data Services has a ton of documentation on filtering with query parameters… I’m still learning about it, but they are powerful. Check them out here.

    You can even send payloads consisting of nested items/objects, too (e.g., an array or list as one of your values). I’m sure this violates normalization rules, but it’s possible. In my case, I didn’t do this; I just stuck to my four columns.

    Executing the code

    After I executed the code, I received the following feedback for these POST requests:

    Feedback from post to my Oracle Autonomous Database, ORDS, Oracle REST APIs, Chris Hoina, Product Manager, Database Actions, Database Tools
    If you squint, you can see three separate responses. I’ve only boxed one, but you should be able to point out the other two.

    You should see:

    • a 201 status code, which indicates “the request has been fulfilled and has resulted in one or more new resources being created2.
    • None – which if there were a 400 error, that would show me the error message
    • {key: value} pairs displaying all header details

    2About the 201 Status Code in Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content

    Double checking

    While in VS Code, the POST requests appeared successful. But since I had access, I logged into Database Actions and manually inspected my table. Success!

    Double checking the post requests in database actions, Oracle Autonomous Database, ORDS, Oracle REST APIs, Chris Hoina, Product Manager, Database Actions, Database Tools
    Reviewing the new updates in Database Actions.

    And that’s it…zero to POST request in my Oracle Autonomous Database + ORDS in no time!

    Was this helpful?

    I have this code in my GitHub repository, so feel free to fork, download, or add comments. But I’ll include here too:

    import requests 
    
    url = "[Your ORDS URL goes here]"
    
    payload1 = {'col1': 'test1', 'col2':'test1', 'col3':'test1', 'col4': 'test1'}
    
    r1 = requests.post(url, json=payload1)
    
    print(r1.status_code)
    print(r1.raise_for_status())
    print(r1.headers) 
    
    payload2 = {'col1': 'test2', 'col2':'test2', 'col3':'test2', 'col4': 'test2'}
    
    r2 = requests.post(url, data=payload2)
    
    print(r2.status_code)
    print(r2.raise_for_status())
    print(r2.headers) 
    
    payload3 = {'col1': 'test3', 'col2':'test3', 'col3':'test3', 'col4': 'test3'}
    
    r3 = requests.post(url, params=payload3)
    
    print(r3.status_code)
    print(r3.raise_for_status())
    print(r3.headers) 

    And if you are still here — was this helpful? Want to see more or something else that is Python + ORDS-related? Let me know here in the comments, on Twitter, or by email!

  • Flask, Python, ORDS, and Oracle CloudWorld 2022

    Flask, Python, ORDS, and Oracle CloudWorld 2022

    Python, Flask, and ORDS

    Restaurants Web Application update

    If you’ve been following along, then you are probably aware of the python/flask/ORDS journey that I’ve embarked on. If not, you can read up on the overview here. The idea was to take local restaurant health inspection scores (aka Sanitary grades) and present the data to a user in a visualization. The web framework I’m using is based on Flask, a popular Python web application framework. And we’ll interact with our Oracle Autonomous Database using our Oracle REST Data Services APIs (for both GET and POST requests).

    That progress is moving along, and I’ve been toying with the Folium library for map visualizations.

    If you are familiar with Leaflet.js then you basically know Folium – a python wrapper used for displaying map renderings from Leaflet. You can get a taste of what I’m working towards in this latest post.

    However, this project is on hold (very, very) temporarily while I become familiar with another Python / Flask / ORDS web application.

    eCommerce/Retail Web Application [NEW]

    Currently titled “Frank’s Franks”, this application is what I’m referring to as an eCommerce type application. You are presented with product information, you then make a product selection, and create an order. There is no credit card gateway, but the application does actually POST order information to a live Autonomous Database.

    This application is the brainchild of our very own Brian Spendolini. But I’m taking a crack at making some additions to it with what I’ve learned from Folium and Bootstrap (HTML and CSS). I’m not sure of the exact final state, but you can follow along as we continue iterating on it (more on this in the GitHub section).

    And here is a very crude example of the index (aka “home”) page:

    And here is a nice screenshot of the python code along with some in-progress HTML:

    Our Flask application is on the left, with the two additional right panes showing the base.html and index.html pages.

    I’m showing this because this will be one of the practical labs that our team will be facilitating in October. Why October you ask? Great question…

    Oracle CloudWorld

    Because that is when Oracle CloudWorld is happening. Queue gratituous copy/paste in 3…2…1…

    Join us at CloudWorld, the new global conference where our customers and partners can share ideas, develop in-demand skills, and learn about cloud infrastructure and applications solutions that serve their unique roles and business needs. At CloudWorld, you’ll experience first-look product demos by Oracle experts, access hands-on training and certification opportunities, and shape the cloud innovation conversation alongside your peers and industry leaders.

    Now I’m not sure who on the team will be attending, but rest assured, one of us will be there.

    GitHub

    Finally, GitHub. Why am I including an ominous “GitHub” section? For two reasons. First, if you want to follow along with the progress of this CloudWorld lab, I encourage you to peruse my repo, which can be found here.

    Second, I’ve started to build out a python-themed library that will document code snippets and frequent code patterns when working with popular libraries + ORDS. The obvious ones are things like “requests” and “pandas”. And a lot of it is things that I have encountered personally. But I’m discovering there is so much more out there for python folks, so hopefully, this will continue to expand.

    If you do visit the “library” you’ll see that I have created some ORDS endpoints based on historical U.S. home sale data. You can check it out here.

    Current state:

    GitHub repo of python ORDS library code patterns, code snippets, Chris Hoina, Database Tools, Oracle Autonomous Database
    Those endpoints are actually enabled, so you can work with them without having to authenticate. Now of course you could totally mess things up and POST or delete whatever...but lets try to avoid that. 

    But wait, there’s more

    You have both an invitation and a request.

    An invitation to follow along with both the lab progress and the python library project.

    And a request to

    • submit ideas and possible areas of improvement (or cool code patterns) for this CloudWorld application,
    • and submit any requests you might have for the python library that I’ve started to build-out

    This is all voluntary of course, but if you think any of this might benefit please do engage and share!

    Finally, I’ll be back to regularly-scheduled technical posts soon. In the meantime…

    Keep following

  • Python + Folium + Oracle REST APIs (aka ORDS)

    Python + Folium + Oracle REST APIs (aka ORDS)

    Willkommen

    I stumbled upon a new [to me] python library called Folium. It’s a mapping tool that enables python developers (or is it programmers, which is less offensive?) to visualize data on a Leaflet map.

    About folium

    Folium makes it easy to visualize data that’s been manipulated in Python on an interactive leaflet map. It enables both the binding of data to a map for choropleth visualizations as well as passing rich vector/raster/HTML visualizations as markers on the map.

    The library has a number of built-in tilesets from OpenStreetMap, Mapbox, and Stamen, and supports custom tilesets with Mapbox or Cloudmade API keys. Folium supports Image/Video, GeoJSON and TopoJSON overlays.

    Folium docs

    …but what about leaflet.js?

    Leaflet.js is the leading open-source JavaScript library for mobile-friendly interactive maps. Out-of-the-box features include: tile layers/WMS, markers/popups, vector layers (polylines, polygons, circles, rectangles), image overlays, and GeoJSON

    Leaflet.js “Features” page

    Tilesets

    I highlighted the tilesets above, and I’m not sure if this is a GIS or a leaflet term, but it seems that tilesets refer to different map renderings. Folium includes the following:

    • OpenStreetMap
    • Stamen Terrain
    • Stamen Toner
    • Stamen Watercolor

    At the time of writing, I didn’t have API credentials for Mapbox Bright or Mapbox Control Room. But I’m assuming they still work if you have the appropriate API credentials.

    Excluding the Mapbox tiles, those packaged by default are more than sufficient:

    Tip: Read more on Stamen here, s'il vous plaît. They seem to be the original creators of these Stamen tiles. 

    Python + Folium + ORDS = Happiness

    Folium installation and quick start pages are straightforward – requiring minimal steps and effort. The same goes for installing the Live Server extension too. Once installed, you can right-click on a .html file to load it onto a local development server (so it displays in a new browser window).

    Let me jump right into how I set up Folium to work with ORDS. If you recall, most of the tables I have in my Autonomous Databases are REST-enabled (previous post working with ORDS).

    An ORDS Review

    Step 1 REST Enabling your table with ORDS in Database Actions Oracle Autonomous Database Chris Hoina Senior Product Manager Database Tools
    1. Once in Database Actions, right-click on an object and select REST > Enable.
    Step 2 REST Enabling your table with ORDS in Database Actions Oracle Autonomous Database Chris Hoina Senior Product Manager Database Tools
    2. Choose an alias (good security measure) along with Authentication (also good practice).
    Step 3 REST Enabling your table with ORDS in Database Actions Oracle Autonomous Database Chris Hoina Senior Product Manager Database Tools
    3. If you forget the URI, you can review the cURL command.
    Step 4 REST Enabling your table with ORDS in Database Actions Oracle Autonomous Database Chris Hoina Senior Product Manager Database Tools
    4. There you can copy/paste directly into a Command-Line or copy the URI portion and place into your browser’s address bar.

    …and now back to your regularly scheduled blog post

    Since I needed latitude and longitude coordinates for Folium, I decided to work with my Restaurant table. Put the REST endpoint (URI) directly into the browser; and you’ll see this:

    ORDS REST URI Endpoint from Oracle Autonomous Database, Chris Hoina Senior Product Manager Database Tools

    Afterward, I looked at the JSON object to see how it was structured (there are too many lists, dictionaries, arrays, etc. to remember amirite?!) because I’d need to iterate through all the items in the GET Response – to tease out what Folium needs.

    The play-by-play

    Printing the response (to see what it looks like)

    Walkthrough 1 Folium Python VS Code ORDS API GET Requests Oracle Autonomous Database Chris Hoina Senior Product Manager Database Tools
    • I’m covering all my bases here; reviewing the ORDS-provided request/object from a table in my Autonomous Database
    • Using json.() to “decode” it into a python dictionary
    • Please pay attention to line 10, where I actually create the base map

    Review of what Folium needs

    Walkthrough 2 Folium Python VS Code ORDS API GET Requests Oracle Autonomous Database Chris Hoina Senior Product Manager Database Tools
    • Looping through the python dictionary, pulling out what I need for the next step
    • Including lines 25-29 to create the pop-up markers.
    • The ('<i>{}</i>'.format(Resty)) allows me to pass the names to the string (stuff in quotes, plus the {}). The HTML for italics is optional.
    • In this case, ‘x’ and ‘y’ are the coordinates
    • Line 31 saves a .html file (which uses Bootstrap!)

    Newly rendered .HTML

    Walkthrough 3 Folium Python VS Code ORDS API GET Requests Oracle Autonomous Database Chris Hoina Senior Product Manager Database Tools
    • Using the Live Server extension, I can review the Folium map plus the pop-up markers!
    NOTE: This isn't the focus, but spoiler alert, I'll be putting this into a Flask application. I'll also build this out, so the individual markers/popups have charts/graphs for each restaurant. Again, all of which will be provided by ORDS-enabled tables (not so shameless plug).

    Summary

    After it is all said and done, it took me less than an afternoon to figure out how to make this work. So if you have a table with latitude and longitude, what are you waiting for?! REST-enable it with ORDS, and you can come up with a very quick demo on your own!

    Also, I didn’t have to create any database models, the dependencies are minimal (a few python libraries), and an Always Free OCI account is really all you need to get started.

    Code

    import json
    import requests
    import folium
    
    m = folium.Map(location=[35.78742648626059, -78.78122033558192], zoom_start=12, tiles="Stamen Watercolor")
    
    response = requests.get('your ORDS enpoint/URI').json()
    
    # print(type(response))
    # print(response)
    
    for entry in response['items']:
        Resty = entry['name']
        Lat = entry['y']
        Long = entry['x']
        
        folium.Marker(
    
            location=[Lat, Long], 
            popup = folium.Popup('<i>{}</i>'.format(Resty))
    #this line is optional/an alternative to the above line
            # popup = folium.Popup('<i>{}</i>'.format(entry['name']))
    
                    ).add_to(m)
            
        m.save("index.html")

    [I did not] reinvent the wheel

    Lastly, much of what I learned about Folium came from the following two videos (I recommend bookmarking them for reference):

    Follow me…

    That’s it for now folks!

    Roger Rabbit Please follow me, Chris Hoina, Senior Product Manager, ORDS, Oracle Database Tools
  • Python or Filtering in Queries: sorting JSON responses from an Oracle Database

    Python or Filtering in Queries: sorting JSON responses from an Oracle Database

    In a recent post, I discussed taking the JSON response from my REST-enabled database table (which looked like this):

    URI from ORDS REST API enabled table, Chris Hoina, ORDS, Database Tools, Oracle Autonomous Database

    And sorting those cities after I performed a GET request (using some python code). However, after a recent discussion and a suggestion from thatjeffsmith, I think I should share another way you can sort this data from the source.

    Background

    If you recall, the portion of my python code that:

    1. Performs a GET request
    2. Extracts the cities to a list, and
    3. Sorts them in ascending order,

    …looks like this:

    def city_choices_python(): 
        import requests
        import json 
        city_list = []
    
        URL = 'https://gf641ea24ecc468-dbmcdeebyface.adb.us-ashburn-1.oraclecloudapps.com/ords/pythondev/cities/'
       
        response = requests.get(URL)
        r = json.loads(response.text)
        for nestedArr in r['items']:
            city_list.append(nestedArr['city'])
        return sorted(city_list)

    As you can see, in this version the URI provided by ORDS remains untouched. I’m just using that simple “sorted(city_list)” function to return a list of sorted cities in ascending order (FYI: its the last line in this code block).

    Filtering in queries

    This approach worked fine in my case, however I am now…enlightened…

    Meaning, I am now aware that I have other options. It hadn’t really occurred to me that I could filter in queries at the time of the get request.

    Filtering is the process of limiting a collection resource by using a per-request dynamic filter definition across multiple page resources, where each page contains a subset of items found in the complete collection. Filtering enables efficient traversal of large collections.

    ORDS Docs

    What I like about this is that filtering in queries doesn’t require any changes to the the original URI. Meaning, you wouldn’t need to go back into (in my case I’m using…) Database Actions to modify anything. You simply include filters along with the URI.

    In my case it would look like something like this:

    https://gf641ea24ecc468-dbmcdeebyface.adb.us-ashburn-1.oraclecloudapps.com/ords/pythondev/cities/?q={"$orderby":{"city":"asc"}}

    Time-out!

    This will screw with your head, so listen up. Quotation marks (e.g. ”
    “) can’t be transmitted over the internet, so they need to be converted to the ASCII character-set.

    Quotations in HTML-5 will look like “%22” in a URL. So if you take that above URL copy it, and then paste it again, it may end up looking like this in your browser’s address bar:

    https://gf641ea24ecc468-dbmcdeebyface.adb.us-ashburn-1.oraclecloudapps.com/ords/pythondev/cities/?q={%22$orderby%22:{%22city%22:%22asc%22}}

    Where you’ll start out with UTF-8 encoding (quotation marks), at some point you’ll see ASCII encoding (which is a combination of the % and hexadecimal encoding).

    I only mention this because I noticed when I took the updated URL and copied it into my VS Code editor it ended up looking like this:

    def city_choices_ords(): 
        import requests
        import json 
        city_list = []
    
        URL = 'https://gf641ea24ecc468-dbmcdeebyface.adb.us-ashburn-1.oraclecloudapps.com/ords/pythondev/cities/?q={%22$orderby%22:{%22city%22:%22asc%22}}'
    
        response = requests.get(URL)
        r = json.loads(response.text)
        for nestedArr in r['items']:
            city_list.append(nestedArr['city'])
        return(city_list) 

    Don’t be alarmed, everything still works. But the query will look like this:

    ?q={%22$orderby%22:{%22city%22:%22asc%22}}

    What is this saying?

    The query above, requests that the cities table be ordered (“orderby”) in ascending (“asc”) order according to the “city” column. You can see this in action if you point your browser to it. It will display like this:

    URI with filtering in query from ORDS REST API enabled table, Chris Hoina, ORDS, Database Tools, Oracle Autonomous Database

    Ordering isn’t the only type of filtering you can do, the combinations are pretty much infinite…

    Levar Burton, in no way endorses ORDS, but if he did anything database or API-related, I like to think that he would, Chris Hoina, Senior Product Manager, Oracle, Database Tools, ORDS

    Take a look.

    Comparing performance

    One last thing…I conducted a very crude A/B performance test, comparing the two code versions.

    In short, there was no appreciable difference. I should remind that I’m working with a very small database object (a table with 2 columns and 18 cities). So if there is a difference one way or another, that difference is negligible.

    I still recorded the test for fun. And you’ll see across the ten requests (five for each version), the elapsed times were essentially the same. I know this isn’t statistically sound, but this was done to satisfy my own curiosity. Our documentation actually states that filtering is advantageous for traversing large objects, so this is probably a terrible example. And as requests scale, I have to image filtering in queries would begin to pull ahead.

    Nonetheless, see the results for yourself:

    The End

    Now you’ve got a couple options for sorting when working with a REST-enabled table (or other object). The nice thing is that setting up these endpoints with ORDS can still be accomplished with simple mouse clicks.

    Also, remember, sorting isn’t the only thing that can be accomplished when filtering in queries. There are TONs of other capabilities, too many to list here. But I would bookmark this page so you can reference later on.

    I don’t want to say you can filter everything, but it sure looks like it. And for a python newbie like myself, this might be a better approach moving forward.

    Funny story, the day I added that “sort()” function in my code was literally the same day (earlier that morning in fact) that I watched a “python tips” video, where sorting was mentioned. It was pure luck.

    Had it not been for that video, I would have been stuck for at least another day trying to figure out how to sort my list.

    Hey, at least now I know I won’t be restricted to what I can filter based on the python functions I do or don’t know. I can’t be the only one….right?

    Follow

    Be sure to follow along for more fun:

  • Working with WTForms and Oracle REST Database Services (ORDS) APIs

    Working with WTForms and Oracle REST Database Services (ORDS) APIs

    Welcome Back

    The title pretty much speaks for itself, and if you’ve been following along, you’d know that I’m working on a demo application that uses Flask (a web application microframework) connected to my Oracle Autonomous Database (ADB) via Oracle REST Data Services (ORDS) APIs.

    One of Flask’s strengths is that it allows you to extend functionality and allows for a good bit of customization. But…

    By default, Flask does not include a database abstraction layer, form validation or anything else where different libraries already exist that can handle that. Instead, Flask supports extensions to add such functionality to your application as if it was implemented in Flask itself.

    Flask Docs

    Why ADB and ORDS?

    I’m primarily using this specific configuration (with ORDS and ADB) for obvious reasons. But secondarily, I haven’t come across anything that actually references the Oracle ADB or ORDS (in the context of Flask, WTForms, and python of course).

    Most of the online tutorials I’ve seen and books I’ve read reference SQLlite or (in one case) MongoDB for the database. SQLite does seem like a good entry point, especially if you are putting together a proof of concept. But as things scale, I suspect you’ll reach diminishing performance returns with SQLite. In fact, the Flask documentation mentions exactly that:

    Python comes with built-in support for SQLite in the sqlite3 module. SQLite is convenient because it doesn’t require setting up a separate database server and is built-in to Python. However, if concurrent requests try to write to the database at the same time, they will slow down as each write happens sequentially. Small applications won’t notice this. Once you become big, you may want to switch to a different database.

    Flask Docs

    That last sentence is significant. I know ORDS can be set up with High Availability in mind, so why not take advantage of that from the beginning? Also, with my ADB, the effort to create a database schema and tables is very minimal (fun even).

    Regarding, MongoDB, obviously, I’m biased but I’m also disorganized. And I’m still a “technical toddler.” So having to download, install, and configure MongoDB Community Edition plus the MongoDB Compass GUI would end up being an unnecessary step for me (I actually ended up doing this for a previous tutorial).

    Regardless, I already have my OCI tenancy and my Autonomous Database, and I continue to get more and more comfortable with our ecosystem.

    Sign up for an Always Free OCI account, Oracle Cloud Infrastructure, Chris Hoina, Senior Product Manager, Database Tools, ORDS
    Also: You should sign up for an 
    Always Free OCI account.

    Moving on…I also want to use Database Actions, and the REST API features provided by ORDS. Not to mention, from everything I’ve seen in the Flask tutorials you have to first create database models before you can do anything really meaningful. I’m not entirely sure how to go about that (please check back in 2023).

    ORDS is straightforward – just give me the URI for the API endpoint and I’ll take it from there. If I need something additional we can modify the API accordingly.

    API GET Request response (via ORDS)

    Speaking of which, here is what the JSON looks like from my ORDS-powered API GET request:

    ORDS REST API response from Autonomous Database, Chris Hoina, Senior Product Manager, Database Tools, ORDS

    I won’t spend too much time explaining this image (you can read about it in detail, here), but I’m most interested in the city: (found under items). There are 18 cities, and I’ll need all of them for Flask. More specifically, I need them for WTForms.

    About WTForms

    I won’t give a crash course on WTForms, but bottom line it’s another way to extend the capabilities of Flask.

    WTForms is a flexible forms validation and rendering library for Python web development. It can work with whatever web framework and template engine you choose. It supports data validation, CSRF protection, internationalization (I18N), and more.

    WTForms Docs

    In my scenario, WTForms allows me to easily make and handle (as in routing) forms in the Flask application. More specifically, there is a SelectFields() form that I need to use (this is just how I initially designed the app, you can review it here).

    Choices

    The forms have fields. Here are details on the form I’m most interested in:

    WTForms Flask Python ORDS REST APIs Autonomous Database, Chris Hoina, Senior Product Manager, ORDS, Database Tools

    You’ll notice there are various parameters I can include, but all I care about are the choices[]. You can either hard (static) code in a list of choices, or (as I just successfully did as of this week), pass a list from an API GET request to the choices[] parameter.

    If you’ve looked at the documentation, then you’ve seen that choices[] can be”passed” in one of three ways:

    1. a list of (value, label) pairs (it can also be a list of only values, in which case the value is used as the label)
    2. a dictionary of {label: list} pairs defining groupings of options, or
    3. a function taking no argument, and returning either a list or a dictionary

    My approach used the first option. Actually, the values-only approach, in my case.

    The breakdown

    First, I’ll show the dropdown list; it will look like this in my application (FYI this actually works):

    Flask application in action

    I know that video is fast, but you should be able to see the dropdown list (at around the :15-:17 mark) and how it renders.

    This dropdown list is made possible from an initial GET request to my ORDS-enabled “Cities” table. And the code in its entirety (including the Flask bits), looks like this:

    Flask WTForms Python ORDS REST APIs Autonomous Database, Chris Hoina, Senior Product Manager, ORDS, Database Tools
    NOTE: I'm working off the assumption that you may have some familiarity with flask and WTForms. And also that your focus is the REST API - to - choices part of this problem. 

    By the numbers

    Okay, a quick overview:

    • Lines 27-29: This is pretty standard for Flask, no explanation is needed
    • Lines 31-33: Are where I build out the form:
      • You’ll see that city is the field (to select from)
      • The dropdown will have a heading that says, “Let’s begin by choosing a city” (you can see this in the video)
      • And the choices will be a list []
      • Submit on line 33 is just how you’d create the submit button
    • Lines 37-39: Are also pretty standard for any GET request using the Requests library in python
    NOTE: But here, you'll see that my URL is actually the URI provided by ORDS (remember, I REST-enabled my cities table - that is the "endpoint" we are seeing) 
    • Line 40: I make a list (it’s empty at this step, so sad)
    • Lines 41-43: This is the pièce de résistance.
      • Here we perform a loop through the items: in the JSON response, and since this is a nested array, you have to specify what array you want and what exactly in that array you want to pull out (in this case the “city”)
      • Line 43 was a suggestion by thatjeffsmith, to display the list in alphabetical order (I must confess, it does better)
    • Lines 45-48: These are pretty standard for Flask as well. You need to set city (in line 32) equal to the city_list so WTForms knows where the SelectField(choices=[]) comes from

    The Code

    With the brief explanation out of the way, here is the code. Please, help yourself:

    #For the GET request portion, you'll at a minimum need requests and json. If you are working in Flask already, then you know the rest. 
    import requests
    import json 
    from wtforms import SelectField, SubmitField
    from flask import Flask, json, jsonify, render_template, request
    from flask_wtf import FlaskForm
    
    #Creating the Flask instance.
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'password1234'
    
    #Creating the Form.
    class CitiesForm(FlaskForm):
      city = SelectField("Let's begin by choosing a city", choices=[])
      submit = SubmitField('Submit')
    
    #Routes, used for displaying the html on your web app.
    @app.route('/', methods = ['GET', 'POST'])
    def index():
    
    #This is the section where you perform the GET request to retrieve the JSON from your ORDS endpoint.
        URL = '[your ORDS endpoint]'
        response = requests.get(URL)
        r = json.loads(response.text)
    
    #Creating the list of cities; which will be populated shortly.
    city_list = []
    
    #Looping through the nested array (from the inital JSON / API response). 
        for nestedArr in r['items']:
          city_list.append(nestedArr['city'])
          city_list.sort()
    
    #Finishing touches so WTForms, and Flask know where to look. And also setting up how/where the html on your web application renders.
    
        city=city_list
        form = CitiesForm()
        form.city.choices = city_list 
        return render_template('index.html', form=form)

    Obviously, you’ll want to change up some of the names. But the ORDS pieces should remain pretty similar.

    Next up

    My next steps are to create a second and third dependent dropdown for the app. What I mean is that, based on a user’s city selection, that city would then be used as a query parameter for a subsequent GET request (to my ADB). And ORDS would respond with a list that includes restaurant information for the target city (everything else would be excluded).

    If you’ve made it this far and you have any questions, please send me a note.

    And if you think this is worth sharing, and that it might help others, please pass it around!

    Cash me outside

    As always, you can find me on: