Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Book Overview & Buying Mastering Flask Web and API Development
  • Table Of Contents Toc
  • Feedback & Rating feedback
Mastering Flask Web and API Development

Mastering Flask Web and API Development

By : Sherwin John C. Tragura
5 (2)
close
close
Mastering Flask Web and API Development

Mastering Flask Web and API Development

5 (2)
By: Sherwin John C. Tragura

Overview of this book

Flask is a popular Python framework known for its lightweight and modular design. Mastering Flask Web and API Development will take you on an exhaustive tour of the Flask environment and teach you how to build a production-ready application. You’ll start by installing Flask and grasping fundamental concepts, such as MVC and ORM database access. Next, you’ll master structuring applications for scalability through Flask blueprints. As you progress, you’ll explore both SQL and NoSQL databases while creating REST APIs and implementing JWT authentication, and improve your skills in role-based access security, utilizing LDAP, OAuth, OpenID, and databases. The new project structure, managed by context managers, as well as ASGI support, has revolutionized Flask, and you’ll get to grips with these crucial upgrades. You'll also explore out-of-the-box integrations with technologies, such as RabbitMQ, Celery, NoSQL databases, PostgreSQL, and various external modules. The concluding chapters discuss enterprise-related challenges where Flask proves its mettle as a core solution. By the end of this book, you’ll be well-versed with Flask, seeing it not only as a lightweight web and API framework, but also as a potent problem-solving tool in your daily work, addressing integration and enterprise issues alongside Django and FastAPI.
Table of Contents (18 chapters)
close
close
1
Part 1:Learning the Flask 3.x Framework
6
Part 2:Building Advanced Flask 3.x Applications
12
Part 3:Testing, Deploying, and Building Enterprise-Grade Applications

Managing request and response data

At this point, we already know that routing is a mechanism for mapping view functions to their URLs. But besides that, routing declares any valid functions to be view implementations that can manage the incoming request and outgoing response.

Retrieving the request object

Flask uses its request object to carry cookies, headers, parameters, form data, form objects, authorization data, and other request-related details. But the view function doesn’t need to declare a variable to auto-wire the request instance, just like in Django, because Flask has a built-in proxy object for it, the request object, which is part of the flask package. The following view function takes the username and password request parameters and checks if the credentials are in the database:

from __main__ import app
from flask import request, Response, render_template, redirect
from repository.user import validate_user
@app.route('/login/params')
def login_with_params():
    username = request.args['username']
    password = request.args['password']
    result = validate_user(username, password)
    if result:
      resp = Response(
       response=render_template('/main.html'), status=200, content_type='text/html')
      return resp
    else:
        return redirect('/error')

For instance, running the URL pattern of the given view function, http://localhost:5000/login/params?username=sjctrags&password=sjctrags2255, will provide us with sjctrags and sjctrags2255 as values when request.args['username'] and request.args['password'] are accessed, respectively.

Here is the complete list of objects and details that we can retrieve from the Request object through its request instance proxy:

  • request.args: Returns a MultiDict class that carries URL arguments or request parameters from the query string.
  • request.form: Returns a MultiDict class that contains parameters from an HTML form or JavaScript’s FormData object.
  • request.data: Returns request data in a byte stream that Flask couldn’t parse to form parameters and values due to an unrecognizable mime type.
  • request.files: Returns a MultiDict class containing all file objects from a form with enctype=multipart/form-data.
  • request.get_data(): This function returns the request data in byte streams before calling request.data.
  • request.json: Returns parsed JSON data when the incoming request has a Content-Type header of application/json.
  • request.method: Returns the HTTP method name.
  • request.values: Returns the combined parameters of args and form and encounters collision problems when both args and form carry the same parameter name.
  • request.headers: Returns request headers included in the incoming request.
  • request.cookies: Returns all the cookies that are part of the request.

The following view function utilizes some of the given request objects to perform an HTTP GET operation to fetch a user login application through an ID value and an HTTP POST operation to retrieve the user details, approve its preferred user role, and save the login details as new, valid user credentials:

from __main__ import app
from flask import render_template
from model.candidates import AdminUser, CounselorUser, PatientUser
from urllib.parse import parse_qsl
@app.route('/signup/approve', methods = ['POST'])
@app.route('/signup/approve/<int:utype>',methods = ['GET'])
def signup_approve(utype:int=None):
    if (request.method == 'GET'):
        id = request.args['id']
        user = select_single_signup(id)
        … … … … … … …
    else:
        utype = int(utype)
        if int(utype) == 1:
            adm = request.get_data()
            adm_dict = dict(parse_qsl(adm.decode('utf-8')))
            adm_model = AdminUser(**adm_dict)
            user_approval_service(int(utype), adm_model)
        elif int(utype) == 2:
            cnsl = request.get_data()
            cnsl_dict = dict(parse_qsl(
                   cnsl.decode('utf-8')))
            cnsl_model = CounselorUser(**cnsl_dict)
            user_approval_service(int(utype), cnsl_model)
        elif int(utype) == 3:
            pat = request.get_data()
            pat_dict = dict(parse_qsl(pat.decode('utf-8')))
            pat_model = PatientUser(**pat_dict)
            user_approval_service(int(utype), pat_model)
        return render_template('approved_user.html', message='approved'), 200

Our application has a listing view that renders hyperlinks that can redirect users to this signup_approve() form page with a context variable id, a code for a user type. The view function retrieves the variable id through request.args, checks what the user type id is, and renders the appropriate page based on the user type detected. The function also uses request.method to check if the user request will pursue either the GET or POST transaction since the given view function caters to both HTTP methods, as defined in its dual route declaration. When clicking the Submit button on the form page, its POST transaction retrieves all the form parameters and values in a byte stream type via request.get_data(). It is decoded to a query string object and converted into a dictionary by parse_sql from the urllib.parse module.

Now, if Flask can handle the request, it can also manage the outgoing response from the view functions.

Creating the response object

Flask uses Response to generate a client response for every request. The following view function renders a form page using the Response object:

from flask import render_template, request, Response
@app.route('/admin/users/list')
def generate_admin_users():
    users = select_admin_join_user()
    user_list = [list(rec) for rec in users]
    content = '''<html><head>
                    <title>User List</title>
            </head><body>
                    <h1>List of Users</h1>
                    <p>{}
            </body></html>
           '''.format(user_list)
    resp = Response(response=content, status=200, content_type='text/html')
    return resp

Response is instantiated with its required constructor parameters and returned by the view function as a response object. The following are the required parameters:

  • response: Contains the content that needs to be rendered either in a string, byte stream, or iterable of either of the two types.
  • status: Accepts the HTTP status code as an integer or string.
  • content_type: Accepts the mime type of the response object that needs rendering.
  • headers: A dictionary that contains the response header(s) that is/are necessary for the rendition process, such as Access-Control-Allow-Origin, Content-Disposition, Origin, and Accept.

But if the purpose is to render HTML pages, Flask has a render_template() method that references an HTML template file that needs rendering. The following route function, signup_users_form(), yields the content of a signup page – that is, add_signup.html from the /pages template folder – for new user applicants:

@app.route('/signup/form', methods= ['GET'])
def signup_users_form():
    resp = Response(  response=render_template('add_signup.html'), status=200, content_type="text/html")
    return resp

render_template() returns HTML content with its context data, if there is any, as a string. To simplify the syntax, Flask allows us to return the method’s result and the status code instead of the Response instance since the framework can automatically create a Response instance from these details. Like the previous examples, the following signup_list_users() uses render_template() to show the list of new user applications subject to admin approval:

@app.route('/signup/list', methods = ['GET'])
def signup_list_users():
    candidates = select_all_signup()
    return render_template('reports/list_candidates.html', records=candidates), 200

The given code emphasizes that render_template() can accept and pass context data to the template page. The candidates variable in this snippet handles an extracted list of records from the database needed by the template for content generation using the Jinja2 engine.

Jinja2

Jinja2 is Python’s fast, flexible, robust, expressive, and extensive templating engine for creating HTML, XML, LaTeX, and other supported formats for Flask’s rendition purposes.

On the other hand, Flask has a utility called make_response() that can modify the response by changing headers and cookies before sending them to the client. This method is suitable when the base response frequently undergoes some changes in its response headers and cookies. The following code modifies the content type of the original response to XLS with a given filename – in this case, question.xls:

@app.route('/exam/details/list')
def report_exam_list():
    exams = list_exam_details()
    response = make_response( render_template('exam/list_exams.html', exams=exams), 200)
    headers = dict()
    headers['Content-Type'] = 'application/vnd.ms-excel'
    headers['Content-Disposition'] = 'attachment;filename=questions.xls'
    response.headers = headers
    return response

Flask will require additional Python extensions when serializing and yielding PDF, XLSX, DOCX, RTF, and other complex content types. But for old and simple mime type values such as application/msword and application/vnd.ms-excel, Flask can easily and seamlessly serialize the content since Python has a built-in serializer for them. Other than mime types, Flask also supports adding web cookies for route functions. The following assign_exam() route shows how to add cookies to the response value that renders a form for scheduling and assigning counseling exams for patients with their respective counselors:

@app.route('/exam/assign', methods=['GET', 'POST'])
def assign_exam():
    if request.method == 'GET':
        cids = list_cid()
        pids = list_pid()
        response = make_response( render_template('exam/assign_exam_form.html', pids=pids, cids=cids), 200)
        response.set_cookie('exam_token', str(uuid4()))
        return response, 200
    else:
        id = int(request.form['id'])
        cid = request.form['cid']
        pid = int(request.form['pid'])
        exam_date = request.form['exam_date']
        duration = int(request.form['duration'])
        result = insert_question_details(id=id, cid=cid, pid=pid, exam_date=exam_date, duration=duration)
        if result:
            task_token = request.cookies.get('exam_token')
            task = "exam assignment (task id {})".format(task_token)
            return redirect(url_for('redirect_success_exam',        message=task ))
        else:
            return redirect('/exam/task/error')

The Response instance has a set_cookie() method that creates cookies before the view dispatches the response to the client. It also has delete_cookie(), which deletes a particular cookie before yielding the response. To retrieve the cookies, request.cookies has a get() method that can retrieve the cookie value through its cookie name. The given assign_exam() route shows how the get() method retrieves exam_cookie in its POST transaction.

Implementing page redirection

Sometimes, it is ideal for the route transaction to redirect the user to another view page using the redirect() utility method instead of building its own Response instance. Flask redirection requires a URL pattern of the destination to where the view function will redirect. For instance, in the previous assign_exam() route, the output of its POST transaction is not a Response instance but a redirect() method:

@app.route('/exam/assign', methods=['GET', 'POST'])
def assign_exam():
        … … … … … …
        if result:
            task_token = request.cookies.get('exam_token')
            task = "exam assignment (task id {})".format(task_token)
            return redirect(url_for('redirect_success_exam', message=task ))
        else:
            return redirect('/exam/task/error')

When the result variable is False, redirection to an error view called /exam/task/error will occur. Otherwise, the route will redirect to an endpoint or view name called redirect_success_exam. Every @route has an endpoint equivalent, by default, to its view function name. So, redirect_success_exam is the function name of a route with the following implementation:

@app.route('/exam/success', methods=['GET'])
def redirect_success_exam():
    message = request.args['message']
    return render_template('exam/redirect_success_view.html', message=message)

url_for(), which is used in the assign_exam() view, is a route handler that allows us to pass the endpoint name of the destination view to redirect() instead of passing the actual URL pattern of the destination. It can also pass context data to the Jinja2 template of the redirected page or values to path variables if the view uses a dynamic URL pattern. The redirect_success_exam() function shows a perfect scenario of context data passing, where it uses request.args to access a message context passed from assign_exam(), which is where the redirection call originated.

More content negotiations and how to serialize various mime types for responses will be showcased in the succeeding chapters, but in the meantime, let’s scrutinize the view templates of our route functions. View templates are essential for web-based applications because all form-handling transactions, report generation, and page generation depend on effective dynamic templates.

Create a Note

Modal Close icon
You need to login to use this feature.
notes
bookmark search playlist download font-size

Change the font size

margin-width

Change margin width

day-mode

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Delete Bookmark

Modal Close icon
Are you sure you want to delete it?
Cancel
Yes, Delete

Delete Note

Modal Close icon
Are you sure you want to delete it?
Cancel
Yes, Delete

Edit Note

Modal Close icon
Write a note (max 255 characters)
Cancel
Update Note

Confirmation

Modal Close icon
claim successful

Buy this book with your credits?

Modal Close icon
Are you sure you want to buy this book with one of your credits?
Close
YES, BUY