Skip to content

Commit 6fdba19

Browse files
committed
✨ Update all for Postgres and new techniques
1 parent 1b4d244 commit 6fdba19

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+789
-1312
lines changed

{{cookiecutter.project_slug}}/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ Make sure you create a "revision" of your models and that you "upgrade" your dat
181181
docker-compose exec backend bash
182182
```
183183

184+
* If you created a new model in `./backend/app/app/db_models/`, make sure to import it in `./backend/app/app/db/base.py`, that Python module (`base.py`) that imports all the models will be used by Alembic.
185+
184186
* After changing a model (for example, adding a column), inside the container, create a revision, e.g.:
185187

186188
```bash

{{cookiecutter.project_slug}}/backend/app/Pipfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ isort = "*"
1111
autoflake = "*"
1212
flake8 = "*"
1313
pytest = "*"
14+
vulture = "*"
1415

1516
[packages]
1617
fastapi = "*"
@@ -25,6 +26,11 @@ tenacity = "*"
2526
pydantic = "*"
2627
emails = "*"
2728
raven = "*"
29+
gunicorn = "*"
30+
jinja2 = "*"
31+
psycopg2-binary = "*"
32+
alembic = "*"
33+
sqlalchemy = "*"
2834

2935
[requires]
3036
python_version = "3.6"

{{cookiecutter.project_slug}}/backend/app/Pipfile.lock

Lines changed: 222 additions & 104 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# A generic, single database configuration.
2+
3+
[alembic]
4+
# path to migration scripts
5+
script_location = alembic
6+
7+
# template used to generate migration files
8+
# file_template = %%(rev)s_%%(slug)s
9+
10+
# timezone to use when rendering the date
11+
# within the migration file as well as the filename.
12+
# string value is passed to dateutil.tz.gettz()
13+
# leave blank for localtime
14+
# timezone =
15+
16+
# max length of characters to apply to the
17+
# "slug" field
18+
#truncate_slug_length = 40
19+
20+
# set to 'true' to run the environment during
21+
# the 'revision' command, regardless of autogenerate
22+
# revision_environment = false
23+
24+
# set to 'true' to allow .pyc and .pyo files without
25+
# a source .py file to be detected as revisions in the
26+
# versions/ directory
27+
# sourceless = false
28+
29+
# version location specification; this defaults
30+
# to alembic/versions. When using multiple version
31+
# directories, initial revisions must be specified with --version-path
32+
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
33+
34+
# the output encoding used when revision files
35+
# are written from script.py.mako
36+
# output_encoding = utf-8
37+
38+
sqlalchemy.url = postgresql://postgres:changethis@db/app
39+
40+
41+
# Logging configuration
42+
[loggers]
43+
keys = root,sqlalchemy,alembic
44+
45+
[handlers]
46+
keys = console
47+
48+
[formatters]
49+
keys = generic
50+
51+
[logger_root]
52+
level = WARN
53+
handlers = console
54+
qualname =
55+
56+
[logger_sqlalchemy]
57+
level = WARN
58+
handlers =
59+
qualname = sqlalchemy.engine
60+
61+
[logger_alembic]
62+
level = INFO
63+
handlers =
64+
qualname = alembic
65+
66+
[handler_console]
67+
class = StreamHandler
68+
args = (sys.stderr,)
69+
level = NOTSET
70+
formatter = generic
71+
72+
[formatter_generic]
73+
format = %(levelname)-5.5s [%(name)s] %(message)s
74+
datefmt = %H:%M:%S
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""First revision
2+
3+
Revision ID: e6ae69e9dcb9
4+
Revises:
5+
Create Date: 2019-02-13 14:27:57.038583
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = 'e6ae69e9dcb9'
14+
down_revision = None
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table('user',
22+
sa.Column('id', sa.Integer(), nullable=False),
23+
sa.Column('full_name', sa.String(), nullable=True),
24+
sa.Column('email', sa.String(), nullable=True),
25+
sa.Column('hashed_password', sa.String(), nullable=True),
26+
sa.Column('is_active', sa.Boolean(), nullable=True),
27+
sa.Column('is_superuser', sa.Boolean(), nullable=True),
28+
sa.PrimaryKeyConstraint('id')
29+
)
30+
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True)
31+
op.create_index(op.f('ix_user_full_name'), 'user', ['full_name'], unique=False)
32+
op.create_index(op.f('ix_user_id'), 'user', ['id'], unique=False)
33+
# ### end Alembic commands ###
34+
35+
36+
def downgrade():
37+
# ### commands auto generated by Alembic - please adjust! ###
38+
op.drop_index(op.f('ix_user_id'), table_name='user')
39+
op.drop_index(op.f('ix_user_full_name'), table_name='user')
40+
op.drop_index(op.f('ix_user_email'), table_name='user')
41+
op.drop_table('user')
42+
# ### end Alembic commands ###
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
from fastapi import APIRouter
22

3-
from app.api.api_v1.endpoints.role import router as roles_router
43
from app.api.api_v1.endpoints.token import router as token_router
54
from app.api.api_v1.endpoints.user import router as user_router
65
from app.api.api_v1.endpoints.utils import router as utils_router
76

87
api_router = APIRouter()
9-
api_router.include_router(roles_router)
108
api_router.include_router(token_router)
119
api_router.include_router(user_router)
1210
api_router.include_router(utils_router)

{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/role.py

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
from datetime import timedelta
22

3-
from fastapi import APIRouter, Depends
3+
from fastapi import APIRouter, Depends, HTTPException
44
from fastapi.security import OAuth2PasswordRequestForm
5-
from starlette.exceptions import HTTPException
5+
from sqlalchemy.orm import Session
66

7+
from app.api.utils.db import get_db
8+
from app.api.utils.security import get_current_user
79
from app.core import config
8-
from app.core.jwt import create_access_token, get_current_user
9-
from app.crud.user import (
10-
authenticate_user,
11-
check_if_user_is_active,
12-
check_if_user_is_superuser,
13-
get_user,
14-
update_user,
15-
)
16-
from app.db.database import get_default_bucket
10+
from app.core.jwt import create_access_token
11+
from app.core.security import get_password_hash
12+
from app.crud import user as crud_user
13+
from app.db_models.user import User as DBUser
1714
from app.models.msg import Msg
1815
from app.models.token import Token
19-
from app.models.user import User, UserInDB, UserInUpdate
16+
from app.models.user import User
2017
from app.utils import (
2118
generate_password_reset_token,
2219
send_reset_password_email,
@@ -27,70 +24,73 @@
2724

2825

2926
@router.post("/login/access-token", response_model=Token, tags=["login"])
30-
def route_login_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
27+
def login_access_token(
28+
db: Session = Depends(get_db), form_data: OAuth2PasswordRequestForm = Depends()
29+
):
3130
"""
3231
OAuth2 compatible token login, get an access token for future requests
3332
"""
34-
bucket = get_default_bucket()
35-
user = authenticate_user(bucket, form_data.username, form_data.password)
33+
user = crud_user.authenticate(
34+
db, email=form_data.username, password=form_data.password
35+
)
3636
if not user:
3737
raise HTTPException(status_code=400, detail="Incorrect email or password")
38-
elif not check_if_user_is_active(user):
38+
elif not crud_user.is_active(user):
3939
raise HTTPException(status_code=400, detail="Inactive user")
4040
access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES)
4141
return {
4242
"access_token": create_access_token(
43-
data={"username": form_data.username}, expires_delta=access_token_expires
43+
data={"user_id": user.id}, expires_delta=access_token_expires
4444
),
4545
"token_type": "bearer",
4646
}
4747

4848

4949
@router.post("/login/test-token", tags=["login"], response_model=User)
50-
def route_test_token(current_user: UserInDB = Depends(get_current_user)):
50+
def test_token(current_user: DBUser = Depends(get_current_user)):
5151
"""
5252
Test access token
5353
"""
5454
return current_user
5555

5656

57-
@router.post("/password-recovery/{username}", tags=["login"], response_model=Msg)
58-
def route_recover_password(username: str):
57+
@router.post("/password-recovery/{email}", tags=["login"], response_model=Msg)
58+
def recover_password(email: str, db: Session = Depends(get_db)):
5959
"""
6060
Password Recovery
6161
"""
62-
bucket = get_default_bucket()
63-
user = get_user(bucket, username)
62+
user = crud_user.get_by_email(db, email=email)
6463

6564
if not user:
6665
raise HTTPException(
6766
status_code=404,
6867
detail="The user with this username does not exist in the system.",
6968
)
70-
password_reset_token = generate_password_reset_token(username)
69+
password_reset_token = generate_password_reset_token(email=email)
7170
send_reset_password_email(
72-
email_to=user.email, username=username, token=password_reset_token
71+
email_to=user.email, email=email, token=password_reset_token
7372
)
7473
return {"msg": "Password recovery email sent"}
7574

7675

7776
@router.post("/reset-password/", tags=["login"], response_model=Msg)
78-
def route_reset_password(token: str, new_password: str):
77+
def reset_password(token: str, new_password: str, db: Session = Depends(get_db)):
7978
"""
8079
Reset password
8180
"""
82-
username = verify_password_reset_token(token)
83-
if not username:
81+
email = verify_password_reset_token(token)
82+
if not email:
8483
raise HTTPException(status_code=400, detail="Invalid token")
85-
bucket = get_default_bucket()
86-
user = get_user(bucket, username)
84+
user = crud_user.get_by_email(db, email=email)
8785
if not user:
8886
raise HTTPException(
8987
status_code=404,
9088
detail="The user with this username does not exist in the system.",
9189
)
92-
elif not check_if_user_is_active(user):
90+
elif not crud_user.is_active(user):
9391
raise HTTPException(status_code=400, detail="Inactive user")
94-
user_in = UserInUpdate(name=username, password=new_password)
95-
user = update_user(bucket, user_in)
92+
hashed_password = get_password_hash(new_password)
93+
user.hashed_password = hashed_password
94+
db.add(user)
95+
db.commit()
9696
return {"msg": "Password updated successfully"}

0 commit comments

Comments
 (0)