Browse Source

init router, config, crud, db_base, db_session, models, schemas for database

chenhaiyang 4 years ago
parent
commit
37edc9a468

+ 1 - 1
alembic.ini

@@ -35,7 +35,7 @@ script_location = alembic
 # are written from script.py.mako
 # output_encoding = utf-8
 
-sqlalchemy.url = driver://user:pass@localhost/dbname
+# sqlalchemy.url = driver://user:pass@localhost/dbname
 
 
 [post_write_hooks]

+ 11 - 0
app/api/dependencies/db.py

@@ -0,0 +1,11 @@
+from typing import Generator
+
+from app.db.session import SessionLocal
+
+
+def get_db() -> Generator:
+    db = SessionLocal()
+    try:
+        yield db
+    finally:
+        db.close()

+ 35 - 0
app/api/routers/item.py

@@ -0,0 +1,35 @@
+from typing import Any, List
+
+from fastapi import APIRouter, Depends
+from sqlalchemy.orm import Session
+
+from app import crud, schemas
+from app.api.dependencies.db import get_db
+
+router = APIRouter()
+
+
+@router.get("/", response_model=List[schemas.Item])
+def read_items(
+        db: Session = Depends(get_db),
+        skip: int = 0,
+        limit: int = 100,
+) -> Any:
+    """
+    Retrieve items.
+    """
+    items = crud.item.get_multi(db, skip=skip, limit=limit)
+    return items
+
+
+@router.post("/", response_model=schemas.Item)
+def create_item(
+        *,
+        db: Session = Depends(get_db),
+        item_in: schemas.ItemCreate,
+) -> Any:
+    """
+    Create new item.
+    """
+    item = crud.item.create_with_owner(db=db, obj_in=item_in)
+    return item

+ 0 - 5
app/core/config.py

@@ -1,16 +1,11 @@
 # -*- coding: utf-8 -*-
 
-import secrets
 from typing import Any, Dict, Optional
 
 from pydantic import AnyHttpUrl, BaseSettings, DirectoryPath, PostgresDsn, SecretStr, validator
 
 
 class Settings(BaseSettings):
-    API_V1_STR: str = '/api/v1'
-    SECRET_KEY: str = secrets.token_urlsafe(32)
-    # 60 minutes * 24 hours * 8 days = 8 days
-    ACCESS_TOKEN_MINUTES: int = 60 * 24 * 8
     # SERVER_NAME: str
     # SERVER_HOST: AnyHttpUrl
 

+ 9 - 0
app/crud/__init__.py

@@ -0,0 +1,9 @@
+from .crud_item import item
+
+# For a new basic set of CRUD operations you could just do
+
+# from .base import CRUDBase
+# from app.models.item import Item
+# from app.schemas.item import ItemCreate, ItemUpdate
+
+# item = CRUDBase[Item, ItemCreate, ItemUpdate](Item)

+ 66 - 0
app/crud/base.py

@@ -0,0 +1,66 @@
+from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union
+
+from fastapi.encoders import jsonable_encoder
+from pydantic import BaseModel
+from sqlalchemy.orm import Session
+
+from app.db.base_class import Base
+
+ModelType = TypeVar("ModelType", bound=Base)
+CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
+UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)
+
+
+class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
+    def __init__(self, model: Type[ModelType]):
+        """
+        CRUD object with default methods to Create, Read, Update, Delete (CRUD).
+
+        **Parameters**
+
+        * `model`: A SQLAlchemy model class
+        * `schema`: A Pydantic model (schema) class
+        """
+        self.model = model
+
+    def get(self, db: Session, id: Any) -> Optional[ModelType]:
+        return db.query(self.model).filter(self.model.id == id).first()
+
+    def get_multi(
+            self, db: Session, *, skip: int = 0, limit: int = 100
+    ) -> List[ModelType]:
+        return db.query(self.model).offset(skip).limit(limit).all()
+
+    def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType:
+        obj_in_data = jsonable_encoder(obj_in)
+        db_obj = self.model(**obj_in_data)  # type: ignore
+        db.add(db_obj)
+        db.commit()
+        db.refresh(db_obj)
+        return db_obj
+
+    def update(
+            self,
+            db: Session,
+            *,
+            db_obj: ModelType,
+            obj_in: Union[UpdateSchemaType, Dict[str, Any]]
+    ) -> ModelType:
+        obj_data = jsonable_encoder(db_obj)
+        if isinstance(obj_in, dict):
+            update_data = obj_in
+        else:
+            update_data = obj_in.dict(exclude_unset=True)
+        for field in obj_data:
+            if field in update_data:
+                setattr(db_obj, field, update_data[field])
+        db.add(db_obj)
+        db.commit()
+        db.refresh(db_obj)
+        return db_obj
+
+    def remove(self, db: Session, *, id: int) -> ModelType:
+        obj = db.query(self.model).get(id)
+        db.delete(obj)
+        db.commit()
+        return obj

+ 30 - 0
app/crud/crud_item.py

@@ -0,0 +1,30 @@
+from typing import List
+
+from fastapi.encoders import jsonable_encoder
+from sqlalchemy.orm import Session
+
+from app.crud.base import CRUDBase
+from app.models.item import Item
+from app.schemas.item import ItemCreate, ItemUpdate
+
+
+class CRUDItem(CRUDBase[Item, ItemCreate, ItemUpdate]):
+    def create_with_owner(
+            self, db: Session, *, obj_in: ItemCreate
+    ) -> Item:
+        obj_in_data = jsonable_encoder(obj_in)
+        db_obj = self.model(**obj_in_data)
+        db.add(db_obj)
+        db.commit()
+        db.refresh(db_obj)
+        return db_obj
+
+    def get_multi_by_owner(
+            self, db: Session, *, owner_id: int, skip: int = 0, limit: int = 100
+    ) -> List[Item]:
+        return (
+            db.query(self.model).filter(Item.owner_id == owner_id).offset(skip).limit(limit).all()
+        )
+
+
+item = CRUDItem(Item)

+ 4 - 0
app/db/base.py

@@ -0,0 +1,4 @@
+# Import all the models, so that Base has them before being
+# imported by Alembic
+from app.db.base_class import Base  # noqa
+from app.models.item import Item  # noqa

+ 14 - 0
app/db/base_class.py

@@ -0,0 +1,14 @@
+from typing import Any
+
+from sqlalchemy.ext.declarative import as_declarative, declared_attr
+
+
+@as_declarative()
+class Base:
+    id: Any
+    __name__: str
+    # Generate __tablename__ automatically
+
+    @declared_attr
+    def __tablename__(self) -> str:
+        return self.__name__.lower()

+ 7 - 0
app/db/session.py

@@ -0,0 +1,7 @@
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+
+from app.core.config import settings
+
+engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
+SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

+ 3 - 2
app/main.py

@@ -7,18 +7,19 @@ import uvicorn
 from fastapi import FastAPI
 from loguru import logger
 
-from app.api.routers import targets, equipment, space
+from app.api.routers import targets, equipment, space, item
 from app.core.config import settings
 from app.core.logger import InterceptHandler
 
 logging.getLogger().handlers = [InterceptHandler()]
 logger.add(Path(settings.LOGS_DIR, 'env_fastapi.log'), level='INFO', rotation='00:00', encoding='utf-8')
 
-app = FastAPI()
+app = FastAPI(title=settings.PROJECT_NAME)
 
 app.include_router(targets.router, prefix='/target')
 app.include_router(space.router, prefix='/room')
 app.include_router(equipment.router, prefix='/equip')
+app.include_router(item.router, prefix='/item', tags=['items'])
 
 
 @app.get('/settings')

+ 1 - 0
app/models/__init__.py

@@ -0,0 +1 @@
+from .item import Item

+ 9 - 0
app/models/item.py

@@ -0,0 +1,9 @@
+from sqlalchemy import Column, Integer, String
+
+from app.db.base_class import Base
+
+
+class Item(Base):
+    id = Column(Integer, primary_key=True, index=True)
+    title = Column(String, index=True)
+    description = Column(String, index=True)

+ 1 - 0
app/schemas/__init__.py

@@ -0,0 +1 @@
+from .item import Item, ItemCreate, ItemInDB, ItemUpdate

+ 32 - 0
app/schemas/item.py

@@ -0,0 +1,32 @@
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+class ItemBase(BaseModel):
+    id: int
+    title: Optional[str] = None
+    description: Optional[str] = None
+
+
+class ItemCreate(ItemBase):
+    title: str
+
+
+class ItemUpdate(ItemBase):
+    pass
+
+
+class ItemInDBBase(ItemBase):
+    id: int
+
+    class Config:
+        orm_mode = True
+
+
+class Item(ItemInDBBase):
+    pass
+
+
+class ItemInDB(ItemInDBBase):
+    pass