В этом посте в блоге мы создадим простой Fastapi Приложение с нуля. Это может служить хорошей отправной точкой для небольших до средних проектов.
Содержание серии 📖.
- Часть 1: Укладка фундамента (этот пост)
- Часть 2 : Миграция
- Часть 3 : Докереновать
Что мы будем покрывать в этом посте? 📝
- Генерировать базовый проект с Поэзия Отказ
- Установить Fastapi. , SQLalchemy и другие зависимости.
- Создайте необходимые файлы, которые будут служить основой приложения
Прежде чем начать … ⚠ ️
Я собираюсь сделать следующие допущения:
- что у вас есть базовое понимание Python и Python типы ;
- Что у вас уже установлено ниже:
- Python 3.6+ (я использую Python 3.8)
- PostgreSQL ; а также
- Поэзия
Без дальнейшего ADO давайте начнем 🙂
Создать базовый проект с поэзией 🏃
Откройте терминал и введите команду ниже.
poetry new app
После запуска вышеуказанной команды новый каталог называется приложение
был создан. Давайте идти вперед и CD
в этот каталог сейчас.
cd app
Структура каталогов должна выглядеть ниже.
. ├── app │ └── __init__.py ├── pyproject.toml ├── README.rst └── tests ├── __init__.py └── test_app.py
Давайте быстро перейдем на то, что у нас здесь.
приложение
Каталог – наш главный пакет Python.
Каталог с __init__.py
Файл в нем считается пакет в Python. Любой .py
Файлы, которые мы добавляем к этому каталогу, будут рассматриваться модули этого пакета. Обычно этот файл пуст, но в этом случае поэзия прошла впереди и добавила __Version__
Отказ
pyproject.toml
Файл – это то, куда будут добавлены все наши зависимости. Позже, когда мы начнем устанавливать наши зависимости, вы заметите Поэзия. Блок
Файл будет создан, больше на что позже.
Readme
Файл можно использовать для добавления подробной информации о проекте или любых полезных инструкциях, которые помогут другим разработчикам, работающим над проектом. Лично я предпочитаю написание документации в Markdown над реструктуреннымтекстом, поэтому я пойду вперед и переименую Readme.rst
к Readme.md.md
. Не стесняйтесь делать то же самое или оставьте его как есть.
mv README.rst README.md
Наконец, у нас есть наш тесты
каталог, который содержит все тесты на единицу.
Установите fastapi и другие зависимости 📦
В этом разделе мы установим только необходимые зависимости, чтобы получить базовый Crud ( C rete, r EAD, u pdate, d lete) Приложение.
Что мы будем устанавливать?
- Fastapi – это выходит, не говорящая 🙂
- SQLalchemy Объект реляционный Mapper (ORM)
- psycopg2-двоичный PostgreSQL адаптер базы данных
- Увикурн Молния-быстрый ASGI Server
poetry add fastapi sqlalchemy psycopg2-binary uvicorn
Мы также установим следующие зависимости разработки, в основном для поддержания качества кода и для тестирования.
- pteest Тестирование Рамки
- Marpy Статический тип проверки для Python
- SQLALCHEMY-STUBS Mypy Plub-In и типа заглушки для SQLALCHEMY
- Flake8 для кода перекликание
- Autoflake Удаляет неиспользуемый импорт и неиспользуемые переменные
- Исторт Сортировать импорт заявления
- черный Формирование кода Python
poetry add -D mypy sqlalchemy-stubs flake8 autoflake isort black
Примечание: мы не включали pteest
В приведенной выше команде, как это обычно устанавливается по умолчанию при использовании поэзии для создания нового проекта.
На данный момент ничего не изменилось в нашей структуре каталогов, но вы заметите, что pyproject.toml
Файл был обновлен и новый Поэзия. Блок
файл был создан. Поэзия. Блок
Файл блокирует установленные зависимости к определенной версии. Это особенно полезно, когда множественные разработчики работают над тем же проектом, чтобы убедиться, что все используют одни и те же версии каждого пакета.
Чтобы повторить нашу структуру каталогов должен выглядеть что-то подобное сейчас.
. ├── app │ └── __init__.py ├── poetry.lock ├── pyproject.toml ├── README.md └── tests ├── __init__.py └── test_app.py
Добавить файлы проекта 📄
В этом разделе мы начнем добавить файлы, которые состоят на нашем приложении.
Первый файл, который мы создадим, это main.py
Файл, он будет служить точкой входа на наше приложение и дом всех наших маршрутов. Давайте создадим этот файл сейчас под приложение
Пакетный каталог.
main.py
приложение/main.py.
from fastapi import FastAPI app = FastAPI() @app.get("/") def index(): return {"message": "Hello world!"}
На данный момент у нас на самом деле есть базовое приложение, которое мы можем запустить. Если мы вернемся к нашему терминалу и запустите следующие команды.
poetry shell uvicorn app.main:app
Кончик: Если вы хотите, чтобы сервер перезагрузился в файл изменения, вы можете использовать наград
Флаг, как так uvicorn app.main: App --reload
Теперь, если мы отправимся в браузер и ударим http://127.0.0.1:8000. Мы будем приветствовать {«сообщение»: «Hello World!»}
Отказ Fastapi также дает нам документацию API из коробки, так что если вы сейчас перейдите к http://127.0.0.1:8000/docs. Теперь вы увидите UI Swagger. Довольно удивительно, верно! 🤘
Мы вернемся позже и обновим main.py
файл, но на данный момент давайте ударим Ctrl + C.
В терминале остановить UVicorn и продолжать добавлять остальные наши файлы.
Далее давайте создадим db.py
в том же каталоге. Этот файл будет содержать нашу сессию базы данных и базовый класс, от которого все модели будут простираться.
db.py
приложение/db.py.
from typing import Any from sqlalchemy import create_engine from sqlalchemy.ext.declarative import as_declarative from sqlalchemy.orm import sessionmaker from .config import settings engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) @as_declarative() class Base: id: Any
Информация: Вы можете прочитать больше о SessionMaker
Функция здесь и as_declarative
декоратор здесь Отказ
Возможно, вы заметили, что мы импортируем настройки
от конфигурация
Но мы еще не создали этот файл еще, так что давайте сделаем это сейчас.
config.py
приложение/config.py.
from typing import Any, Dict, Optional from pydantic import BaseSettings, PostgresDsn, validator class Settings(BaseSettings): POSTGRES_SERVER: str POSTGRES_USER: str POSTGRES_PASSWORD: str POSTGRES_DB: str SQLALCHEMY_DATABASE_URI: Optional[PostgresDsn] = None @validator("SQLALCHEMY_DATABASE_URI", pre=True) def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any: if isinstance(v, str): return v return PostgresDsn.build( scheme="postgresql", user=values.get("POSTGRES_USER"), password=values.get("POSTGRES_PASSWORD"), host=values.get("POSTGRES_SERVER"), path=f"/{values.get('POSTGRES_DB') or ''}", ) class Config: case_sensitive = True env_file = ".env" settings = Settings()
Информация: при загрузке конфигураций из .env
Файл . Python-Dotenv Пакет обязателен.
Здесь мы используем Pydantics Управление настройками Отказ По умолчанию Базовые базовы
Класс попытается прочитать переменные среды, установленные на уровне системы, используя Os.environ Отказ Однако в нашем случае мы указываем, что мы хотели бы прочитать переменные среды для чтения из .env
файл. Pydantic полагается на Python-Dotenv Пакет для достижения этого, давайте добавим его в зависимостью сейчас.
poetry add python-dotenv
И теперь мы создадим .env
Файл в корне в каталоге проекта.
.env.env.
.env.env.
# PostgreSQL POSTGRES_SERVER=localhost POSTGRES_USER=postgres POSTGRES_PASSWORD=password POSTGRES_DB=app
Обязательно отредактируйте этот файл, чтобы отразить вашу настройку.
.gitignore.
С .env
Файл может содержать конфиденциальную информацию, которую мы не хотели бы совершить это для контроля версий. Так что теперь, вероятно, будет хорошее время, чтобы добавить .gitignore
Файл на наш проект. Мы скопируем Python .gitignore
Шаблон, предоставленный GitHub здесь Отказ .gitignore.
wget https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore mv Python.gitignore .gitignore
Далее мы создадим Models.py
и stchemas.py
файл.
models.py
Models.py
Файл будет содержать все наши модели, которые простираются от SQLALCHEMY База
класс, который мы определены в db.py
Мы создадим этот файл сейчас на примере Пользователь
модель.
приложение/модели
from uuid import uuid4 from sqlalchemy import Column, String, Text from sqlalchemy.dialects.postgresql import UUID from .db import Base class Post(Base): __tablename__ = "posts" id = Column(UUID(as_uuid=True), primary_key=True, index=True, default=uuid4) title = Column(String) body = Column(Text)
schemas.py
Давайте создадим stchemas.py
файл сейчас. Этот файл будет содержать все наши Пидантические модели Отказ Под капотом Fastapi использует эти модели для проверки корпуса входящего запроса, анализировать тело ответа и генерировать Автоматические документы для нашего API. Действительно круто, по крайней мере, я так думаю! 👌
Приложение/Schemas.py.
from typing import Optional from pydantic import BaseModel, UUID4 # Shared properties class PostBase(BaseModel): title: Optional[str] = None body: Optional[str] = None # Properties to receive via API on creation class PostCreate(PostBase): title: str body: str # Properties to receive via API on update class PostUpdate(PostBase): pass class PostInDBBase(PostBase): id: Optional[UUID4] = None class Config: orm_mode = True # Additional properties to return via API class Post(PostInDBBase): pass # Additional properties stored in DB class PostInDB(PostInDBBase): pass
Последний файл, который мы создадим сейчас, это Действия .py
файл. Этот файл будет содержать все наши случаи или действия или действия, которые будут выполнены, такие как операции CRUD.
actions.py
Приложение/Action.py.
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union from fastapi.encoders import jsonable_encoder from pydantic import UUID4, BaseModel from sqlalchemy.orm import Session from . import schemas from .db import Base from .models import Post # Define custom types for SQLAlchemy model, and Pydantic schemas ModelType = TypeVar("ModelType", bound=Base) CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel) UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel) class BaseActions(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): def __init__(self, model: Type[ModelType]): """Base class that can be extend by other action classes. Provides basic CRUD and listing operations. :param model: The SQLAlchemy model :type model: Type[ModelType] """ self.model = model def get_all( self, db: Session, *, skip: int = 0, limit: int = 100 ) -> List[ModelType]: return db.query(self.model).offset(skip).limit(limit).all() def get(self, db: Session, id: UUID4) -> Optional[ModelType]: return db.query(self.model).filter(self.model.id == id).first() 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: UUID4) -> ModelType: obj = db.query(self.model).get(id) db.delete(obj) db.commit() return obj class PostActions(BaseActions[Post, schemas.PostCreate, schemas.PostUpdate]): """Post actions with basic CRUD operations""" pass post = PostActions(Post)
Перед возвращением и обновлением нашего main.py
Файл, давайте просмотрим нашу конечную структуру каталогов.
. ├── app │ ├── actions.py │ ├── config.py │ ├── __init__.py │ ├── main.py │ ├── models.py │ └── schemas.py ├── poetry.lock ├── pyproject.toml ├── README.md └── tests ├── __init__.py └── test_app.py
Давайте обновим наше main.py
Файл сейчас и подключите все точки.
Обновить main.py.
приложение/main.py.
from typing import Any, List from fastapi import Depends, FastAPI, HTTPException from pydantic import UUID4 from sqlalchemy.orm import Session from starlette.status import HTTP_201_CREATED, HTTP_404_NOT_FOUND from . import actions, models, schemas from .db import SessionLocal, engine # Create all tables in the database. # Comment this out if you using migrations. models.Base.metadata.create_all(bind=engine) app = FastAPI() # Dependency to get DB session. def get_db(): try: db = SessionLocal() yield db finally: db.close() @app.get("/") def index(): return {"message": "Hello world!"} @app.get("/posts", response_model=List[schemas.Post], tags=["posts"]) def list_posts(db: Session = Depends(get_db), skip: int = 0, limit: int = 100) -> Any: posts = actions.post.get_all(db=db, skip=skip, limit=limit) return posts @app.post( "/posts", response_model=schemas.Post, status_code=HTTP_201_CREATED, tags=["posts"] ) def create_post(*, db: Session = Depends(get_db), post_in: schemas.PostCreate) -> Any: post = actions.post.create(db=db, obj_in=post_in) return post @app.put( "/posts/{id}", response_model=schemas.Post, responses={HTTP_404_NOT_FOUND: {"model": schemas.HTTPError}}, tags=["posts"], ) def update_post( *, db: Session = Depends(get_db), id: UUID4, post_in: schemas.PostUpdate, ) -> Any: post = actions.post.get(db=db, id=id) if not post: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Post not found") post = actions.post.update(db=db, db_obj=post, obj_in=post_in) return post @app.get( "/posts/{id}", response_model=schemas.Post, responses={HTTP_404_NOT_FOUND: {"model": schemas.HTTPError}}, tags=["posts"], ) def get_post(*, db: Session = Depends(get_db), id: UUID4) -> Any: post = actions.post.get(db=db, id=id) if not post: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Post not found") return post @app.delete( "/posts/{id}", response_model=schemas.Post, responses={HTTP_404_NOT_FOUND: {"model": schemas.HTTPError}}, tags=["posts"], ) def delete_post(*, db: Session = Depends(get_db), id: UUID4) -> Any: post = actions.post.get(db=db, id=id) if not post: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Post not found") post = actions.post.remove(db=db, id=id) return post
Наконец, если мы снова запустим сервер и нажмите http://127.0.0.1:8000/docs. Теперь у нас есть основные API, которые могут выполнять операции CRUD на нашей почве. 🚀
uvicorn app.main:app
Заключение 💡.
Если вы сделали это далеко, хорошо сделано! 👍.
Мы создали простое приложение, которое может служить хорошей отправной точкой для небольших до средних проектов. Есть еще ряд вещей, которые мы можем включить в этот базовый проект, такой как миграция или добавление докера в наш стек. ( Подсказка: мы осмотрим это в будущих постах, оставайтесь на улице 😉 )
Последний код для этого поста можно найти на Github Отказ
Если вам понравилось прочитать эту статью и хотел бы оставаться настроенными для дальше, или просто хочу подключиться, следуйте за мной в Twitter @alexvanzyl Отказ
Оригинал: “https://dev.to/alexvanzyl/fastapi-simple-application-structure-from-scratch-2mem”