English 中文(简体)
Python Falcon - Jinja2 Template
  • 时间:2024-11-03

Python Falcon - Jinja2 Template


Previous Page Next Page  

The Falcon pbrary is primarily used to build APIs and microservices. Hence, by default, a Falcon responder returns a JSON response. However, if the content type is changed to falcon.MEDIA_HTML, it is possible to render HTML output.

Rendering a HTML content with variable data is very tedious. For this purpose, web templating pbraries are used. Many Python web frameworks are bundled with specific template pbrary. But Falcon being a minimapst micro framework doesn t come pre-bundled with anyone.

Jinja2 is one of the most popular template pbraries used by many python frameworks. In this section, we shall see how to use inja2 with Falcon apppcation. The jinja2 is a fast and designer-friendly templating language that is easy to configure and debug. Its sandboxed environment makes it easy to prevent the execution of untrusted code, prohibit potentially unsafe data, and prevent cross-site scripting attacks (called XSS attacks).

Another very powerful feature of jinja2 is the template inheritance, wherein You can define a base template having common design features which child templates can override.

First of all, install jinja2 in the current Python environment with the use of PIP utipty.


pip3 install jinja2

Hello World Template

The jinja2 module defines a Template class. A Template object is obtained by reading the contents of a file containing HTML script (one with .html extension). By invoking the render() method of this Template object, HTML response can be rendered to the cpent browser. The content_type property of Response object must be set to falcon.MEDIA_HTML.

Let us save the following HTML script as hello.py in the apppcation folder.


<html>
   <body>
      <h2>Hello World</h2>
   </body>
</html>

Example

The on_get() responder in the resource class below reads this file and renders it as HTML response.


import uvicorn
import falcon
import falcon.asgi
from jinja2 import Template
class HelloResource:
   async def on_get(self, req, resp):
      resp.status = falcon.HTTP_200
      resp.content_type =  text/html 
      fp=open("hello.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render()
app = falcon.asgi.App()
hello = HelloResource()
app.add_route( /hello , hello)
if __name__ == "__main__":
   uvicorn.run("hello:app", host="0.0.0.0", port=8000, reload=True)

Output

Run the above Python code and visit http://localhost:8000/hello pnk in the browser.

Jinja2

Template Variable

jinja2 is a server-side templating pbrary. The web page is constructed as a template by putting various elements of jinja2 templating language as place-holders within appropriate depmiters inside the HTML script. The template engine reads the HTML script, substitutes the place-holders with context data on the server, reassembles the HTML, and renders it to the cpent.

The Template.render() function has an optional context dictionary parameter. The key attributes of this dictionary become the template variables. This helps in rendering the data passed by the responders in the web page.

Example

In the following example, the route /hello/nm is registered with the resource object, where nm is the path parameter. The on_get() responder passes it as a context to the template object obtained from a web page.


import uvicorn
import falcon
import falcon.asgi
from jinja2 import Template
class HelloResource:
   async def on_get(self, req, resp, nm):
      resp.status = falcon.HTTP_200
      resp.content_type =  text/html 
      fp=open("hello.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render({ name :nm})
app = falcon.asgi.App()
hello = HelloResource()
app.add_route( /hello/{nm} , hello)
if __name__ == "__main__":
   uvicorn.run("hello:app", host="0.0.0.0", port=8000, reload=True)

The hello.html reads the path parameter in a template variable name. It acts as a place holder in the HTML script. It is put in {{ and }} symbols so that its value appears as a HTML response.


<html>
   <body>
      <h2>Hello {{ name }}</h2>
   </body>
</html>

Output

Run the Python code and enter http://localhost:8000/hello/Priya as the URL. The browser displays the following output −

Jinja2 Hello

Loop in jinja2 Template

If the responder passes any Python iterable object such as a pst, tuple or a dictionary, its elements can be traversed inside the jinja2 template using its looping construct syntax.


{% for item in collection %}
HTML block
{% endfor %}

In the following example, the on_get() responder sends students object which is a pst of dict objects, to the template pst.html. It in turn traverses the data and renders it as a HTML table.


import falcon
import json
from waitress import serve
from jinja2 import Template
students = [
   {"id": 1, "name": "Ravi", "percent": 75.50},
   {"id": 2, "name": "Mona", "percent": 80.00},
   {"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_HTML
      fp=open("pst.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render({ students :students})

pst.html is a jinja2 template. It receives the students object as pst of dictionary objects and puts the value of each key inside <td>..<.td> element of a table.


<html>
<body>
<table border=1>
   <thead> <tr>
      <th>Student ID</th> <th>Student Name</th>
      <th>percentage</th>
      <th>Actions</th>
   </tr> </thead>
   <tbody>
   {% for Student in students %}
   <tr> <td>{{ Student.id }}</td> <td>{{ Student.name }}</td>
      <td>{{ Student.percent }}</td>
      <td>
         <a href="#">Edit</a>
         <a href="#">Delete</a>
      </td> </tr>
   {% endfor %}
   </tbody>
</table>
</body>
</html>

Visit the /students route in the browser s address bar. The pst of students is rendered in the browser.

Jinja2 Image

HTML Form Template

In this section, we shall see how Falcon reads the data from HTML form. Let us save the following HTML script as myform.html. We shall use it for obtaining Template object and render it.


<html>
<body>
   <form method="POST" action="http://localhost:8000/students">
   <p>Student Id: <input type="text" name="id"/> </p>
   <p>student Name: <input type="text" name="name"/> </p>
   <p>Percentage: <input type="text" name="percent"/> </p>
   <p><input type="submit"> </p>
</body>
</html>

The Falcon App object is declared in Hello.py file which also has a resource class mapped to /adddnew route. The on_get() responder reads the myform.html and renders the same. The HTML form will be displayed. The form is submitted to /students route by POST method.

To be able to read the form data, the auto_parse_form_urlencoded property of falcon.RequestOptions class must be set to True.


app = falcon.App()
app.req_options.auto_parse_form_urlencoded = True

Here, we also import StudentResource class from student.py. The on_get() responder renders the pst of students.

The on_post() responder will be called when the user fills and submits the form. This method collects the form data in the req.params property, which is nothing but a dictionary of form elements and their values. The students dictionary is then appended.


def on_post(self, req, resp):
   student=req.params
   students.append(student)

The complete code of hello.py is as follows −


import falcon
import json
from waitress import serve
from jinja2 import Template
from student import StudentResource
class MyResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_200
      resp.content_type =  text/html 
      fp=open("myform.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render()
app = falcon.App()
app.req_options.auto_parse_form_urlencoded = True
form = MyResource()
app.add_route( /addnew , form)
app.add_route("/students", StudentResource())
if __name__ ==  __main__ :
   serve(app, host= 0.0.0.0 , port=8000)

The student.py having StudentResource class and on_get() and on_post() responders is as follows −


import falcon
import json
from waitress import serve
from jinja2 import Template
students = [
   {"id": 1, "name": "Ravi", "percent": 75.50},
   {"id": 2, "name": "Mona", "percent": 80.00},
   {"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_HTML
      fp=open("pst.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render({ students :students})

   def on_post(self, req, resp):
      student = req.params
      students.append(student)
      resp.text = "Student added successfully."
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON

Run hello.py from the command pne. Open the HTML form in the browser by entering http://locLhost:8000/addnew.

Jinja2 Host

The students database dictionary will be appended. Visit /students route. You will find a new row appended.

Jinja2 Example

Multipart Forms

In order to let the user select files from the local filesystem, the enctype attribute of HTML form must be set to multipart/form-data. Falcon uses MultipartFormHandler to handle the multipart/form-data media type, allowing it to iterate over the body parts in the form.

The BodyPart class has the following properties −

    stream − stream wrapper just for the current body part

    data − body part content bytes

    content_type would default to text/plain if not specified, as per RFC

    text − the current body part decoded as text string (only provided it is of type text/plain, None otherwise)

    media − automatically parsed by media handlers in the same way as req.media

    name, filename − relevant parts from the Content-Disposition header

    secure_filename − sanitized filename that could safely be used on the server filesystem.

The following HTML script (index.html) is a multi-part form.


<html>
   <body>
      <form action="http://localhost:8000/hello" method="POST" enctype="multipart/form-data">
         <h3>Enter User name</h3>
         <p><input type= text  name= name /></p>
         <h3>Enter address</h3>
         <p><input type= text  name= addr /></p>
         <p><input type="file" name="file" /></p>
         <p><input type= submit  value= submit /></p>
      </form>
   </body>
</html>

This form is rendered by the on_get() responder of the HelloResource class in the code below. The form data is submitted to on_post() method which iterates over the parts and sends a JSON response of the form data.


import waitress
import falcon
import json
from jinja2 import Template
class HelloResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_200
      resp.content_type =  text/html 
      fp=open("index.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render()

   def on_post(self, req, resp):
      result=[]
      for part in req.media:
         data={"name" :part.name,
            "content type":part.content_type,
            "value":part.text, "file":part.filename}
         result.append(data)
         resp.text = json.dumps(result)
         resp.status = falcon.HTTP_OK
         resp.content_type = falcon.MEDIA_JSON
app = falcon.App()
hello = HelloResource()
app.add_route( /hello , hello)
if __name__ ==  __main__ :
   waitress.serve(app, host= 0.0.0.0 , port=8000)

Run the above program and visit http://localhost:8000/hello pnk to render the form as shown below −

Jinja2 User

When the form is submitted after filpng the data, the JSON response is rendered in the browser as shown below −


[
   {
      "name": "name",
      "content type": "text/plain",
      "value": "SuyashKumar Khanna",
      "file": null
   },
   {
      "name": "addr",
      "content type": "text/plain",
      "value": "New Delhi",
      "file": null
   },
   {
      "name": "file",
      "content type": "image/png",
      "value": null,
      "file": "hello.png"
   }
]
Advertisements