잘 안되서 고생을 좀 했다.(아직 해결은 못한 부분이 있지만.. 정리 된거만 블로깅)
우선 코드스테이츠에서 토크세션을 진행했는데 "출판사가 OK하는 글쓰기" 를 주제로 최현우 프로 에디터님이 주관하여 세션을 진행하셨다.
간단히 요약하고 블로깅을 시작해보겠습니다.
단문으로 글쓰기
첫문장에는 정의로 정어주면 좋다 ex) string은 문자열을 담는 자료형이다.
오늘 배운내용은 최대한 빨리 블로깅을 하는게 좋다.(나중에 블로깅하면 내가 왜 이렇게 코드를 짯는지 잘 모를수 있다.)
번역투로 글쓰기
poetry add fastapi uvicorn sqlalchemy pymysql async-generator async-exit-stack
FastAPI는 레퍼런스는 많지는 않지만 , 공식문서가 엄청나게 자세하게 되어있어 좋은듯 하다.
.
└── sql_app
├── init.py
├── crud.py
├── database.py
├── main.py
├── models.py
└── schemas.py
공식문서 대로 파일을 이렇게 만들었고, __init.py
폴더의 내용은 비었있지만 sql_app이 python파일이 있는 패키지라고 알려주는 파일입니다.(비어있다고 삭제하면 x)
공식문서에서는 sqllite와 postgresql이 예시로 되어있지만 postgresql 예시와 비슷하게 하면 동작은 된다.
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:1234@localhost:3306/test_db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
SQLALCHEMY_DATABASE_URL은 mysql을 사용할 경우 "mysql+pymysql://root:1234@localhost:3306/test_db"
이렇게 작성하면 되고
root: username /1234: 데이터베이스 비밀번호 / localhost: hostname /
3306: port test_db: 스키마 이름
실제 사용시에는 환경변수로 설정(보안목적)
engin변수는 sqlalchemy engine을 만드는것이며 나중에 main.py 폴더에서 사용할 예정
SessionLocal의 역할은 잘 모르겠지만 SessionLocal 클래스의 인스턴스를 생성하면 실제 데이터베이스 접근해서 CRUD를 할 수 있는거 같음.
Base는 나중에 이 클래스에서 상속하여 각 데이터베이스 모델 또는 클래스 (ORM 모델)를 만듭니다.
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from .database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String(20), unique=True, index=True)
hashed_password = Column(String(100))
is_active = Column(Boolean, default=True)
items = relationship("Item", back_populates="owner")
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(20), index=True)
description = Column(String(20), index=True)
owner_id = Column(Integer, ForeignKey("users.id"))
owner = relationship("User", back_populates="items")
테이블 관계에 대해서는 나중에 블로깅을 할 예정이고(아직 잘몰라서 ..) Item에 owner_id가 있는걸 보아 1(User):N(Item)관계인걸 알 수 있다.
from typing import List, Optional
from pydantic import BaseModel # 객체 타입설정
class ItemBase(BaseModel):
title: str
description: Optional[str] = None
class ItemCreate(ItemBase):
pass
class Item(ItemBase):
id: int
owner_id: int
class Config:
orm_mode = True
class UserBase(BaseModel):
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
is_active: bool
class Config:
orm_mode = True
schemas.py는 테이블의 타입을 설정 해놓은 파일
공식문서의 crud.py에서 create_user
코드만 가져와서 살펴보기
from sqlalchemy.orm import Session
from . import models, schemas
import sql_app.database
# import sql_app.models # 절대경로로 불러오는방법
# import sql_app.schemas # 절대경로로 불러오는방법
def create_user(db: Session, user: schemas.UserCreate):
fake_hashed_password = user.password + "notreallyhashed"
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
파라미터로 받은 db를 활용해서 실제 쿼리를 날리고 user는 페이로드값이라고 보면된다.
(실제 함수 실행부분은 main.py에서 진행) user의 타입은 schemas.UserCreate라고 적혀있는데
schemas.py폴더에 있는 UserCreate를 가져와서 타입핑(타입힌팅)을 한거라고 보면된다.
(email: str, password: str 이렇게 설정이 되어 있음)
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
models에 있는 User테이블을 불러와서 (제일 최상단에 임포트가 되어 있음) 바디값을 넣어주면된다.
(email(실제 테이블컬럼명) = 데이터, hashed_password= 데이터)
데이터를 insert 하는 과정을 보면 db.add(db_user => 인스턴스 변수를 넣어주고)
db.commit()
을 해야 데이터베이스에 적용이 된다. db.refresh(db_user)
마지막으로 이렇게 해줘야지 인스턴스변수에 변경된 내용이 적용된다.
여기서 주의할 점은 패스워드 해싱화 할때는 공식문서에 내용처럼 하면 안되고 암호화 모듈을 사용하던지 자신이 직접 암호화를 해야된다.
email_update = db.query(models.User).filter_by(email = 'kim').first()
email_update.email = 'hyunjin'
db.add(email_update)
db.commit()
email_delete = db.query(models.User).filter_by(email = 'hyunjon').first()
db.delete(email_delete)
db.commit()
db.query(models.User).all()
User 테이블 모든 데이터 가져오기
# for문을 돌려서 data확인
for data in db.query(models.User).all():
print(data.email, data.password)
db.query(models.User).filter(models.User.email == 'email')
필터링
filter(models.User.email == 'email', models.User.password == '1234')
여러가지 조건을 넣어도 된다.db.query(models.User).filter(models.User.email == 'email').first()
db.query(models.User).filter_by(email='kims', hashed_password="a").first()
db.query(models.User).limit(10).all()
limit 10개만 가져오기
db.query(models.User).offset(5).limit(10).all()
=> 6번부터 10개 가져오기(6,7,8,9,10,11,12,13,14,15)여기까지만 하고 나중에 정리
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
from sqlalchemy.orm import Session, sessionmaker
import sqlalchemy.orm.session
import sql_app.models
import sql_app.database
import sql_app.schemas
import sql_app.crud
sql_app.models.Base.metadata.create_all(bind=sql_app.database.engine)
app = FastAPI()
# Dependency
def get_db():
db = sql_app.database.SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/users",response_model=sql_app.schemas.User)
def create_user2(user:sql_app.schemas.UserCreate, db: Session = Depends(get_db)): # 무조건 typing을 해줘야 에러가 발생하지 않음
db_user = sql_app.crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return sql_app.crud.create_user(db=db, user=user)
일부분만 공식문서에서 가져와서 연습을 해보았다. main.py 설명은 내일로.....
TODO:
sqlalchemy 관계설정 방법 보기
Alembic 마이그레이션 방법 알아보기 (오늘 이게 잘안되서 고생 ㅠ)
GO lang 기본문법 공부