---
# System prepended metadata

title: 1/17課程

---

# 1/17課程

[1/19 課程筆記連結](https://hackmd.io/y5h8x86xQYS4-xlJdtyTdQ?both)


## 大綱
- 簡介 PostgreSQL 
- 下載 PostgreSQL 並透過 PGAdmin 操作
- Python Alembic 版控

## 需預先下載的軟體
- [Vscode](https://code.visualstudio.com/download)
- [PGAdmin](https://www.pgadmin.org/download/pgadmin-4-windows/)
- [今天上課講義](https://docs.google.com/presentation/d/1a8D5zRzx9109b2oVNPVwJw7LWSdglUvV/edit?usp=sharing&ouid=101247442856896968889&rtpof=true&sd=true)
- [今天上課講義2](https://www.canva.com/design/DAFp31PYLYQ/WeWxwnzmYmgjDJI3E80lJQ/edit?utm_content=DAFp31PYLYQ&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton)
- [上課用程式碼](https://drive.google.com/file/d/1Q94HpV2KlddNXj9YTJ9oUSzE1suuY3BX/view?usp=drive_link)
- Python3.10

requirements.txt
```
aniso8601==9.0.1
apispec==5.2.2
certifi==2023.5.7
charset-normalizer==3.2.0
click==8.1.3
Flask==2.1.2
flask-apispec==0.11.4
Flask-JWT-Extended==4.4.1
Flask-RESTful==0.3.9
idna==3.4
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
marshmallow==3.18.0
marshmallow-enum==1.5.1
packaging==24.2
pip==24.3.1
PyJWT==2.6.0
python-dotenv==1.0.1
pytz==2022.1
requests==2.31.0
setuptools==75.6.0
six==1.16.0
urllib3==1.26.16
webargs==8.2.0
Werkzeug==2.2.2

psycopg2-binary==2.9.3
alembic==1.10.2
SQLAlchemy==1.4.37
Flask-SQLAlchemy==2.5.1
SQLAlchemy-Utils==0.38.2
```
- Ubuntu24.04

## Q&A
`pip install -i https://test.pypi.org/simple/ psycopg2-binary==2.9.3 --force-reinstall --no-cache-dir`
網路上找到這個方法有成功，但這個方式有什麼要注意的嗎?
![image](https://hackmd.io/_uploads/SkiYuFvv1l.png)


## 上課會用到網頁
- [ChatGPT](https://chat.openai.com/)
- [API User專案下載網址](https://drive.google.com/file/d/1Q94HpV2KlddNXj9YTJ9oUSzE1suuY3BX/view?usp=drive_link)


## 補充
- [UTM](https://techblog.shippio.io/how-to-run-an-ubuntu-22-04-vm-on-m1-m2-apple-silicon-9554adf4fda1)



## 上半堂(要用sudo權限在api_rest_user資料夾底下進行)
```
apt-get install curl ca-certificates python3-pip python3-venv libpq-dev gnupg2 wget vim ufw net-tools -y


sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'


wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -


apt-get update

apt-get -y install postgresql-12

apt list --installed | grep postgresql
```

```
vim /etc/postgresql/12/main/pg_hba.conf


找到 replication
/replication // 找 replication 的關鍵字

壓 enter // 確定要找這個

按 n // 跳到下一個符合關鍵字的地方

o // 跟 i  一樣是寫入、但是是寫下一行

加上 host    all    all     0.0.0.0/0       md5

```

```
vim /etc/postgresql/12/main/postgresql.conf

/listen  // 找 listen 的關鍵字

壓 enter // 確定要找這個

加上  listen_addresses = '*'

```


```
確認外部連線
netstat -ano | grep 5432
```

```
pg_isready 

pg_ctlcluster 12 main start
pg_ctlcluster 12 main restart


```


```
ufw allow 5432
```
```
su - postgres  // 更換成 postgres 的 user

psql  // 進到 postgresql 操作介面
 
\password postgres  // 更換 postgres 的密碼


```

建立 server group
![image](https://hackmd.io/_uploads/H1QV0NDwkx.png)

建立 server 
![image](https://hackmd.io/_uploads/Bk3S04vDJg.png)


填入連線資訊
![image](https://hackmd.io/_uploads/SJslJBvPke.png)

建立 database
![image](https://hackmd.io/_uploads/Syr6xBvPkg.png)
![image](https://hackmd.io/_uploads/S1_ceSwwyg.png)

建立 table
![image](https://hackmd.io/_uploads/HyTyWrDvyx.png)
![image](https://hackmd.io/_uploads/HkTZbHww1l.png)

進入 {自己取的database 的名稱} > Schemas > Tables (感恩！！！)
![image](https://hackmd.io/_uploads/H1iHbSPPyg.png)
## 建立 table 的 column
![image](https://hackmd.io/_uploads/rk16-BDP1x.png)

### 建立 id 
![image](https://hackmd.io/_uploads/BJZgMSDPkg.png)
![image](https://hackmd.io/_uploads/ry9VzrPwJl.png)

### 建立 name
![image](https://hackmd.io/_uploads/S19OGSPvyl.png)


## 讀取 table
![image](https://hackmd.io/_uploads/ryFH7BDPke.png)

新增資料
![image](https://hackmd.io/_uploads/SyTZVSPPye.png)

存擋
![image](https://hackmd.io/_uploads/rk4NErvDJg.png)


## 其他
再次進到 table 做設定
![image](https://hackmd.io/_uploads/ryyfmrPwkg.png)


[模擬 DB 資料遺失]
資料備份

切換回 postgres
![image](https://hackmd.io/_uploads/SyB-_HPPkl.png)


`pg_dump -U postgres -Fc class > post.dump`

![image](https://hackmd.io/_uploads/SyKXuSwPJl.png)


將資料移除
![image](https://hackmd.io/_uploads/SJ7VjBPP1l.png)

移除後進行儲存（save）
![image](https://hackmd.io/_uploads/rkKOjHDwye.png)

[將資料回復]
`pg_restore -U postgres  -d class  post.dump`




## 下半堂

### Alembic 相關指令
`alembic --help`
![image](https://hackmd.io/_uploads/HyOc2Pvwkl.png)
`pip install alembic`

alembic.ini
```
sqlalchemy.url = postgresql://{username}:{password}@{ip}:{port}/{db}

ip: 127.0.0.1 -> python 程式碼 以及 postgresql 都在虛擬機中
ip: {VM ip}   -> postgresql 在虛擬機中 + python 程式碼在 window


sqlalchemy.url = postgresql://postgres:postgres@192.168.70.3:5432/class
```

model.py
```
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()



class User(db.Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(45))
    account = Column(String(45))
    password = Column(String(100))
    birth = Column(String(100))
    note = Column(String(45))


```



`alembic revision --autogenerate -m 'init'`
`alembic upgrade head`


## 下半堂程式碼
![image](https://hackmd.io/_uploads/BkS70Fwvyl.png)


api.py
```
from flask import Flask
from flask_restful import Api
from apispec import APISpec
from flask_apispec.extension import FlaskApiSpec
from apispec.ext.marshmallow import MarshmallowPlugin
from flask_jwt_extended import JWTManager
from model import db

from resource.user import Users, SingleUser
from resource.login import Login

# Flask init
app = Flask(__name__)

# FlaskRestFul init
api = Api(app)

# DB setting
app.config["SQLALCHEMY_DATABASE_URI"] = (
    "postgresql://postgres:postgres@192.168.70.3:5432/class"
)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
    "pool_pre_ping": True, #檢查連線是否有效，避免使用失效的連線。
    "pool_recycle": 60, #設定連線的最大使用時間，防止連線長時間閒置而被資料庫切斷。
    "pool_timeout": 300, #設定等待可用連線的最大時間，防止長時間阻塞。
    "pool_size": 20, #設定連線池的最大連線數量，控制資料庫連線的併發數。
}


# Swagger

## JWT swagger setting
app.config["JWT_SECRET_KEY"] = "secret_key"
security_definitions = {
    "bearer": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
    }
}

app.config.update(
    {
        "APISPEC_SPEC": APISpec(
            title="Awesome Projectdfdfssdd",
            version="v1",
            # JWT token setting
            securityDefinitions=security_definitions,
            plugins=[MarshmallowPlugin()],
            openapi_version="2.0.0",
        ),
        "APISPEC_SWAGGER_URL": "/swagger/",  # URI to access API Doc JSON
        "APISPEC_SWAGGER_UI_URL": "/swagger-ui/",  # URI to access UI of API Doc
    }
)

# Swagger init
docs = FlaskApiSpec(app)

# Route
api.add_resource(Users, "/users")
docs.register(Users)
api.add_resource(SingleUser, "/user/<int:id>")
docs.register(SingleUser)
api.add_resource(Login, "/login")
docs.register(Login)


if __name__ == "__main__":
    # DB setting
    db.init_app(app)
    db.app = app
    # JWT init
    jwt = JWTManager().init_app(app)
    app.run(host="0.0.0.0", port="10009", debug=True, use_reloader=True)


```


user.py
```
import json
from flask_apispec import MethodResource, marshal_with, doc, use_kwargs
from flask_jwt_extended import jwt_required
from model import User, db


from . import user_route_model
import util


class Users(MethodResource):
    # GET_ALL
    @doc(description="Get Users info.", tags=["User"])
    @marshal_with(user_route_model.UserGetResponse, code=200)
    def get(self, name):
        return util.success()

    # Create User
    @doc(description="Get Users info.", tags=["User"])
    @use_kwargs(user_route_model.UserPostSchema, location="form")
    @marshal_with(user_route_model.UserPostResponse, code=201)
    def post(self, name, birth, note, account, password):
        row = User(
            name=name,
            birth=birth,
            note=note,
            account=account,
            password=password,
        )
        db.session.add(row)
        db.session.commit()
        return util.success(status_code=201)


class SingleUser(MethodResource):
    # Get single by id
    @doc(description="Get Single Users info.", tags=["User"])
    @marshal_with(user_route_model.UserSingleGetResponse, code=200)
    def get(self):
        return util.success()

    @doc(description="Update Single Users info.", tags=["User"])
    @use_kwargs(user_route_model.UserPutSchema, location="form")
    @marshal_with(user_route_model.UserPutResponse, code=200)
    def put(self, id, name, birth, note, account, password):
        return util.success()

    @doc(
        description="Delete Single Users info.",
        tags=["User"],
        security=[{"bearer": []}],
    )
    @marshal_with(user_route_model.UserSingleDeleteResponse, code=204)
    @jwt_required()
    def delete(self, id):
        return util.success(status_code=204)


```


user_route_model.py
```
from marshmallow import Schema, fields


# Request
class UserPutSchema(Schema):
    name = fields.Str(doc="name", example="string", required=True)
    birth = fields.Str(doc="birth", example="string", required=True)
    note = fields.Str(doc="note", example="string", required=True)
    account = fields.Str(doc="account", example="string", required=True)
    password = fields.Str(doc="password", example="string", required=True)


class UserPostSchema(Schema):
    name = fields.Str(doc="name", example="string", required=True)
    birth = fields.Str(doc="birth", example="string", required=True)
    note = fields.Str(doc="note", example="string", required=True)
    account = fields.Str(doc="account", example="string", required=True)
    password = fields.Str(doc="password", example="string", required=True)


# Response
class UserGetResponse(Schema):
    message = fields.Str(example="success")
    datatime = fields.Str(example="1970-01-01T00:00:00.000000")
    data = fields.List(fields.Dict())


class UserSingleGetResponse(Schema):
    message = fields.Str(example="success")
    datatime = fields.Str(example="1970-01-01T00:00:00.000000")
    data = fields.Dict()


class UserPostResponse(Schema):
    message = fields.Str(example="success")


class UserPutResponse(Schema):
    message = fields.Str(example="success")


class UserSingleDeleteResponse(Schema):
    message = fields.Str(example="success")
```

