You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Kerem Yollu ce6b498a7f
Starting video 4
1 year ago
..
.old Starting video 4 1 year ago
downloads following new training for folask 1 year ago
templates Starting video 4 1 year ago
README.md Starting video 4 1 year ago
app.py Starting video 4 1 year ago
file.txt Starting video 4 1 year ago

README.md

Flask_from_zero_to_docker

Pre-requirements

Create a Python virtual enviroement

You can create a virtual enviroment using the venv command that comes with Python. this will create a virtual enviroment in the specified .venv directory. I like to make it hidden so that it doesn't show up during developpment.

python3 -m venv .venv

Activate the virtual enviroement

Then you need to activate the virtual enviroment.

  • for Bash
source .venv/bin/activate
  • for Fish
. .venv/bin/activate.fish

If executed corectly your (.venv) should apear on your terminal

(.venv) key@devbian ~/g/m/flask_to_docker >

Install libraries and make them exportable

Once this is done you can check the installed libraries using pip.

pip3 freeze

This should return empty as we didn't installed anything yet. So let's proceed and install Flaks

pip3 install flask

Let's chech once again with pip freeze.

pip3 freeze

This time you shuld get a list of installed libraries.

(.venv) key@devbian ~/g/m/flask_to_docker > pip3 freeze
    blinker==1.8.2
    click==8.1.7
    Flask==3.0.3
    itsdangerous==2.2.0
    Jinja2==3.1.4
    MarkupSafe==2.1.5
    Werkzeug==3.0.3

You can also expoirt the required libraries to a text file.

pip3 freeze > requirements.txt

This file can afterward be usted to automatically install all the libraries used on this porject.

pip install -r requirements.txt

This make it very easy to add .venv to your git ingnore list and for someone colning your project to easely install pip libraries after cloning your porject.


Default application YouTube

The main funtion to start a Flask Server

This is slef explanatory but each Flask application start the same way. Let's create an app.py and start with the following steps.

  • Import Flask
  • We declare the app()
  • Add decoration for routes @app.route('/')
  • Define a function under the decoration index():
  • Run the app wiht app.run()
from flask import Flask # Importing flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Hello World"
    
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

In the run() funtion there are some main parameters that needs to be defined these correspond to:

  • host (str* | *None) the hostname to listen on. Set this to 0.0.0.0 to have the server available externally as well. Defaults to 127.0.0.1 (AKA localhost) or the host in the SERVER_NAME config variable if present.

  • port (int* | *None) the port of the webserver. Defaults to 5000 or the port defined in the SERVER_NAME config variable if present.

  • debug (bool* | *None) if given, enable or disable debug mode. See debug.


First Run

The firsat run is simple enought. Just execute your app.py. Just make sure that your venv is active.

(.venv) key@devbian ~/g/m/flask_to_docker > python3 app.py

you should be greeted wiht :

 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://192.168.1.128:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 633-588-052

If this is tha case open http://localhost:5000/ on wour browser and you should be greeted wiht your hello wolrd message.

Congratulations you have a running Flask Application.


Flask URL's & Route YouTube

These are Some code snippets wiht examples please do not forget tha the start annd the end is the same as the Default Application described just before.

Main Route

Hereyou can see as before the main route (/)

@app.route('/')
def index():
    return "Welcome"

URL proccessors

We can also use url proccessors to get something that we have typed on the url. In this case the url to visit would be http://localhost:5000/greet/kerem

@app.route('/greet/<name>')
def greet(name):
    return f"Hello {name}"

Variables given to the url can also be typed here as integers. This allows us to do some advanced operations. In this case the url to visit would be http://localhost:5000/add/20/10

@app.route('/add/<int:number1>/<int:number2>')
def add(number1, number2):
    return f'{number1} + {number2} = {number1+number2}'

Responses

Now to do generate some responses. Here you can use curl so see exactly what is happening

#We can also return some responses (like 404 page not found and co)
@app.route('/hello')
def hello():
    response = make_response('Hello World')
    response.status_code = 202
    response.headers['content-type'] = 'application/octet-stream'
    return response

So if we were to use curl we would gete here the following response.http://127.0.0.1:5000/hello is the same as http://localhost:5000/hello

key@devbian ~> curl -I http://127.0.0.1:5000/hello
HTTP/1.1 202 ACCEPTED
Server: Werkzeug/3.0.3 Python/3.11.2
Date: Wed, 26 Jun 2024 07:06:06 GMT
content-type: application/octet-stream
Content-Length: 11
Connection: close

Methods

This is the way to allow or block some Methods.

@app.route('/test_methodes', methods=['GET', 'POST'])
def test_methodes():
    if request.method == 'GET':
        return f"You made a GET request\n"
    if request.method == 'POST':
        return f"You made a POST request\n"
    else:
        return f"You will never see this message\n"

Parameters

You have surely seen some Parametes embedded in liks like ?user=bgfdrt56+pass=Rtrssdf. Flask alows some asy maniputation of those parameters. To test this : http://localhost:5000/handle_url_params?greeting=hello&name=kerem. If there was a mistmatch you would get the Error message instead.

#Handle parameters :
@app.route('/handle_url_params')
def handle_url_params():
    if 'greeting' in request.args.keys() and 'name' in request.args.keys():
        greeting = request.args['greeting']
        name = request.args.get('name')
        return f'{greeting}, {name}'
    else:
        return f'some params are missing'

Flask HTML files And TEMPLATES YouTube

Declare that we use Templates

To indicate that we will use templates first we need to create a directory containing those tmeplates and second we need to reference it in our code. let's create the directory

key@devbian ~/g/m/flask_to_docker > mkdir templates
key@devbian ~/g/m/flask_to_docker > cd templates
key@devbian ~/g/m/flask_to_docker > touch index.html

The sturcture should look like this. We can use Tree to find this out

key@devbian ~/g/m/flask_to_docker > tree
.
├── app.py
└── templates
    └── index.html

Let's declare it in our code. template_folder take the source of app.py as starting route for directories.

app = Flask(__name__, template_folder='templates')

Using Templates

The render_template() let's use use tem when returning the content of the page.

@app.route('/')
def index():
    myval = 'Kynsight'
    myres = 30
    mylist = [10,20,30,40,50]
    return render_template('index.html', value=myval, result=myres, list=mylist)

Now this allows us to have a huge flexibility that HTML doesn't otherwise allow. This is thnaks to Jinja. Let's see how it works.

HTML & Jinja

Now let's have a look to index.html herre we can now se some Jinja templates funtionlaities.

<!DOCTYPE html>
<html lang="en">

<head charset="UTF-8">
    <title>Flask APP</title>
</head>

<body>
    <h1>My First Heading</h1>
    <p>My value: {{ value }}</p>
    <p>My result: {{ result }}</p>
    <ul>
        {% for item in list %}
            {% if item == 30 %}
                <li style="color: red">{{ item }}</li>
            {% else %}
                <li {%if item == 20 %} style="color: blue" {%endif%} >{{ item }}</li>
            {% endif %}
        {% endfor %}
    </ul>
</body>
</html>

Jinja Allows us to use these kind of functions : {% for item in list %} that are ususaly inpossible wiht HTML. All Jinja Functions starts with {% and ends with %}.

  • They can also be used inline like: <li {%if item == 20 %} style="color: blue" {%endif%} >{{ item }}</li>.

Extend Templates

We cna extend templates to other templates. What does this bring us ? Let's say we want ta have the same header on multiple pages we can use a template with the header and only add an content BLOCK onto this template.
In templeates directory we can now create a base html file let's call it base.htm and two pages that extends on it index.html and other.html.

key@devbian ~/g/m/flask_to_docker > cd templates
key@devbian ~/g/m/flask_to_docker > touch base.html other.html

the structure should look like this

key@devbian ~/g/m/flask_to_docker > tree
.
├── app.py
└── templates
    ├── base.html
    ├── index.html
    └── other.html

base.html

<!DOCTYPE html>
<html lang="en">

<head charset="UTF-8">
    <title>Flask APP</title>
</head>

<body>
    <h1>My First Heading</h1>
    <p>My value: {{ value }}</p>
    <p>My result: {{ result }}</p>
    <ul>
        {% for item in list %}
            {% if item == 30 %}
                <li style="color: red">{{ item }}</li>
            {% else %}
                <li {%if item == 20 %} style="color: blue" {%endif%} >{{ item }}</li>
            {% endif %}
        {% endfor %}
    </ul>
</body>
</html>

index.html Here you can see that we are extending frim base.html with {% extends "base.html" %}.

  • we can indicate a title using the block that we calle title.
  • we can fill the content using the block that we calle content.
{% extends "base.html" %}

{% block title %}Index Page{% endblock %}

{% block content %}
    <h1>My First Heading</h1>
    <p>My value: {{ value }}</p>
    <p>My result: {{ result }}</p>
    <ul>
        {% for item in list %}
            {% if item == 30 %}
                <li style="color: red">{{ item }}</li>
            {% else %}
                <li {%if item == 20 %} style="color: blue" {%endif%} >{{ item }}</li>
            {% endif %}
        {% endfor %}
    </ul>
{% endblock %}

other.html all the same rules applies here too.

{% extends "base.html" %}

{% block title %}Other Page{% endblock %}

{% block content %}
    <h1>Other</h1>
{% endblock %}

but here we need to add a route to this page at app.py

@app.route('/other')
def other():
    return render_template('other.html')

Jinja Filters

Filters that python uses lin .uppercase can't be used in Jinja. There are some filters taht already exists but most importnatly we can create our own filters.

Here is an filter that alredy exitst which mase everything uppercase. But every filter needs to be piped trhpug Jinja thus the|.

<p> {{ sometext|upper }}</p>

Let's create a exmaple file for filters. Create filter.html in templates, and add a custom Filter namde reverse_string

filter.html:

{% extends "base.html" %}

{% block title %}Filter Page{% endblock %}

{% block content %}
    <h1>Filter</h1>
    <p> {{ sometext }}</p>
    <p> {{ sometext|upper }}</p>
    <p> {{ sometext|lower }}</p>
    <p> {{ sometext|replace('l', 'L') }}</p>
    <p> {{ sometext|reverse_string }}</p>
{% endblock %}

We of course need to define reverse_string in app.py as follows :

@app.template_filter('reverse_string')
def reverse_string(s):
    return s[::-1]

you can see that we use a speciel decorator named template_filter to declare our custom filters.


Redirection

Redirections can be tricky but thankfully Flask has a way od dynamic redirection wich takes a function as argument

@app.route('/redirect_endpoint')
def redirect_endpoint():
        return redirect(url_for('other'))

the other in return redirect(url_for('other')) used above, is redirection to the def other():defined under V:

@app.route('/wewwa')
def other():
    return render_template('other.html')

this mean when we type http://localhost:5000/redirect_endpoint we will be redirected to http://localhost:5000/wewwa with the page defined at other.html


GET & POST

To have some fields is much better ! Here is an example how to handle some GET & POST Methods

app.py

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html')
    elif request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')

        if username == 'kynsight' and password == 'pass':
            return 'Success'
        else:
            return 'Failure'

inted.html

{% extends "base.html" %}

{% block title %}Index Page{% endblock %}

{% block content %}
    <h1>Hellooo</h1>
    <form method="POST" action="{{ url_for('index') }}">
        <input type="text" name="username" placeholder="Username"><br>
        <input type="text" name="password" placeholder="Password"><br>
        <input type="submit" value="login">
    </form>
{% endblock %}

So what is going on here ?

  • First we check in index(): function with if request.method == 'xxx': What Method is being used.
  • If the Methode is GET we just print the page wiht the Form using return render_template('index.html')
  • If the Methode is POST we collect the information filler in form sent by pressing submit
  • We get collect the information by referencing form items by their name attribute : request.form.get('username')
  • Then we can put these infiormation in varibales and use them as we please.

Uploading & downloading files

Be careful here are some notions that are not related to Flask or Jinja. Like Pandas for excel manipulation and os of opperating system functions. Nevertheless this is a very important topic with widely used applications for website.

Uploading a file

Here is the File Upload form located in index.html the most important element to know are :

<h1>File Upload</h1>
    <form method="POST" action="{{ url_for('file_upload') }}" enctype="multipart/form-data">
        <input type="file" name="file" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain" ><br>
        <input type="submit" value="upload File">
    </form>

Wiht the action="{{ url_for('file_upload') }}" we go back to our app.py funtion def file_upload(): where we treat this information. Where we do nothing else then to read the file and print his output.

  • If it's a Text file we put directly it's output
  • If it's a Excel file we read the file using pandas df = pd.read_excel(file) and return the result as html table using return df.to_html()
import pandas as pd

@app.route('/file_upload', methods=['GET', 'POST'])
def file_upload():
    file = request.files.get('file')
    if file.content_type == 'text/plain':
        return file.read().decode()
    elif file.content_type == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' or file.content_type == 'application/vnd.ms-excel':
        df = pd.read_excel(file)
        return df.to_html()

Download a file directly

Here is the File Upload form located in index.html tnothing mroe to know then the previous example:

    <h1>Convert To CSV</h1>
    <form method="POST" action="{{ url_for('convert_csv') }}" enctype="multipart/form-data">
        <input type="file" name="file" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" ><br>
        <input type="submit" value="upload File">
    </form>

Wiht the action="{{ url_for('convert_csv') }}" we go back to our app.py funtion def convert_csv(): where we convert the excel into csv but also directly donwload the ouput. It is a specific example and we don't need to waste too much time on it as there is, in my opignon a much better way to do this.

@app.route('/convert_csv', methods=['GET', 'POST'])
def convert_csv():
    file = request.files.get('file')
    df = pd.read_excel(file)
    response = Response(
            df.to_csv(),
            mimetype = 'text/csv',
            headers={'Content-Disposition': 'attachement; filename=result.csv'}
            )
    return response

Download a file thourgh a directory

Again nothing special to note here, same a the ones before.

    <h1>Convert To CSV Two</h1>
    <form method="POST" action="{{ url_for('convert_csv_two') }}" enctype="multipart/form-data">
        <input type="file" name="file" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" ><br>
        <input type="submit" value="upload File">
    </form>

Wiht the action="{{ url_for('convert_csv_two') }}" we go back to our app.py funtion def convert_csv_two():, convert the file to csv and make it redy for download:

  • os.makedirs('downloads') we create a donwload directory
  • filename = f'{uuid.uuid4()}.cvs' we give the file a unique name
  • df.to_csv(os.path.join('downloads', filename))we convert the file into csv and register in downloads with hisfilename
  • return render_template('download.html', filename=filename) we return the download.html page with filename as argument
@app.route('/convert_csv_two', methods=['GET', 'POST'])
def convert_csv_two():
    file = request.files.get('file')
    df = pd.read_excel(file)

    if not os.path.exists('downloads'):
        os.makedirs('downloads')

    filename = f'{uuid.uuid4()}.cvs'
    df.to_csv(os.path.join('downloads', filename))

    return render_template('download.html', filename=filename)

for that to work we need to create the download.html in templates directory:

  • href="{{ url_for('download', filename=filename) }}"Directs the request to def download(filename): function which generates the download proccess
{% extends "base.html" %}

{% block title %}Download Page{% endblock %}

{% block content %}
<h1>Download {{ filename }}</h1>
    <a href="{{ url_for('download', filename=filename) }}"> Donwload </a>
{% endblock %}

and aso create a new route for download in app.py:

  • @app.route('/download/<filename>' the route has de name route to /download but allso the argument
  • send_from_directory('downloads', filename, download_name='result.csv') alows us to send the file downloads directory and rename it at the same time
@app.route('/download/<filename>', methods=['GET', 'POST'])
def download(filename):
    return send_from_directory('downloads', filename, download_name='result.csv')

JS/json Request for live update

todo if needed. for now i am nto so shure.


Static Files & Integrating Bootstrap