Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Mastering Flask Web and API Development
Mastering Flask Web and API Development

Mastering Flask Web and API Development: Build and deploy production-ready Flask apps seamlessly across web, APIs, and mobile platforms

eBook
₹799.99 ₹2382.99
Paperback
₹2084.99 ₹2978.99
Subscription
Free Trial
Renews at ₹800p/m

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
OR
Modal Close icon
Payment Processing...
tick Completed

Billing Address

Table of content icon View table of contents Preview book icon Preview Book

Mastering Flask Web and API Development

A Deep Dive into the Flask Framework

Flask is a Python web framework that was created by Armin Ronacher to solve both web-based and API-related requirements that need a rapid development approach. It is a lightweight framework with helper classes and methods, a built-in server, a debugger, and a reloader, all of which are required for building scalable web applications and web services.

Unlike the Django framework, Flask is minimalistic and slimmer in that it requires more experience in using Python to craft various coding techniques and workarounds to implement its components. It is more open-ended and extensible than the full-stack Django, which is more friendly to newbies because of its easy-to-build projects and reusable components.

This first chapter will showcase the essential task itineraries that cover the initial components and base features of Flask 3.x that are essential in initiating our web development.

In this chapter, we will cover the following development tasks:

  • Setting up the project baseline
  • Creating routes and navigations
  • Managing the requests and response data
  • Implementing view templates
  • Creating web forms
  • Building the data layer with PostgreSQL
  • Managing the project structure

Technical requirements

The first chapter will focus on building a prototype for an Online Personal Counseling System that simulates a face-to-face consultation between a patient and a counselor while highlighting the base components of Flask 3.x. The application will cover modules such as managing users, questionnaires, and some reports. The code for this chapter can be found at https://github.com/PacktPublishing/Mastering-Flask-Web-Development/tree/main/ch01.

Setting up the project baseline

Gathering and studying the system requirements for the development environment for the proposed project is essential. Some of these requirements include the correct versions of the installers and libraries, the appropriate servers, and the inclusion of other essential dependencies. We have to perform various setups before kicking off our projects.

Installing the latest Python version

All our applications will run on the Python 11 environment for faster performance. The updated Python installer for all operating systems is available at https://www.python.org/downloads/.

Installing the Visual Studio (VS) Code editor

The Django framework has a django-admin command that generates a project structure, but Flask does not have that. We can use a terminal console or a tool such as the Visual Studio (VS) Code editor that can help developers create a Flask project. The VS Code installer is available at https://code.visualstudio.com/download.

After installing the VS Code editor, we can create a filesystem folder through it and start a Flask project. To create the folder, we should go to the Open Folder option under File or use the Ctrl + K + O shortcut to open the Open Folder mini-window. Figure 1.1 shows a sample process of creating a Flask project using the editor:

Figure 1.1 – Creating a Flask project folder using the VS Code editor

Figure 1.1 – Creating a Flask project folder using the VS Code editor

Creating the virtual environment

Another aspect of developing a Flask project is having a repository called a virtual environment that can hold its libraries. It is a mechanism or a tool that can manage all dependencies of a project by isolating these dependencies from the global repository and other project dependencies. The following are the advantages of using this tool in developing Flask-based applications:

  • It can avoid broken module versions and collisions with other existing similar global repository libraries.
  • It can help build a dependency tree for the project.
  • It can help ease the deployment of applications with libraries to both physical and cloud-based servers.

A Python extension named virtualenv is required to set up these virtual environments. To install the extension, run the following command in the terminal:

pip install virtualenv

After this installation, we need to run python virtualenv -m ch01-01 to create our first virtual environment for our Flask project. Figure 1.2 shows a snapshot of creating our ch01-env repository:

Figure 1.2 – Creating a virtual environment

Figure 1.2 – Creating a virtual environment

The next step is to open the project and link it to the virtual environment created for it. Pressing Ctrl + Shift + P in VS Code will open the Command Palette area. Here, we can search for Python: Select Interpreter. Clicking this option will lead you to the Enter interpreter path… menu command and eventually to the Find… option. This Find… option will help you locate the virtual environment’s Python.exe file in the /Scripts folder. Figure 1.3 shows a snapshot of locating the Python interpreter in the repository’s /Scripts folder:

Figure 1.3 – Locating the Python interpreter of the virtual environment

Figure 1.3 – Locating the Python interpreter of the virtual environment

Afterward, the virtual environment must be activated for the project to utilize it. You must run /Scripts/activate.bat in Windows or /bin/activate in Linux through the editor’s internal console. Upon activation, the terminal should show the name of the virtual environment in its prompt (for example, (ch01-env) C:\).

Installing the Flask 3.x libraries

The integrated terminal of VS Code will appear after right-clicking the explorer portion of the editor, which leads to the Open in Integrated Terminal option. Once it appears on the lower right-hand side, activate the virtual environment first, then install all Flask dependencies into the repository by running pip install flask.

Once all the requirements are in place, we are ready to create our baseline application.

Creating the Flask project

The first component that must be implemented in the main project folder (that is, ch01) is the application file, which can be main.py or sometimes app.py. This component will become the top-level module the Flask will recognize when the server starts. Here is the baseline application file for our Online Personal Counseling System prototype:

from flask import Flask
app = Flask(__name__)
@app.route('/', methods = ['GET'])
def index():
    return "This is an online … counseling system (OPCS)"
if __name__ == '__main__':
    app.run(debug=True)

Let’s dissect and scrutinize the essential parts of the given main.py file:

  • An imported Flask class from the flask package plays a considerable role in building the application. This class provides all the utilities that implement the Werkzeug specifications, which include features such as managing the requests and the responses of every route, redirecting pages, handling form data, accessing and creating cookies, parsing custom and built-in headers, and even providing debuggers for the development environment. In other words, the Flask instance is the main element in building a Web Server Gateway Interface (WSGI)-compliant application.

Werkzeug

Werkzeug is a WSGI-based library or module that provides Flask with the necessary utilities, including a built-in server, for running WSGI-based applications.

  • The imported Flask instance must be instantiated once per application. The __name__ argument must be passed to its constructor to provide Flask with a reference to the main module without explicitly setting its actual package. Its purpose is to provide Flask with the reach it needs in providing the utilities across the application and to register the components of the project to the framework.
  • The if statement tells the Python interpreter to run Werkzeug’s built-in development server if the module is main.py. This line validates the main.py module as the top-level module of the project.
  • app.run() calls and starts the built-in development server of Werkzeug. Setting its debug parameter to True sets development or debug mode and enables Werkzeug’s debugger tool and automatic reloading. Another way is to create a configuration file that will set FLASK_DEBUG to True. We can also set development mode by running main.py using the flask run command with the --debug option. Other configuration approaches before Flask 3.0, such as using FLASK_ENV, are already deprecated.

Running the python main.py command on the VS Code terminal will start the built-in development server and run our application. A server log will be displayed on the console with details that include the development mode, the debugger ID, and the URL address. The default port is 5000, while the host is localhost.

Now, it is time to explore the view functions of our Flask application. These are the components that manage the incoming requests and outgoing responses.

Creating routes and navigations

Routing is a mapping of URL pattern(s) and other related details to a view function that’s done using Flask’s route decorators. On the other hand, the view function is a transaction that processes an incoming request from the clients and, at the same time, returns the necessary response to them. It follows a life cycle and returns an HTTP status as part of its response.

There are different approaches to assigning URL patterns to view functions. These include creating static and dynamic URL patterns, mapping URLs externally, and mapping multiple URLs to a view function.

Creating static URLs

Flask has several built-in route decorators that implement some of its components, and @route decorator is one of these. @route directly maps the URL address to the view function seamlessly. For instance, @route maps the index() view function presented in the project’s main.py file to the root URL or /, which makes index() the view function of the root URL.

But @route can map any valid URL pattern to any view function. A URL pattern is accepted if it follows the following best practices:

  • All characters must be in lowercase.
  • Use only forward slashes to establish site hierarchy.
  • URL names must be concise, clear, and within the business context.
  • Avoid spaces and special symbols and characters as much as possible.

The following home() view function renders an introductory page of our ch01 application and uses the URL pattern of /home for its access:

@app.route('/home')
def home():
    return '''
       <html><head><title>Online Personal … System</title>
          </head><body>
           <h1>Online … Counseling System (OPCS)</h1>
           <p>This is a template of a web-based counseling
              application where counselors can … … …</em>
           </body></html>
       '''

Now, Flask accepts simple URLs such as /home or complex ones with slashes and path-like hierarchy, including these multiple URLs.

Assigning multiple URLs

A view function can have a stack of @route decorators annotated on it. Flask allows us to map these valid multiple URLs if there is no conflict with other view functions and within that stack of @route mappings. The following version of the home() view function now has three URLs, which means any of these addresses can render the home page:

@app.route('/home')
@app.route('/information')
@app.route('/introduction')
def home():
    return '''<html><head>
             <title>Online Personal … System</title>
        </head><body>
           <h1>Online … Counseling System (OPCS)</h1>
            … … … … …
        </body></html>
       '''

Aside from complex URLs, Flask is also capable of creating dynamic routes.

Applying path variables

Adding path variables makes a URL dynamic and changeable depending on the variations of the values passed to it. Although some SEO experts may disagree with having dynamic URLs, the Flask framework can allow view functions with changeable URL patterns to be implemented.

In Flask, a path variable is declared inside a diamond operator (<>) and placed within the URL path. The following view function has a dynamic URL with several path variables:

@app.route('/exam/passers/list/<float:rate>/<uuid:docId>')
def report_exam_passers(rating:float, docId:uuid4 = None):
    exams = list_passing_scores(rating)
    response = make_response(
      render_template('exam/list_exam_passers.html',
           exams=exams, docId=docId), 200)
    return response

As we can see, path variables are identified with data types inside the diamond operator (<>) using the <type:variable> pattern. These parameters are set to None if the path variables are optional. The path variable is considered a string type by default if it has no associated type hint. Flask 3.x offers these built-in data types for path variables:

  • string: Allows all valid characters except for slashes.
  • int: Takes integer values.
  • float: Accepts real numbers.
  • uuid: Takes unique 32 hexadecimal digits that are used to identify or represent records, documents, hardware gadgets, software licenses, and other information.
  • path: Fetches characters, including slashes.

These path variables can’t function without the corresponding parameters of the same name and type declared in the view function’s parameter list. In the previous report_exam_passers() view function, the local rating and docId parameters are the variables that will hold the values of the path variables, respectively.

But there are particular or rare cases where path variables should be of a type different than the supported ones. View functions with path variables declared as list, set, date, or time will throw Status Code 500 in Flask. As a workaround, the Werkzeug bundle of libraries offers a BaseConverter utility class that can help customize a variable type for paths that allows other types to be part of the type hints. The following view function requires a date type hint to generate a certificate in HTML format:

@app.route('/certificate/accomp/<string:name>/  <string:course>/<date:accomplished_date>')
def show_certification(name:str, course:str, accomplished_date:date):
    certificate = """<html><head>
          <title>Certificate of Accomplishment</title>
         </head><body>
           <h1>Certificate of Accomplishment</h1>
           <p>The participant {} is, hereby awarded this certificate of accomplishment, in {} course on {} date for passing all exams. He/she proved to be ready for any of his/her future endeavors.</em>
         </body></html>
    """.format(name, course, accomplished_date)
    return certificate, 200

accomplished_date in show_certification() is a date hint type and will not be valid until the following tasks are implemented:

  • First, subclass BaseConverter from the werkzeug.routing module. In the /converter package of this project, there is a module called date_converter.py that implements our date hint type, as shown in the following code:
    from werkzeug.routing import BaseConverter
    from datetime import datetime
    class DateConverter(BaseConverter):
       def to_python(self, value):
         date_value = datetime.strptime(value, "%Y-%m-%d")
         return date_value

    The given DateConverter will custom-handle date variables within our Flask application.

  • BaseConverter has a to_python() method that must be overridden to implement the necessary conversion process. In the case of DateConverter, we need strptime() so that we can convert the path variable value in the yyyy-mm-dd format into the datetime type.
  • Lastly, declare our new custom converter in the Flask instance of the main.py module. The following snippet registers DateConverter to app:
    app = Flask(__name__)
    app.url_map.converters['date'] = DateConverter

After following all these steps, the custom path variable type – for instance, date – can now be utilized across the application.

Assigning URLs externally

There is also a way to implement a routing mechanism without using the @route decorator, and that’s by utilizing Flask’s add_url_rule() method to register views. This approach binds a valid request handler to a unique URL pattern for every call to add_url_rule() of the app instance in the main.py module, not in the handler’s module scripts, thus making this approach an external way of building routes. The following arguments are needed by the add_url_rule() method to perform mapping:

  • The URL pattern with or without the path variables.
  • The URL name and, usually, the exact name of the view function.
  • The view function itself.

The invocation of this method must be in the main.py file, anywhere after its @route implementations and view imports. The following main.py snippet shows the external route mapping of the show_honor_dismissal() view function to its dynamic URL pattern. This view function generates a termination letter for the counseling and consultation agreement between a clinic and a patient:

app = Flask(__name__)
def show_honor_dissmisal(counselor:str, effective_date:date, patient:str):
    letter = """
       … … … … …
       </head><body>
           <h1> Termination of Consultation </h1>
           <p>From: {}
           <p>Head, Counselor
           <p>Date: {}
           <p>To: {}
           <p>Subject: Termination of consultation
                    <p>Dear {},
                    … … … … … …
                    <p>Yours Sincerely,
                    <p>{}
                </body>
            </html>
    """.format(counselor, effective_date, patient, patient, counselor)
    return letter, 200
app.add_url_rule('/certificate/terminate/<string:counselor>/<date:effective_date>/<string:patient>', 'show_honor_dissmisal', views.certificates.show_honor_dissmisal)

Binding URL mappings to views using add_url_rule() is not only confined to the decorated function views but is also necessary for class-based views.

Implementing class-based views

Another way to create the view layer is through Flask’s class-based view approach. Unlike the Django framework, which uses mixin programming to implement its class-based views, Flask provides two API classes, namely View and MethodView, that can directly subclass any custom view implementations.

The most common and generic class to implement HTTP GET operations is the View class from the flask.views module. It has a dispatch_request() method that executes the request-response transactions like a typical view function. Thus, subclasses must override this core method to implement their view transactions. The following class, ListUnpaidContractView, renders a list of patients with payments due to the clinic:

from flask.views import View
class ListUnpaidContractView(View):
    def dispatch_request(self):
        contracts = select_all_unpaid_patient()
        return render_template("contract/ list_patient_contract.html", contracts=contracts)

select_all_unpaid_patient() will provide the patient records from the database. All these records will be rendered to the list_patient_contract.html template. Now, aside from overriding the dispatch_request() method, ListUnpaidContractView also inherits all the attributes and helper methods from the View class, including the as_view() static method, which creates a view name for the view. During view registration, this view name will serve as the view_func name of the custom View class in the add_url_rule() method with its mapped URL pattern. The following main.py snippet shows how to register ListUnpaidContractView:

app.add_url_rule('/contract/unpaid/patients', view_func=ListUnpaidContractView.as_view('list-unpaid-view'))

If a View subclass needs an HTTP POST transaction, it has a built-class class attribute called methods that accepts a list of HTTP methods the class needs to support. Without it, the default is the [ "GET" ] value. Here is another custom View class of our Online Personal Counselling System app that deletes existing patient contracts of the clinic:

class DeleteContractByPIDView(View):
    methods = ['GET', 'POST']
    … … … … … …
    def dispatch_request(self):
       if request.method == "GET":
          pids = list_pid()
          return render_template("contract/ delete_patient_contract.html", pids=pids)
       else:
          pid = int(request.form['pid'])
          result = delete_patient_contract_pid(pid)
          if result == False:
               pids = list_pid()
               return render_template("contract/ delete_patient_contract.html", pids=pids)
          contracts = select_all_patient_contract()
          return render_template("contract/ list_patient_contract.html", contracts=contracts)

DeleteContractByPIDView handles a typical form-handling transaction, which has both a GET operation for loading the form page and a POST operation to manage the submitted form data. The POST operation will verify if the patient ID submitted by the form page exists, and it will eventually delete the contract(s) of the patient using the patient ID and render an updated list of contracts.

Other than the View class, an alternative API that can also build view transactions is the MethodView class. This class is suitable for web forms since it has the built-in GET and POST hints or templates that subclasses need to define but without the need to identify the GET transactions from POST, like in a view function. Here is a view that uses MethodView to manage the contracts of the patients in the clinic:

from flask.views import MethodView
class ContractView(MethodView):
    … … … … … …
    def get(self):
        return render_template("contract/ add_patient_contract.html")
    def post(self):
        pid = request.form['pid']
        approver = request.form['approver']
        … … … … … …
        result = insert_patient_contract(pid=int(pid), approved_by=approver, approved_date=approved_date, hcp=hcp, payment_mode=payment_mode, amount_paid=float(amount_paid), amount_due=float(amount_due))
        if result == False:
          return render_template("contract/ add_patient_contract.html")
        contracts = select_all_patient_contract()
        return render_template("contract/ list_patient_contract.html", contracts=contracts)

The MethodView class does not have a methods class variable to indicate the HTTP methods supported by the view. Instead, the subclass can select the appropriate HTTP hints from MethodView, which will then implement the required HTTP transactions of the custom view class.

Since MethodView is a subclass of the View class, it also has an as_view() class method that creates a view_func name of the view. This is also necessary for add_url_rule() registration.

Aside from GET and POST, the MethodView class also provides the PUT, PATCH, and DELETE method hints for API-based applications. MethodView is better than the View API because it organizes the transactions according to HTTP methods and checks and executes these HTTP methods by itself at runtime. In general, between the decorated view function and the class-based ones, the latter approach provides a complete Flask view component because of the attributes and built-in methods inherited by the view implementation from these API classes. Although the decorated view function can support a flexible and open-ended strategy for scalable applications, it cannot provide an organized base functionality that can supply baseline view features to other related views, unlike in a class-based approach. However, the choice still depends on the scope and requirements of the application.

Now that we’ve created and registered the routes, let’s scrutinize these view implementations and identify the essential Flask components that compose them.

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.

Implementing view templates

Jinja2 is the default templating engine of the Flask framework and is used to create HTML, XML, LaTeX, and markup documents. It is a simple, extensive, fast, and easy-to-use templating approach with powerful features such as layout capabilities, built-in programming constructs, support for asynchronous operations, context data filtering, and utility for unit testing.

Firstly, Flask requires all template files to be in the templates directory of the main project. To change this setting, the Flask() constructor has a template_folder parameter that can set and replace the default directory with another one. Our prototype, for instance, has the following Flask instantiation that overrides the default templates directory with a more high-level directory name:

from flask import Flask
app = Flask(__name__, template_folder='pages')

In our given setup, the view functions always refer to the pages directory when calling the template files through the render_template() method.

When it comes to syntax, Jinja2 has a placeholder ({{ }}) that renders dynamic content passed by the view functions to its template file. It also has a Jinja block ({% %}) that supports control structures such as loops, conditional statements, macros, and template inheritance. In the previous route function, assign_exam(), the GET transaction retrieves a list of counselor IDs (cids) and patient IDs (pids) from the database and passes them to the assign_exam_form.html template found in the exam subfolder of the pages directory. The following snippet shows the implementation of the assign_exam_form.html view template:

<!DOCTYPE html>
<html lang="en"><head><title>Patient's Score Form</title>
    </head><body>
        <form action="/exam/score" method="POST">
           <h3>Exam Score</h3>
           <label for="qid">Enter Questionnaire ID:</label>
           <select name="qid">
              {% for id in qids %}
                <option value="{{ id }}">{{ id }}</option>
              {% endfor %}
           </select><br/>
           <label for="pid">Enter patient ID:</label>
           <select name="pid">
              {% for id in pids %}
                <option value="{{ id }}">{{ id }}</option>
              {% endfor %}
           </select><br/>
           … … … … … …
           <input type="submit" value="Assign Exam"/>
        </form></body>
</html>

This template uses the Jinja block to iterate all the IDs and embed each in the <option> tag of the <select> component with the placeholder operator.

More about Jinja2 and Flask 3.x will be covered in Chapter 2, but for now, let’s delve into how Flask can implement the most common type of web-based transaction – that is, by capturing form data from the client.

Creating web forms

In Flask, we can choose from the following two approaches when implementing view functions for form data processing:

  • Creating two separate routes, one for the GET operation and the other for the POST transaction, as shown for the following user signup transaction:
    @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
    @app.route('/signup/submit', methods= ['POST'])
    def signup_users_submit():
        username = request.form['username']
        password = request.form['password']
        user_type = request.form['utype']
        firstname = request.form['firstname']
        lastname = request.form['lastname']
        cid = request.form['cid']
        insert_signup(user=username, passw=password, utype=user_type, fname=firstname, lname=lastname, cid=cid)
        return render_template('add_signup_submit.html', message='Added new user!'), 200
  • Utilizing only one view function for both the GET and POST transactions, as shown in the previous signup_approve() route and in the following assign_exam() view:
    @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'])
           … … … … … …
           duration = int(request.form['duration'])
           result = insert_question_details(id=id, cid=cid, pid=pid, exam_date=exam_date, duration=duration)
           if result:
               exam_token = request.cookies.get('exam_token')
               return redirect(url_for('introduce_exam', message=str(exam_token)))
           else:
               return redirect('/error')

Compared to the first, the second approach needs request.method to separate GET from the POST transaction.

In setting up the form template, binding context data to the form components through render_template() is a fast way to provide the form with parameters with default values. The form model must derive the names of its attributes from the form parameters to establish a successful mapping, such as in the signup_approve() route. When it comes to retrieving the form data, the request proxy has a form dictionary object that can store form parameters and their data while its get_data() function can access the entire query string in byte stream type. After a successful POST transaction, the view function can use render_template() to load a success page or go back to the form page. It may also apply redirection to bring the client to another view.

But what happens to the form data after form submission? Usually, form parameter values are rendered as request attributes, stored as values of the session scope, or saved into a data store using a data persistency mechanism. Let’s explore how Flask can manage data from user requests using a relational database such as PostgreSQL.

Building the data layer with PostgreSQL

PostgreSQL is an object-relational database system, and Flask can utilize it as a data storage platform if the activated virtual environment has the psycopg2-binary extension module. To install this extension module into the venv, run the following command:

pip install psycopg2-binary

Now, we can write an approach to establish a connection to the PostgreSQL database.

Setting up database connectivity

There are multiple ways to create a connection to a database, but this chapter will showcase a Pythonic way to extract that connection using a custom decorator. In the project’s /config directory, there is a connect_db decorator that uses psycopgy2.connect() to establish connectivity to the opcs database of our prototype. Here is the implementation of this custom decorator:

import psycopg2
import functools
from os import environ
def connect_db(func):
    @functools.wraps(func)
    def repo_function(*args, **kwargs):
        conn = psycopg2.connect(
            host=environ.get('DB_HOST'),
            database=environ.get('DB_NAME'),
            port=environ.get('DB_PORT'),
            user = environ.get('DB_USER'),
            password = environ.get('DB_PASS'))
        resp = func(conn, *args, **kwargs)
        conn.commit()
        conn.close()
        return resp
    return repo_function

The given decorator provides the connection instance, conn, to a repository function and commits all the changes to the database after a transaction’s successful execution. Also, it will close the database connection at the end of the process. All the database details, such as DB_HOST, DB_NAME, and DB_PORT, are stored as environment variables inside a .env file. To retrieve them using the environ dictionary of the os module, run the following command to install the required extension:

pip install python-dotenv

However, there are other ways to manage these custom and built-in configuration variables instead of storing them as .env variables. The next topic will expound on this, but first, let’s apply @connect_db to our repository layer.

Implementing the repository layer

The following insert_signup() transaction adds a new user signup record to the database. It gets the conn instance from the @connect_db decorator. Our application has no object-relational mapper yet and solely depends on the psycopg2 driver to perform the CRUD operation. The cursor instance created by conn executes the INSERT statement of the following transaction with form data provided by its view function:

from config.db import connect_db
from typing import Dict, Any, List
@connect_db
def insert_signup(conn, user:str, passw:str, utype:str, fname:str, lname:str, cid:str) -> bool:
    try:
        cur = conn.cursor()
        sql = 'INSERT INTO signup (username, password, user_type, firstname, lastname, cid) VALUES (%s, %s, %s, %s, %s, %s)'
        values = (user, passw, utype, fname, lname, cid)
        cur.execute(sql, values)
        cur.close()
        return True
    except Exception as e:
        cur.close()
        print(e)
    return False

cursor is an object derived from conn that uses a database session to perform insert, update, delete, and fetch operations. So, just like insert_signup(), the following transaction uses cursor again to execute the UPDATE statement:

@connect_db
def update_signup(conn, id:int, details:Dict[str, Any]) -> bool:
    try:
        cur = conn.cursor()
        params = ['{} = %s'.format(key) for key in details.keys()]
        values = tuple(details.values())
        sql = 'UPDATE signup SET {} where id = {}'.format(', '.join(params), id);
        cur.execute(sql, values)
        cur.close()
        return True
    except Exception as e:
        cur.close()
        print(e)
    return False

To complete the CRUD operations for the signup table, here is the DELETE transaction from our application:

@connect_db
def delete_signup(conn, id) -> bool:
    try:
        cur = conn.cursor()
        sql = 'DELETE FROM signup WHERE id = %s'
        values = (id, )
        cur.execute(sql, values)
        cur.close()
        return True
    except Exception as e:
        cur.close()
        print(e)
    return False

The use of an ORM to build the model layer will be part of Chapter 2’s discussions. For now, the views and services of our application rely on a repository layer that manages PostgreSQL data directly through the psycopg2 driver.

After creating the repository layer, many applications can build a service layer to provide loose coupling between the CRUD operations and the views.

Creating the service layer

The service layer of the application builds the business logic of the view functions and the repository. Instead of loading the view functions with transaction-related and business processes, we place all these implementations in the service layer by creating lists of all the counselor and patient IDs, validating where to persist the newly approved user, and creating a list of patients who excelled in the examinations. The following service function evaluates and records patients’ exam scores:

def record_patient_exam(formdata:Dict[str, Any]) -> bool:
    try:
        pct = round((formdata['score']formdata['total']) * 100, 2)
        status = None
        if (pct >= 70):
            status = 'passed'
        elif (pct < 70) and (pct >= 55):
            status = 'conditional'
        else:
            status = 'failed'
        insert_patient_score(pid=formdata['pid'], qid=formdata['qid'], score=formdata['score'], total=formdata['total'], status=status, percentage=pct)
        return True
    except Exception as e:
        print(e)
    return False

Instead of directly accessing insert_patient_score() to save patient exam scores, record_score() accesses the record_patient_exam() service to compute some formulas before invoking insert_patient_score() from the repository layer for record insertion. The service lessens some friction between the database transactions and the view layer. The following snippet is the view function that accesses the record_patient_exam() service for record exam record insertion:

@app.route('/exam/score', methods=['GET', 'POST'])
def record_score():
    if request.method == 'GET':
        pids = list_pid()
        qids = list_qid()
        return render_template( 'exam/add_patient_score_form.html', pids=pids, qids=qids), 200
    else:
        params = dict()
        params['pid'] = int(request.form['pid'])
        params['qid'] = int(request.form['qid'])
        params['score'] = float(request.form['score'])
        params['total'] = float(request.form['total'])
        result = record_patient_exam(params)
        … … … … … … …
        else:
            return redirect('/exam/task/error')

Aside from calling record_patient_exam(), it also utilizes the list_pid() and list_qid() services to retrieve the IDs. The use of services can help separate the abstraction and use cases from the route functions, which has a beneficial impact on the scope, clean coding, and runtime performance of the routes. Moreover, the project structure can also contribute to clear business flow, maintainability, flexibility, and adaptability.

Managing the project structure

Flask provides developers with the convenience of building their desired project structure. It is open to any design patterns and architectural strategies for building a project directory because of its Pythonic characteristics. The focus of this discussion revolves around setting up our Online Personal Counseling System application using the simple and single-structured project approach while highlighting the different configuration variable setups.

Building the directory structure

The first aspect to consider in building the project structure is the level of complexity of the project scope. Since our project focuses only on small-scale clientele, a typical single-structured approach is enough to cater to a less scalable application. Second, we must ensure the proper layering or breakdown of various project components from the view layer down to the test modules so that the developers can identify what parts to prioritize, maintain, bug-fix, and test. The following is a screenshot of the directory structure of our prototype:

Figure 1.4 – The single-structured project directory

Figure 1.4 – The single-structured project directory

Chapter 2 will discuss other project structure techniques, especially when applications are scalable and complex.

Setting up a development environment

A Flask application, by default, is production-ready, even though its server, the Werkzeug’s built-in server, is not. We need to replace it with an enterprise-grade server to be fully ready for production setup. However, our goal is to set up a Flask project with a development environment that we can sample and experiment on with various features and test cases. There are three ways to set up a Flask 3.x project for development and testing purposes:

  • Running the server with app.run(debug=True) in main.py.
  • Setting the FLASK_DEBUG and TESTING built-in configuration variables to true in the configuration file.
  • Running the application with the flask run --debug command.

Setting the development environment will also enable automatic reloading and the default debugger of the framework. However, turn off debugging mode after deploying the application to production to avoid security risks for the applications and software logging problems. The following screenshot shows the server log when running a Flask project with a development environment setup:

Figure 1.5 – The server log of Flask’s built-in server

Figure 1.5 – The server log of Flask’s built-in server

Figure 1.5 shows that debug mode is set to ON and that the debugger is enabled and given a PIN value.

Implementing the main.py module

When creating a simple project like our specimen, the main module usually contains the Flask instantiation and some of its parameters (for example, template_folder for the new directory of the HTML templates) and the required imports of the views below it. The following is the complete code of our main.py file:

from flask import Flask
from converter.date_converter import DateConverter
app = Flask(__name__, template_folder='pages')
app.url_map.converters['date'] = DateConverter
@app.route('/', methods = ['GET'])
def index():
    return "This is an online … counseling system (OPCS)"
import views.index
import views.certificates
import views.signup
import views.examination
import views.reports
import views.admin
import views.login
import views.profile
app.add_url_rule('/certificate/terminate/<string:counselor>/<date:effective_date>/<string:patient>', 'show_honor_dissmisal', views.certificates.show_honor_dissmisal)
if __name__ == '__main__':
    app.run()

The imports to the views are placed below the Flask instantiation to avoid circular dependency problems. In this type of project structure, conflict always happens when a view module imports the app instance of the main module while the main module has the imports to the views declared at the beginning. This occurrence is called a circular dependency between two modules importing components from each other, which leads to some circular import issues. To avoid this problem with the main and view modules, the area below the Flask instantiation is where we place these view imports. The if statement at the bottom of main.py, on the other hand, verifies that only the main module can run the Flask server through the app.run() command.

The main module usually sets the configuration settings through its app instance to build the sessions and other context-based objects or integrate other custom components, such as the security and database modules. But the ideal setup doesn’t recommend including them there; instead, you should place them separately from the code, say using a configuration file, to seamlessly manage the environment variables when configuration blunders arise, to avoid performance degradation or congestion when the Flask app instance has several variables to load at server startup, and to replicate and back up the environment settings with less effort during project migration or replication.

Creating environment variables

Configuration variables will always be part of any project setup, and how the frameworks or platforms manage them gives an impression of the kind of framework they are. A good framework should be able to decouple both built-in and custom configuration variables from the implementation area while maintaining their easy access across the application. It can support having a configuration file that can do the following:

  • Contain the variables in a structured and readable manner.
  • Easily integrate with the application.
  • Allow comments to be part of its content.
  • Work even when deployed to other servers or containers.
  • Decouple the variables from the implementation area.

Aside from the .env file, Flask can also support configuration files in JSON, Python, and Tom’s Obvious Minimal Language (TOML) format. Flask will not require an extension module if configuration files are in JSON and Python formats. The following is the application’s config.json file, which contains the database and Flask development environment settings:

{
    «DB_USER» : «postgres»,
    «DB_PASS» : «admin2255»,
    «DB_PORT» : 5433,
    "DB_HOST" : "localhost",
    "DB_NAME" : "opcs",
    "FLASK_DEBUG" : true,
    "TESTING": true
}

This next is a Python config.py file with the same variable settings in config.json:

DB_USER = «postgres»
DB_PASS = «admin2255»
DB_PORT = 5433
DB_HOST = "localhost"
DB_NAME = "opcs"
FLASK_DEBUG = True
TESTING = True

The app instance has the config attribute with a from_file() method that can load the JSON file, as shown in the following snippet:

app.config.from_file("config.json", load=json.load)

On the other hand, config has a from_pyfile() method that can manage the Python config file when invoked, as shown in this snippet:

app.config.from_pyfile('myconfig.py')

The recent addition to the supported type, TOML, requires Flask to install the toml extension module before loading the .toml file into the platform. After running the pip install toml command, the config attribute’s from_file() method can now load the following settings of the config.toml file:

DB_USER = «postgres»
DB_PASS = «admin2255»
DB_PORT = 5433
DB_HOST = "localhost"
DB_NAME = "opcs"
FLASK_DEBUG = true
TESTING = true

TOML, like JSON and Python, has data types. It supports arrays and tables and has structural patterns that may seem more complex than the JSON and Python configuration syntax. A TOML file will have the .toml extension.

When accessing variables from these file types, the Flask instance uses its config object to access each variable. This can be seen in the following version of our db.py module for database connectivity, which uses the config.toml file:

from __main__ import app
import psycopg2
import functools
def connect_db(func):
    @functools.wraps(func)
    def repo_function(*args, **kwargs):
        conn = psycopg2.connect(
            host=app.config['DB_HOST'],
            database=app.config['DB_NAME'],
            port=app.config['DB_PORT'],
            user=app.config['DB_USER'],
            password=app.config['DB_PASS'])
        resp = func(conn, *args, **kwargs)
        conn.commit()
        conn.close()
        return resp
    return repo_function

Summary

This chapter has presented the initial requirements to set up a development environment for a single-structured Flask project. It provided the basic elements that are essential to creating a simple Flask prototype, such as the main.py module, routes, database connectivity, repository, services, and configuration files. The nuts and bolts of every procedure in building every aspect of the project describe Flask as a web framework. The many ways to store the configuration settings, the possibility of using custom decorators for database connectivity, and the many options to capture the form data are indicators of Flask being so flexible, extensible, handy, and Pythonic in many ways. The next chapter will focus on the core components and advanced features that Flask can provide in building a more scalable application.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Implement web and API applications using both standard and asynchronous Flask components
  • Improve your dev experience with signals, route decorators, async/await design patterns, context managers, and nested blueprints
  • Tie all the features together in each chapter through practical, relatable applications
  • Purchase of the print or Kindle book includes a free PDF eBook

Description

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.

Who is this book for?

This book is for proficient Python developers seeking a deeper understanding of the Flask framework as a solution for tackling enterprise challenges. It is also a great resource for Flask-savvy readers eager to learn more about the framework’s advanced capabilities and new features.

What you will learn

  • Prepare, set up, and configure development environments for both API and web applications
  • Explore built-in serializers and encoders that processes request and response data
  • Solve big data issues by integrating Flask applications with NoSQL databases
  • Apply various ORM and ODM techniques to build model and repository layers
  • Integrate with OpenAPI, Circuit Breaker, ZooKeeper, and OpenTracing to build scalable API applications
  • Use Flask middleware to provide CRUD transactions for Flutter-based mobile applications

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Aug 16, 2024
Length: 494 pages
Edition : 1st
Language : English
ISBN-13 : 9781837638574
Languages :
Tools :

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
OR
Modal Close icon
Payment Processing...
tick Completed

Billing Address

Product Details

Publication date : Aug 16, 2024
Length: 494 pages
Edition : 1st
Language : English
ISBN-13 : 9781837638574
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
₹800 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
₹4500 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just ₹400 each
Feature tick icon Exclusive print discounts
₹5000 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just ₹400 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 6,359.97 9,086.97 2,727.00 saved
FastAPI Cookbook
₹2345.99 ₹3351.99
Mastering Flask Web and API Development
₹2084.99 ₹2978.99
Full-Stack Web Development with TypeScript 5
₹1928.99 ₹2755.99
Total 6,359.97 9,086.97 2,727.00 saved Stars icon
Banner background image

Table of Contents

17 Chapters
Part 1:Learning the Flask 3.x Framework Chevron down icon Chevron up icon
Chapter 1: A Deep Dive into the Flask Framework Chevron down icon Chevron up icon
Chapter 2: Adding Advanced Core Features Chevron down icon Chevron up icon
Chapter 3: Creating REST Web Services Chevron down icon Chevron up icon
Chapter 4: Utilizing Flask Extensions Chevron down icon Chevron up icon
Part 2:Building Advanced Flask 3.x Applications Chevron down icon Chevron up icon
Chapter 5: Building Asynchronous Transactions Chevron down icon Chevron up icon
Chapter 6: Developing Computational and Scientific Applications Chevron down icon Chevron up icon
Chapter 7: Using Non-Relational Data Storage Chevron down icon Chevron up icon
Chapter 8: Building Workflows with Flask Chevron down icon Chevron up icon
Chapter 9: Securing Flask Applications Chevron down icon Chevron up icon
Part 3:Testing, Deploying, and Building Enterprise-Grade Applications Chevron down icon Chevron up icon
Chapter 10: Creating Test Cases for Flask Chevron down icon Chevron up icon
Chapter 11: Deploying Flask Applications Chevron down icon Chevron up icon
Chapter 12: Integrating Flask with Other Tools and Frameworks Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Full star icon Full star icon 5
(2 Ratings)
5 star 100%
4 star 0%
3 star 0%
2 star 0%
1 star 0%
M. Aug 29, 2024
Full star icon Full star icon Full star icon Full star icon Full star icon 5
PROS:Contains a very keen attention to detail while working through complex projectsOffers actionable examples and infographics to jump-start engagement with the contentFocuses on real-world, production-quality applications while highlighting what Flask has to offer.Delivers enough content to offset the price tag only halfway through reading the book!CONS:**This book is not an introductory Python or Flask guide; consider spending your money elsewhere.**Presents a sharp learning curve to develop applicable skills, even with years of prior experience.It eats up a hefty chunk of storage and RAM to install and run the required software (See image).
Amazon Verified review Amazon
Butchy Brannan Sep 03, 2024
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This book offers an in-depth exploration of Flask with a keen focus on technical detail and real-world applications, making it invaluable for experienced developers. Its actionable examples and visual aids enhance learning, though its complexity might overwhelm beginners, suggesting it's best suited for those already familiar with Python and web development concepts.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.