# Python - Flask & MongoDB
### Create Virtual Environment
Command Line
```
python -m venv {name of virtual envrionment}
```
for example
```
python -m venv .env
```
(. will make the folder invisible)
Then install flask using pip install
```
pip install flask
```
After installation, remember to activate the environment
---
### Deployment
example Heroku
```
pip install gunicorn
```
Procfile
web: gunicorn app:app
.flaskenv
FLASK_APP=app.py
FLASK_ENV=dev
---
### Create an app.py file
```python
from flask import Flask
# Create application object
app = Flask(__name__)
# Create route and its corresponding function
@app.route("/")
def index():
return "Hello Flask"
# Run
app.run()
```
Documentation: https://flask.palletsprojects.com/en/2.3.x/tutorial/layout/
---
### Configurate Port
Default port is 5000
```python
app.run(port=3000)
```
---
### Configurate Route
Route is written as a decorator
Basic Configuration
```python
@app.route("/data")
def getData():
return "Here are some data"
```
Dynamica Configuration
```python
@app.route("/user/<username>") # <username> is a variable
def greeting(username):
return f"Hello, {username}!"
```
---
### Static files handling
Statics files: do not execute program to get the requested files
Steps:
1. Create a sub directory named "static"
2. Save any file into the folder
3. Obtain the file by visiting the route: /static/filename
---
### Customized Folder
Customized at the creation of app object
```python
app=Flask(
__name__,
static_folder="xxx",
static_url_path="/xxx"
)
```
for example:
```python
app = Flask(
__name__,
static_folder="static",
static_url_path="/custom"
)
```
Folder name should be changed if the parameter is not "static"
Usually, the following config is used:
```python
static_url_path="/"
```
---
### Request
Basic Request:
```python
from flask import request
@app.route("/")
def index():
print("Get Request Method", request.method)
print("Get Protocal", request.scheme)
print("Get Host Name", request.host)
print("Get Path", request.path)
print("Get URL", request.url)
```
Result:
```
Get Request Method GET
Get Protocal http
Get Host Name 127.0.0.1:5000
Get Path /
Get URL http://127.0.0.1:5000/
```
Headers related:
```python
@app.route("/")
def index():
print("Get User Agent", request.headers.get("user-agent"))
print("Get Language", request.headers.get("accept-language"))
print("Get Referrer", request.headers.get("referrer"))
```
Result:
```
Get User Agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.54
Get Language zh-TW,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Get Referrer None
```
Some Application:
Check Language:
```python
lang = request.headers.get("accept-language")
if lang.startswith("en"):
return "Hello Flask"
else:
return "您好,歡迎光臨"
```
---
### Query String
format: ?x=xxx
Implement a query string to calculate sum:
```python
@app.route("/getSum")
def getSum():
max_number = request.args.get("max", 100) # second parameter is the default value
max_number = int(max_number) # need to cast to int
total = 0
for n in range(1, max_number + 1):
total += n
return f"Sum Result: {total}"
```
URL: http://127.0.0.1:5000/getSum?max=1000
will result in 500500
Enhancement: pass in min and max and sum from min to max
```python
@app.route("/getSum")
def getSum():
min_number = request.args.get("min", 1)
max_number = request.args.get("max", 100)
total = 0
min_number = int(min_number)
max_number = int(max_number)
for n in range(min_number, max_number + 1):
total += n
return f"Result: {total}"
```
URL: http://127.0.0.1:5000/getSum?min=5&max=10
will result in 45
---
### Response & Redirect
Response in string
```python
@app.route("/")
def index():
return "something"
```
Response in JSON string: use json.dump()
```python
@app.route("/")
def index():
return json.dumps(dict)
```
**NOTES:
Languages other than English will be encoded in ASCII, so we have to turn off the default parameter**
```python
@app.route("/")
def index():
return json.dumps(dict, ensure_ascii=False)
```
example:
```python
@app.route("/")
def index():
lang = request.headers.get("accept-language")
if lang.startswith("en"):
return json.dumps({
"status": "ok",
"text": "Hello, world!"
})
else:
return json.dumps({
"status": "ok",
"text": "您好,歡迎光臨"
}, ensure_ascii=False)
```
Redirect: use redirect()
```python
from flask import redirect
@app.route("/")
def index():
return redirect("url")
```
example
```python
@app.route("/")
def index():
return redirect("www.youtube.com")
```
homepage in different language application
```python
@app.route("/")
def index():
lang = request.headers.get("accept-language")
if lang.startswith("en"):
return redirect("/en/")
else:
return redirect("/zh/")
@app.route("/en/")
def index_english():
return json.dumps({
"status": "ok",
"text": "Hello, world"
})
@app.route("/zh/")
def index_chinese():
return json.dumps({
"status": "ok",
"text": "您好,歡迎光臨"
}, ensure_ascii=False)
```
---
### Template Engine
```python
from flask import render_template
@app.route("/")
def index():
return render_template("filename")
```
template must be placed in a folder called "templates"
Purpose: for faster and more complex front-end design
example:
This is a sample from template
```html
<head>
<h3>Hello {{ name }}! Welcome!</h3>
<p>This content is rendered from template engine.</p>
</head>
```
template engine
variable can be passed in as a parameter
```python
@app.route("/")
def index():
return render_template("index", name="John")
```
Result:
```
Hello John! Welcome!
This content is rendered from template engine.
```
---
### Hyperlinks and Images(with html)
Sample
backend
```python
app = Flask(
__name__,
static_folder="images",
static_url_path="/"
)
# Homepage Template
@app.route("/")
def index():
return render_template("index.html")
@app.route("/page")
def page():
return render_template("page.html")
if __name__ == "__main__":
app.run()
```
frontend:
index.html
```htmlembedded
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Hello and Welcome!</title>
</head>
<body>
<h3>This is my first webpage</h3>
<a href="https://css186.github.io/", target="_blank">This is my Portfolio Site</a>
<h3>This is another webpage</h3>
<a href="/page">Page</a>
</body>
</html>
```
page.html
```htmlembedded
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Second</title>
</head>
<body>
<p>The second page!!</p>
<br/>
<img src="/xxx.jpg" />
</body>
</html>
```
---
### Form
this will use the concept of query string
Frontend
```htmlembedded
<form action="url">
<input type="text" name="data" />
<button>submit</button>
</form>
```
Backend
```python
@app.route("url")
def handle():
input = request.args.get("data", "")
return "response"
```
Sample Code
Frontend:
Form
```htmlembedded
<body>
<hr/>
<p>Calculation</p>
<form action="/compute">
min: <input type="text" name="min" />
<br/>
max: <input type="text" name="max" />
<br/>
<button>Press to Compute</button>
</form>
<hr/>
</body>
```
Result
```htmlembedded
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Result</title>
</head>
<body>
<p>The Result is {{data}}</p>
<a href="/">Return to Home</a>
</body>
</html>
```
Backend
```python
@app.route("/compute")
def compute():
min_num = request.args.get("min", "")
max_num = request.args.get("max", "")
total = 0
for i in range(int(min_num), int(max_num) + 1):
total += i
return render_template("result.html", data=total)
```
**the variable "total" will be passed into result.html page as {{data}} and then be presented on the webpage**
---
### Get & Post Method
Get method is the default method
```python
@app.get("/show")
def show():
name = request.args.get("n", None)
mail = request.args.get("m", None)
return f"Your name is {name} and your email is {mail}"
```
```htmlembedded
<body>
<p>Contact Me</p>
<form action="/show", method="GET">
Name: <input type="text" name="n" />
<br/>
Email: <input type="text" name="m" />
<br/>
<button>Press to Submit</button>
</form>
</body>
```
Post Method
the data from the user must be written in "request.form["variable name"]"
```python
@app.post("/update")
def handle():
input = request.form["data"]
return input
```
```htmlembedded
<form action="/update", method="POST">
Data: <input type="text" name="data" />
<br/>
<button>Press to Submit</button>
</form>
```
Notes:
* URL: GET
* Hyperlinks: GET
* Forms: GET or POST(default is GET, POST is safer than GET) -> inputting id & password will usually use POST
Sample:
Change homepage to specify GET method
```python
@app.get("/")
def index():
return render_template("index.html")
```
If we change the method to "@app.post("/")", then we will encounter a "Method not allowed error"
Change compute page to POST method
Backend
```python
@app.post("/compute")
def compute():
min_num = request.form["min"]
max_num = request.form["max"]
total = 0
for i in range(int(min_num), int(max_num) + 1):
total += i
return render_template("result.html", data=total)
```
Frontend
```htmlembedded
<p>Calculation</p>
<form action="/compute", method="POST">
min: <input type="text" name="min" />
<br/>
max: <input type="text" name="max" />
<br/>
<button>Press to Compute</button>
</form>
```
---
### Session
Setting:
```python
from flask import session
app = Flask(...)
app.secret_key = "XXXX"
```
secret_key should be secret
Store variable into session
```python
# Using session
# Greeting page
@app.get("/hello")
def hello():
name = request.args.get("name", "")
# store variable into session
session["username"] = name
return f"Hello, {name}"
# Talk page
@app.get("/talk")
def talk():
username = session["username"]
return f"Let's talk, {username}"
```
---
### Python & MongoDB
#### Connection:
install pymongo[srv]
```python
pip install pymongo[srv]
```
MongoDB Side
* Set up network access and database access
* Create cluster/database
* Connect to cluster/database (choose "connect to your application")
Connection Code in Python(provided by MongoDB and revised by me)
```python
import pymongo
uri = "mongodb+srv://username:password@cluster0.yf9rhuj.mongodb.net/?retryWrites=true&w=majority"
# Create a new client and connect to the server
client = pymongo.MongoClient(uri)
```
Add data into db
(all data type will be a dictionary)
```python
# Create a database named "test" (name can be changed)
db = client.test
# Create a collection named "users" (name can be changed)
collection = db.users
# Add data into MongoDB
collection.insert_one({
"name": "Brian",
"gender": "male"
})
print("Add Successfully")
```
#### CRUD operation
##### 1. Create:
Create: (one document) -> one dictionary
```python
collection.insert_one(document)
```
Sample:
```python
collection = db.users
collection.insert_one({
"email": "test@test.com",
"password": "test"
})
```
Obtain id of each record
```python
result = collection.insert_one({
"email": "test@test.com",
"password": "test"
})
print(result.insert_id)
```
Create: (multiple documents) -> list of dictionary
```python
colletion = db.users
collection.insert_many([{
"email": "test@test.com",
"password": "test"
}, {
"email": "abc@abc.com",
"password": "abc"
}])
```
```python
result = collection.insert_many([{
"email": "test@test.com",
"password": "test"
}, {
"email": "abc@abc.com",
"password": "abc"
}])
print(result.insert_ids)
```
##### 2. Read:
find the first record
```python
collection = db.users
data = collection.find_one()
print(data)
```
find the record according to object id
```python
from bson.objectid import ObjectId
collection = db.users
data = collection.find_one(
ObjectId("xxxxxxxxx")
)
print(data)
```
find all the record
```python
collection = db.users
cursor = collection.find()
# Print data in loop
for doc in cursor:
print(doc)
```
##### 3. Update:
```
collection.update_one(criteria, info to update)
```
Sample
```python
collection = db.users
collection.update_one({
"email": "test@test.com"
}, {
"$set":{
"password": "testtest"
}
})
```
update many records
```python
collection.update_many({
"level": 2
}, {
"$set": {
"role": "editor"
}
})
```
**There are many methods to update the records**
* "$set": replace / append
* "$inc": increment
* "$mul": multiply
* "$unset": delete the field
check update results:
```python
# one record
result = collection.update_one({....})
# many record
result = collection.update_many({...})
# matches
print(result.matches_count)
# modified results
print(result.modified_count)
```
Sample
```python
# update new field
result = collection.update_one({
"email": "john.wick@test.com"
}, {
"$set": {
"description": "Hello and Die Hard"
}
})
print("Matched Count", result.matched_count)
print("Modified Count", result.modified_count)
# unset field
result = collection.update_one({
"email": "john.wick@test.com"
}, {
"$unset": {
"description": ""
}
})
print("Matched Count", result.matched_count)
print("Modified Count", result.modified_count)
```
##### 4. Delete:
```
collection.delete_one(criteria)
collection.delete_many(criteria)
```
Get deleted count
```python
collection = db.users
result = collection.delete_many({
"level": 1
})
print(result.deleted_count)
```
#### Selection & Order
Single Criteria
```
collection.find_one(selection criteria)
collection.find(selection criteria)
```
Multiple Criteria
```
{"$and": [{criteria 1}, {criteria 2}...]}
{"$or": [{criteria 1}, {criteria 2}...]}
```
Order
```
collection.find(criteria, sort=[("key_name", pymongo.ASCENDING | pymongo.DESCENDING)])
When criteria is {}, it means that we want all records to be sorted
```
Sample:
```python
collection = db.users
cursor = collection.find({}, sort=[
("level", pymongo.ASCENDING)
])
for doc in cursor:
print(doc)
```
---
### Small Project - Full-Stack Membership System
Functions:
* Membership Registration
* Member Login
* Check Duplicate Registration
* Connection to MongoDB
#### Backend Code
```python
# Create connection to database
import pymongo
uri = "xxxx"
client = pymongo.MongoClient(uri)
# Create database named "member_system"
db = client.member_system
# print("MongoDB Successfully Connected")
# import Flask packages
from flask import *
# Create Flask application object
app = Flask(
__name__,
static_folder = "public",
static_url_path = "/"
)
# Configurate session
app.secret_key = "something secret"
# homepage
@app.route("/")
def index():
return render_template("index.html")
# Member page
@app.route("/member")
def member():
# Session control
if "username" in session:
# If valid, get username
username = session.get("username", "")
return render_template("member.html", username=username)
else:
return redirect("/")
# Error page
# /error?msg="error_message"
@app.route("/error")
def error():
error_message = request.args.get("msg", "Looks like there is something wrong.")
return render_template("error.html", message=error_message)
# Member sign-up(interact with db)
# Use POST method to obtain input data
@app.post("/signup")
def sign_up():
# Receive input data from frontend
input_username = request.form["username"]
input_email = request.form["email"]
input_password = request.form["password"]
# check input data with mongoDB
# named collection "users"
collection = db.users
# check if email is duplicated
check_mail = collection.find_one({
"email": input_email
})
# if duplicated(not None), then redirect to error page
if check_mail:
return redirect("/error?msg=This Email has been registered.")
# else, store data into db
collection.insert_one({
"username": input_username,
"email": input_email,
"password": input_password
})
# and redirect to success page
return render_template("success.html")
# Log-in Page
@app.route("/login")
def login_page():
return render_template("login.html")
# Member sign-in
# Use Session to store sign-in data
@app.post("/signin")
def sign_in():
# Obtain users input
input_email = request.form["email"]
input_password = request.form["password"]
# Check with db collection
collection = db.users
sign_in = collection.find_one({
"$and": [
{"email": input_email},
{"password": input_password}
]
})
# if not found, then redirect to error page
if not sign_in:
return redirect("/error?msg=Wrong Username or Password.")
# if found, store member info into session and redirect to memeber page
session["username"] = sign_in["username"]
return redirect("/member")
# Member sign-out (Uses GET)
# Remove user session
@app.route("/signout")
def sign_out():
# Remove session
del session["username"]
# Redirect to homepage
return redirect("/")
if __name__ == "__main__":
# Configurate port=3000
app.run(port=3000)
```
#### Frontend Code
**index(homepage)**
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Membership Registration System</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
max-width: 400px;
margin: 0 auto;
background-color: #ffffff;
padding: 20px;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
}
h3 {
text-align: center;
}
label {
display: block;
margin-bottom: 5px;
}
.input-group {
display: flex;
flex-direction: column;
margin-bottom: 15px;
}
input[type="text"],
input[type="password"] {
padding: 10px;
border: 1px solid #ccc;
border-radius: 3px;
}
button {
background-color: #007BFF;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 3px;
}
button:hover {
background-color: #0056b3;
}
.center-form {
text-align: center;
}
p {
text-align: center;
}
a {
text-decoration: none;
color: #007BFF;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<h2>Contact Us</h2>
<div>
<h3>Please Leave Your Info to Become a Member</h3>
<p>Already a Member? Click <a href="/login">Here</a></p>
</div>
<div class="center-form">
<form action="/signup" method="POST">
<div class="input-group">
<label for="username">Enter Your Name:</label>
<input type="text" name="username" id="username" placeholder="Enter Your Name" required>
</div>
<div class="input-group">
<label for="email">Enter Your E-mail:</label>
<input type="text" name="email" id="email" placeholder="name@example.com" required>
</div>
<div class="input-group">
<label for="password">Enter Your Password:</label>
<input type="password" name="password" id="password" required>
</div>
<button type="submit">Submit</button>
</form>
</div>
</div>
</body>
</html>
```
**member(memberpage)**
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Member Pages</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
max-width: 400px;
margin: 0 auto;
background-color: #ffffff;
padding: 20px;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
}
div {
text-align: center;
margin: 20px 0;
}
button {
background-color: #007BFF;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 3px;
display: block;
margin: 0 auto;
}
button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h2>Welcome!!</h2>
<div>Nice seeing you, {{ username }}!</div>
<button class="return-button" onclick="window.location.href='/signout';">Sign Out</button>
</div>
</body>
</html>
```
**error**
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Error</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
max-width: 400px;
margin: 0 auto;
background-color: #ffffff;
padding: 20px;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
}
div {
text-align: center;
margin: 20px 0;
}
button {
background-color: #007BFF;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 3px;
display: block;
margin: 0 auto;
}
button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<h2>Oops...</h2>
<div>{{ message }}</div>
<div>Please try again.</div>
<button onclick="window.location.href='/';"> Return Home</button>
</body>
</html>
```
**login**
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Member Login</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
max-width: 400px;
margin: 0 auto;
background-color: #ffffff;
padding: 20px;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
h3 {
text-align: center;
}
label {
display: block;
margin-bottom: 5px;
}
.input-group {
display: flex;
flex-direction: column;
margin-bottom: 15px;
}
input[type="text"],
input[type="password"] {
width: 95%;
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
button {
background-color: #007BFF;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 3px;
width: 100%;
}
button:hover {
background-color: #0056b3;
}
.center-form {
text-align: center;
}
.return-button {
background-color: #ccc;
color: #333;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 3px;
margin-top: 10px;
width: 100%;
}
.return-button:hover {
background-color: #999;
}
</style>
</head>
<body>
<div class="container">
<h3>Member Login</h3>
<div class="center-form">
<form action="/signin" method="POST">
<label for="email">E-mail:</label>
<input type="text" name="email" id="email" placeholder="name@example.com" required>
<label for="password">Password:</label>
<input type="password" name="password" id="password" required>
<button type="submit">Log in</button>
</form>
<button class="return-button" onclick="window.location.href='/';">Return Home</button>
</div>
</div>
</body>
</html>
```
**success**
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Redirect to Homepage in 3 second -->
<meta http-equiv = "refresh" content = "3; url = /" />
<title>Success</title>
</head>
<body>
<h3>Registered Successfully!</h3>
<p>Will return to Homepage shortly.</p>
</body>
</html>
```