10000 chore: many updates · chilcano/python_fastapi@ecbf757 · GitHub
[go: up one dir, main page]

Skip to content

Commit ecbf757

Browse files
committed
chore: many updates
1 parent 6fd0c0b commit ecbf757

File tree

15 files changed

+515
-132
lines changed

15 files changed

+515
-132
lines changed

.env renamed to .env.sample

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
DATABASE_PORT=6500
2-
POSTGRES_PASSWORD=password123
3-
POSTGRES_USER=postgres
4-
POSTGRES_DB=fastapi
1+
DB_PORT=6500
2+
DB_PWD=password123
3+
DB_USR=postgres
4+
DB_NAME=fastapi
55
POSTGRES_HOST=postgres
6-
POSTGRES_HOSTNAME=127.0.0.1
6+
DB_HOST=127.0.0.1
77

88
ACCESS_TOKEN_EXPIRES_IN=15
99
REFRESH_TOKEN_EXPIRES_IN=60
@@ -13,7 +13,7 @@ CLIENT_ORIGIN=http://localhost:3000
1313

1414
VERIFICATION_SECRET=my-email-verification-secret
1515

16-
EMAIL_HOST=smtp.mailtrap.io
16+
EMAIL_HOST=sandbox.smtp.mailtrap.io
1717
EMAIL_PORT=587
1818
EMAIL_USERNAME=af7917ee09173f
1919
EMAIL_PASSWORD=dbd2433183aa32

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
__pycache__
22
venv/
3-
# .env
3+
venv1/
4+
.env

01-step-by-step-guide-crud-python.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# 01. Step by step guide to run and use the CRUD RESTful API with Python
2+
3+
## Prerequisites
4+
5+
- Docker and Docker Compose to get PostgreSQL, pgAdmin and deploy the Python CRUD.
6+
- Python > 3.6
7+
8+
## 1. Setup the environment
9+
10+
```sh
11+
docker-compose up -d
12+
```
13+
14+
## 2. checks docker-compose.yml and vars
15+
16+
```sh
17+
docker compose config
18+
```
19+
20+
## 3. checks if extensions.sql has been executed
21+
22+
```sh
23+
docker logs -f crud-postgres-1
24+
25+
...
26+
CREATE DATABASE
27+
28+
29+
/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/extension.sql
30+
CREATE EXTENSION
31+
...
32+
```
33+
34+
And tailing the logs.
35+
```sh
36+
docker compose logs -f
37+
```
38+
39+
40+
## 4. Get a shell from docker to check uuid-ossp
41+
42+
```sh
43+
docker exec -it postgres bash
44+
45+
root@f41e7e519277:/# psql -U postgres fastapi
46+
psql (15.3 (Debian 15.3-1.pgdg120+1))
47+
Type "help" for help.
48+
49+
fastapi=# select * from pg_available_extensions;
50+
...
51+
uuid-ossp | 1.1 | 1.1 | generate universally unique identifiers (UUIDs)
52+
...
53+
```
54+
55+
56+
## 5. Create env, install deps
57+
58+
```sh
59+
$ python -V
60+
Python 3.11.3
61+
62+
$ sudo pacman -Sy python-pip
63+
$ pip -V
64+
pip 23.1.2 from /usr/lib/python3.11/site-packages/pip (python 3.11)
65+
66+
$ python -m venv venv1
67+
$ source venv1/bin/activate
68+
```
69+
70+
Install Postgres libraries and Python module.
71+
```sh
72+
$ sudo pacman -S postgresql-libs
73+
$ pip install psycopg2
74+
```
75+
76+
Update the `pip` if needed.
77+
```sh
78+
$ pip install --upgrade pip
79+
```
80+
81+
## 6. Optionally, upgrade the dependencies
82+
83+
You have 3 options, install one by one each python module, run `pip install -r requirements.txt --upgrade` or use `pip-upgrader` and `requirements.txt` to install all python modules in a consistent way.
84+
85+
* Option 1, this is a slow process and requires to review the code to identify what modules are used.
86+
The next command only install 3 main modules. There are more.
87+
88+
```sh
89+
$ pip install uvicorn fastapi SQLAlchemy
90+
```
91+
92+
* Option 2, using `pip install -r requirements.txt --upgrade` is faster but generate conflicts. Use it if you are sure about the dependencies versions.
93+
94+
```sh
95+
$ pip install -r requirements.txt --upgrade
96+
```
97+
98+
* Option 3, this uses `pip-upgrader` and `requirements.txt`. Helps to choose the right module version to avoid conflicts.
99+
```sh
100+
$ pip install pip-upgrader
101+
$ pip-upgrade requirements.txt
102+
```
103+
104+
Once completed this process, the next task is freeze and update the `requirements.txt` file optionally.
105+
```sh
106+
pip freeze > requirements.txt
107+
```
108+
109+
## 7. Run the application
110+
111+
```sh
112+
$ uvicorn app.main:app --host localhost --port 8000 --reload
113+
```
114+
115+
This process automatically will create the DB, serve the application using uvicorn as server. If the DBs changes, then Alembic will detect the changes and will apply them automatically.
116+
Additionally, this will expose a set of REST API. You can see all API endpoints available here:
117+
118+
* [http://localhost:8000/docs](http://localhost:8000/docs)
119+
120+
121+
## 8. Using the applicaion
122+
123+
### 8.1. Mailserver for testing
124+
125+
Before using the application, create an account on [Mailtrap](https://mailtrap.io) to simulate the process account validation during the of user signup. Once created, update the `.env` file, specifically the next values:
126+
```
127+
EMAIL_HOST=sandbox.smtp.mailtrap.io
128+
EMAIL_PORT=587
129+
EMAIL_USERNAME=your-username
130+
EMAIL_PASSWORD=your-password
131+
EMAIL_FROM=info@your-domain.com
132+
```
133+
134+
### 8.2. RESTful API testing
135+
136+
Import this [Postman collection](https://www.getpostman.com/collections/969286ffb3ee641b3a83) in your favorite API Testing Tool (Postman or Insomnia). It is a set of API requests that we can use for testing our application.
137+
138+
But, if you do want to use `cURL` instead of Postman or Insomnia, use these commands:
139+
140+
#### 1. API Healthceck
141+
142+
```sh
143+
144+
```
145+
146+
#### 2. API User signup
147+
```sh
148+
149+
```
150+
151+
#### 3. API User login
152+
```sh
153+
154+
```
155+
156+
#### 4. API User info
157+
```sh
158+
159+
```
160+
161+
#### 5. API create Post
162+
```sh
163+
164+
```
165+
166+
#### 6. API list Posts
167+
```sh
168+
169+
```
170+
171+
172+
## References
173+
174+
1. https://credibledev.com/python-flask-dev-environment-on-manjaro-linux/

readMe.md renamed to README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
In this article, you'll learn how to secure a FastAPI app by implementing access and refresh token functionalities using JSON Web Tokens (JWTs). We'll use the FastAPI JWT Auth package to sign, encode and decode the access and refresh JWT tokens.
66

7-
![RESTful API with Python,SQLAlchemy, & FastAPI: Access and Refresh Tokens](https://codevoweb.com/wp-content/uploads/2022/07/RESTful-API-with-Python-FastAPI-Access-and-Refresh-Tokens.webp)
8-
97
### Topics Covered
108

119
- Python FastAPI JWT Authentication Overview
@@ -36,8 +34,6 @@ Read the entire article here: [https://codevoweb.com/restful-api-with-python-fas
3634

3735
In this article, you'll learn how to send HTML emails with Python, FastAPI, SQLAlchemy, PostgreSQL, Jinja2, and Docker-compose. Also, you'll learn how to dynamically generate HTML templates with the Jinja2 package.
3836

39-
![RESTful API with Python, SQLAlchemy, & FastAPI: Send HTML Emails](https://codevoweb.com/wp-content/uploads/2022/07/RESTful-API-with-Python-FastAPI-Send-HTML-Emails.webp)
40-
4137
### Topics Covered
4238

4339
- Send HTML Email with jinja2 and FastAPI Overview
@@ -56,8 +52,6 @@ Read the entire article here: [https://codevoweb.com/restful-api-with-python-fas
5652

5753
This article will teach you how to create a CRUD RESTful API with Python, FastAPI, SQLAlchemy ORM, Pydantic, Alembic, PostgreSQL, and Docker-compose to perform the basic Create/Read/Update/Delete operations against a database.
5854

59-
![CRUD RESTful API Server with Python, SQLAlchemy, FastAPI, and PostgreSQL](https://codevoweb.com/wp-content/uploads/2022/07/CRUD-RESTful-API-Server-with-Python-FastAPI-and-PostgreSQL.webp)
60-
6155
### Topics Covered
6256

6357
- Python, FastAPI, PostgreSQL, SQLAlchemy CRUD API Overview
@@ -79,3 +73,16 @@ This article will teach you how to create a CRUD RESTful API with Python, FastAP
7973

8074
Read the entire article here: [https://codevoweb.com/crud-restful-api-server-with-python-fastapi-and-postgresql](https://codevoweb.com/crud-restful-api-server-with-python-fastapi-and-postgresql)
8175

76+
### Step by step guide
77+
78+
:arrow_right: I've created step by step guide to run and use this CRUD RESTful API.
79+
:point_right: [01-step-by-step-guide-crud-python.md](01-step-by-step-guide-crud-python.md)
80+
81+
Changes I did:
82+
1. Postgres extension installation
83+
2. Docker compose for pgAdmin
84+
3. Adding the Postman collection in the repo
85+
4. Examples using curl commands
86+
5. Deploying the python application in Docker
87+
88+

alembic/env.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
# this is the Alembic Config object, which provides
1111
# access to the values within the .ini file in use.
1212
config = context.config
13-
config.set_main_option(
14-
"sqlalchemy.url", f"postgresql+psycopg2://{settings.POSTGRES_USER}:{settings.POSTGRES_PASSWORD}@{settings.POSTGRES_HOSTNAME}:{settings.DATABASE_PORT}/{settings.POSTGRES_DB}")
13+
config.set_main_option("sqlalchemy.url", f"postgresql+psycopg2://{settings.DB_USR}:{settings.DB_PWD}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}")
1514

1615
# Interpret the config file for Python logging.
1716
# This line sets up loggers basically.
@@ -43,12 +42,7 @@ def run_migrations_offline() -> None:
4342
4443
"""
4544
url = config.get_main_option("sqlalchemy.url")
46-
context.configure(
47-
url=url,
48-
target_metadata=target_metadata,
49-
literal_binds=True,
50-
dialect_opts={"paramstyle": "named"},
51-
)
45+
context.configure(url=url, target_metadata=target_metadata, literal_binds=True, dialect_opts={"paramstyle": "named"},)
5246

5347
with context.begin_transaction():
5448
context.run_migrations()
@@ -61,16 +55,10 @@ def run_migrations_online() -> None:
6155
and associate a connection with the context.
6256
6357
"""
64-
connectable = engine_from_config(
65-
config.get_section(config.config_ini_section),
66-
prefix="sqlalchemy.",
67-
poolclass=pool.NullPool,
68-
)
58+
connectable = engine_from_config(config.get_section(config.config_ini_section), prefix="sqlalchemy.", poolclass=pool.NullPool,)
6959

7060
with connectable.connect() as connection:
71-
context.configure(
72-
connection=connection, target_metadata=target_metadata
73-
)
61+
context.configure(connection=connection, target_metadata=target_metadata)
7462

7563
with context.begin_transaction():
7664
context.run_migrations()

app/config.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
from pydantic import BaseSettings, EmailStr
22

3-
43
class Settings(BaseSettings):
5-
DATABASE_PORT: int
6-
POSTGRES_PASSWORD: str
7-
POSTGRES_USER: str
8-
POSTGRES_DB: str
4+
DB_PORT: int
5+
DB_PWD: str
6+
DB_USR: str
7+
DB_NAME: str
98
POSTGRES_HOST: str
10-
POSTGRES_HOSTNAME: str
9+
DB_HOST: str
1110

1211
JWT_PUBLIC_KEY: str
1312
JWT_PRIVATE_KEY: str
@@ -28,5 +27,4 @@ class Settings(BaseSettings):
2827
class Config:
2928
env_file = './.env'
3029

31-
3230
settings = Settings()

app/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from sqlalchemy.orm import sessionmaker
44
from .config import settings
55

6-
SQLALCHEMY_DATABASE_URL = f"postgresql://{settings.POSTGRES_USER}:{settings.POSTGRES_PASSWORD}@{settings.POSTGRES_HOSTNAME}:{settings.DATABASE_PORT}/{settings.POSTGRES_DB}"
6+
SQLALCHEMY_DATABASE_URL = f"postgresql://{settings.DB_USR}:{settings.DB_PWD}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}"
77

88
engine = create_engine(
99
SQLALCHEMY_DATABASE_URL

app/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,7 @@
2626
@app.get('/api/healthchecker')
2727
def root():
2828
return {'message': 'Hello World'}
29+
30+
## $ docker compose -f docker-compose.pgadmin.yaml config (test config file)
31+
## $ docker compose -f docker-compose.pgadmin.yaml up (creates and starts containers)
32+
## $ docker compose -f docker-compose.pgadmin.yaml up --build (build containers before running containers)

app/models.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,28 @@
44
from sqlalchemy.dialects.postgresql import UUID
55
from sqlalchemy.orm import relationship
66

7-
87
class User(Base):
98
__tablename__ = 'users'
10-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False,
11-
default=uuid.uuid4)
9+
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False, default=uuid.uuid4)
1210
name = Column(String, nullable=False)
1311
email = Column(String, unique=True, nullable=False)
1412
password = Column(String, nullable=False)
1513
photo = Column(String, nullable=True)
1614
verified = Column(Boolean, nullable=False, server_default='False')
1715
verification_code = Column(String, nullable=True, unique=True)
1816
role = Column(String, server_default='user', nullable=False)
19-
created_at = Column(TIMESTAMP(timezone=True),
20-
nullable=False, server_default=text("now()"))
21-
updated_at = Column(TIMESTAMP(timezone=True),
22-
nullable=False, server_default=text("now()"))
17+
created_at = Column(TIMESTAMP(timezone=True), nullable=False, server_default=text("now()"))
18+
updated_at = Column(TIMESTAMP(timezone=True), nullable=False, server_default=text("now()"))
2319

2420

2521
class Post(Base):
2622
__tablename__ = 'posts'
27-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False,
28-
default=uuid.uuid4)
29-
user_id = Column(UUID(as_uuid=True), ForeignKey(
30-
'users.id', ondelete='CASCADE'), nullable=False)
23+
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False, default=uuid.uuid4)
24+
user_id = Column(UUID(as_uuid=True), ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
3125
title = Column(String, nullable=False)
3226
content = Column(String, nullable=False)
3327
category = Column(String, nullable=False)
3428
image = Column(String, nullable=False)
35-
created_at = Column(TIMESTAMP(timezone=True),
36-
nullable=False, server_default=text("now()"))
37-
updated_at = Column(TIMESTAMP(timezone=True),
38-
nullable=False, server_default=text("now()"))
29+
created_at = Column(TIMESTAMP(timezone=True), nullable=False, server_default=text("now()"))
30+
updated_at = Column(TIMESTAMP(timezone=True), nullable=False, server_default=text("now()"))
3931
user = relationship('User')

0 commit comments

Comments
 (0)
0