# 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") ```