From 30ba804f40d6bd9ce8fe940c7cd0817d1fa30923 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 02:55:29 +0300 Subject: [PATCH 01/20] =?UTF-8?q?=D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=20main.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/main.py | 182 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 172 insertions(+), 10 deletions(-) diff --git a/app/main.py b/app/main.py index 5596b44..004715a 100644 --- a/app/main.py +++ b/app/main.py @@ -1,16 +1,178 @@ -# This is a sample Python script. +from typing import List +from fastapi import FastAPI, HTTPException +from sqlalchemy import create_engine, Column, Integer, String, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, relationship +from pydantic import BaseModel -# Press Shift+F10 to execute it or replace it with your code. -# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. +app = FastAPI() +# Создание базовой модели +Base = declarative_base() -def print_hi(name): - # Use a breakpoint in the code line below to debug your script. - print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint. +# Модель студента +class Student(Base): + __tablename__ = 'students' + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False) + group_id = Column(Integer, ForeignKey('groups.id')) + group = relationship('Group', back_populates='students') +# Модель группы +class Group(Base): + __tablename__ = 'groups' + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False) + students = relationship('Student', back_populates='group') -# Press the green button in the gutter to run the script. -if __name__ == '__main__': - print_hi('PyCharm') +# Модель для создания студента +class StudentCreate(BaseModel): + name: str + group_id: int -# See PyCharm help at https://www.jetbrains.com/help/pycharm/ +# Модель для создания группы +class GroupCreate(BaseModel): + name: str + +# Модель для ответа с информацией о студенте +class StudentResponse(BaseModel): + id: int + name: str + group_id: int + +# Модель для ответа с информацией о группе +class GroupResponse(BaseModel): + id: int + name: str + students: List[StudentResponse] + +# Подключение к базе данных +SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" +engine = create_engine(SQLALCHEMY_DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Создание таблиц в базе данных +Base.metadata.create_all(bind=engine) + +# API эндпоинт для создания студента +@app.post("/students/") +def create_student(student: StudentCreate): + db = SessionLocal() + db_student = Student(name=student.name, group_id=student.group_id) + db.add(db_student) + db.commit() + db.refresh(db_student) + return {"id": db_student.id, "name": db_student.name, "group_id": db_student.group_id} + +# API эндпоинт для создания группы +@app.post("/groups/") +def create_group(group: GroupCreate): + db = SessionLocal() + db_group = Group(name=group.name) + db.add(db_group) + db.commit() + db.refresh(db_group) + return {"id": db_group.id, "name": db_group.name} + +# API эндпоинт для получения информации о студенте по его id +@app.get("/students/{student_id}") +def get_student(student_id: int): + db = SessionLocal() + student = db.query(Student).filter(Student.id == student_id).first() + if not student: + raise HTTPException(status_code=404, detail="Student not found") + return {"id": student.id, "name": student.name, "group_id": student.group_id} + +# API эндпоинт для получения информации о группе по ее id +@app.get("/groups/{group_id}") +def get_group(group_id: int): + db = SessionLocal() + group = db.query(Group).filter(Group.id == group_id).first() + if not group: + raise HTTPException(status_code=404, detail="Group not found") + return {"id": group.id, "name": group.name} + +# API эндпоинт для удаления студента +@app.delete("/students/{student_id}") +def delete_student(student_id: int): + db = SessionLocal() + student = db.query(Student).filter(Student.id == student_id).first() + if not student: + raise HTTPException(status_code=404, detail="Student not found") + db.delete(student) + db.commit() + return {"message": "Student deleted"} + +# API эндпоинт для удаления группы +@app.delete("/groups/{group_id}") +def delete_group(group_id: int): + db = SessionLocal() + group = db.query(Group).filter(Group.id == group_id).first() + if not group: + raise HTTPException(status_code=404, detail="Group not found") + db.delete(group) + db.commit() + return {"message": "Group deleted"} + +# API эндпоинт для получения списка студентов +@app.get("/students/") +def get_students(): + db = SessionLocal() + students = db.query(Student).all() + return students + +# API эндпоинт для получения списка групп +@app.get("/groups/") +def get_groups(): + db = SessionLocal() + groups = db.query(Group).all() + return groups + +# API эндпоинт для добавления студента в группу +@app.post("/groups/{group_id}/students/{student_id}") +def add_student_to_group(group_id: int, student_id: int): + db = SessionLocal() + group = db.query(Group).filter(Group.id == group_id).first() + student = db.query(Student).filter(Student.id == student_id).first() + if not group: + raise HTTPException(status_code=404, detail="Group not found") + if not student: + raise HTTPException(status_code=404, detail="Student not found") + student.group_id = group_id + db.commit() + return {"message": "Student added to group"} + +# API эндпоинт для удаления студента из группы +@app.delete("/groups/{group_id}/students/{student_id}") +def remove_student_from_group(group_id: int, student_id: int): + db = SessionLocal() + student = db.query(Student).filter(Student.id == student_id).first() + if not student: + raise HTTPException(status_code=404, detail="Student not found") + student.group_id = None + db.commit() + return {"message": "Student removed from group"} + +# API эндпоинт для получения всех студентов в группе +@app.get("/groups/{group_id}/students/") +def get_students_in_group(group_id: int): + db = SessionLocal() + group = db.query(Group).filter(Group.id == group_id).first() + if not group: + raise HTTPException(status_code=404, detail="Group not found") + students = group.students + return students + +# API эндпоинт для перевода студента из группы A в группу B +@app.put("/students/{student_id}/move/{group_id}") +def move_student(student_id: int, group_id: int): + db = SessionLocal() + student = db.query(Student).filter(Student.id == student_id).first() + group = db.query(Group).filter(Group.id == group_id).first() + if not student: + raise HTTPException(status_code=404, detail="Student not found") + if not group: + raise HTTPException(status_code=404, detail="Group not found") + student.group_id = group_id + db.commit() + return {"message": "Student moved to another group"} From c305b8de5bad6d629a14d022d7405da43c9a410a Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 02:55:48 +0300 Subject: [PATCH 02/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20.env.example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d386bd4 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +DB_USER=myuser +DB_PASSWORD=mypassword +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=mydatabase \ No newline at end of file From dba1dd6fa46b941045180e6313b73bab60534190 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 02:57:08 +0300 Subject: [PATCH 03/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20docker-compose.yml=20=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5e91efb --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.8' + +services: + db: + image: postgres + restart: always + environment: + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_DB: ${DB_NAME} + ports: + - ${DB_PORT}:5432 + + app: + build: + context: . + dockerfile: Dockerfile + restart: always + environment: + - SQLALCHEMY_DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@db/${DB_NAME} + ports: + - 8000:8000 + depends_on: + - db \ No newline at end of file From 57c94086932e5a0c1cb5327b67f0beace2a984f4 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 02:58:06 +0300 Subject: [PATCH 04/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=20Dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1bbf2f4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.9-slim-buster +WORKDIR /app +COPY requirements.txt . +RUN pip install -r requirements.txt +COPY . . +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] From debc6143d99fe3d59ece41f63657410e697ae397 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 02:58:59 +0300 Subject: [PATCH 05/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20requirements.txt=20=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a544bf5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi +uvicorn +sqlalchemy +databases[sqlite] +python-dotenv \ No newline at end of file From fb1c16a15b406a84a9ca31f8c67b4976f0b2d9b6 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 03:21:13 +0300 Subject: [PATCH 06/20] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 +- docker-compose.yml | 2 +- app/main.py => main.py | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename app/main.py => main.py (100%) diff --git a/Dockerfile b/Dockerfile index 1bbf2f4..b53838a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,4 +3,4 @@ WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +CMD ["uvicorn", "main:app", "--host", "127.0.0.1", "--port", "8080"] diff --git a/docker-compose.yml b/docker-compose.yml index 5e91efb..c2ae55f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,6 @@ services: environment: - SQLALCHEMY_DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@db/${DB_NAME} ports: - - 8000:8000 + - 8080:8000 depends_on: - db \ No newline at end of file diff --git a/app/main.py b/main.py similarity index 100% rename from app/main.py rename to main.py From 5a6ae7c2edea001432b6cb0f868f2a38f8843a34 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 03:47:47 +0300 Subject: [PATCH 07/20] =?UTF-8?q?=D0=B2=D0=BD=D0=B5=D1=81=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BD=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D0=B8?= =?UTF-8?q?=D1=85=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 1 - Dockerfile | 2 +- docker-compose.yml | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index d386bd4..91a690a 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,4 @@ DB_USER=myuser -DB_PASSWORD=mypassword DB_HOST=localhost DB_PORT=5432 DB_NAME=mydatabase \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index b53838a..1bbf2f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,4 +3,4 @@ WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . -CMD ["uvicorn", "main:app", "--host", "127.0.0.1", "--port", "8080"] +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.yml b/docker-compose.yml index c2ae55f..26963b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: restart: always environment: POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_PASSWORD: 12345 POSTGRES_DB: ${DB_NAME} ports: - ${DB_PORT}:5432 @@ -17,8 +17,8 @@ services: dockerfile: Dockerfile restart: always environment: - - SQLALCHEMY_DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@db/${DB_NAME} + - SQLALCHEMY_DATABASE_URL=postgresql://${DB_USER}:12345@db/${DB_NAME} ports: - - 8080:8000 + - 8000:8000 depends_on: - db \ No newline at end of file From d8bfd89d59efe9bd2a7d0974df14917eebf8755e Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 04:01:23 +0300 Subject: [PATCH 08/20] README.md --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f852b8..7676e1b 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,11 @@ $ .\myenv\Scripts\activate Так же установить пакеты: ```shell $ pip install fastapi uvicorn sqlalchemy databases[sqlite] python-dotenv -``` \ No newline at end of file +``` +Что бы собрать контейнер и запустить его воспользоваться командами: +```shell +$ docker-compose up --build +$ docker-compose up -d +``` + +P.s. Для запуска необходим установленный на пк Docker. \ No newline at end of file From 07810a292231c93331c6c468c53d8524969b2efc Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 23:23:54 +0300 Subject: [PATCH 09/20] =?UTF-8?q?=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20=D0=B2=20=D0=BE=D1=82=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/models.py diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..0850331 --- /dev/null +++ b/app/models.py @@ -0,0 +1,45 @@ +from typing import List +from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship +from pydantic import BaseModel + +# Создание базовой модели +Base = declarative_base() + +# Модель студента +class Student(Base): + __tablename__ = 'students' + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False) + group_id = Column(Integer, ForeignKey('groups.id')) + group = relationship('Group', back_populates='students') + +# Модель группы +class Group(Base): + __tablename__ = 'groups' + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False) + students = relationship('Student', back_populates='group') + +# Модель для создания студента +class StudentCreate(BaseModel): + name: str + group_id: int + +# Модель для создания группы +class GroupCreate(BaseModel): + name: str + +# Модель для ответа с информацией о студенте +class StudentResponse(BaseModel): + id: int + name: str + group_id: int + +# Модель для ответа с информацией о группе +class GroupResponse(BaseModel): + id: int + name: str + students: List[StudentResponse] + From 2fa84d9aa7a7a23e8b5d860f1b37bf62d6bab333 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 23:24:11 +0300 Subject: [PATCH 10/20] =?UTF-8?q?=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BA=20db=20=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=BE=D0=BC=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/database.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 app/database.py diff --git a/app/database.py b/app/database.py new file mode 100644 index 0000000..8bb2a05 --- /dev/null +++ b/app/database.py @@ -0,0 +1,10 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +# Подключение к базе данных +SQLALCHEMY_DATABASE_URL = "postgresql://:@:5432/" + +engine = create_engine(SQLALCHEMY_DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + + From 19ce417c7d63b0203360a75082c817a1ab876195 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 23:24:37 +0300 Subject: [PATCH 11/20] =?UTF-8?q?=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=20API=20?= =?UTF-8?q?=D1=8D=D0=BD=D0=B4=D0=BF=D0=BE=D0=B8=D0=BD=D1=82=D1=8B=20=D0=B2?= =?UTF-8?q?=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 app/routes.py diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..fcc90b7 --- /dev/null +++ b/app/routes.py @@ -0,0 +1,128 @@ +from fastapi import APIRouter, HTTPException +from app.database import SessionLocal, engine +from app.models import Student, Group, StudentCreate, GroupCreate, StudentResponse, GroupResponse + +router = APIRouter() + +# API эндпоинт для создания студента +@router.post("/students/") +def create_student(student: StudentCreate): + db = SessionLocal() + db_student = Student(name=student.name, group_id=student.group_id) + db.add(db_student) + db.commit() + db.refresh(db_student) + return {"id": db_student.id, "name": db_student.name, "group_id": db_student.group_id} + +# API эндпоинт для создания группы +@router.post("/groups/") +def create_group(group: GroupCreate): + db = SessionLocal() + db_group = Group(name=group.name) + db.add(db_group) + db.commit() + db.refresh(db_group) + return {"id": db_group.id, "name": db_group.name} + +# API эндпоинт для получения информации о студенте по его id +@router.get("/students/{student_id}") +def get_student(student_id: int): + db = SessionLocal() + student = db.query(Student).filter(Student.id == student_id).first() + if not student: + raise HTTPException(status_code=404, detail="Student not found") + return {"id": student.id, "name": student.name, "group_id": student.group_id} + +# API эндпоинт для получения информации о группе по ее id +@router.get("/groups/{group_id}") +def get_group(group_id: int): + db = SessionLocal() + group = db.query(Group).filter(Group.id == group_id).first() + if not group: + raise HTTPException(status_code=404, detail="Group not found") + return {"id": group.id, "name": group.name} + +# API эндпоинт для удаления студента +@router.delete("/students/{student_id}") +def delete_student(student_id: int): + db = SessionLocal() + student = db.query(Student).filter(Student.id == student_id).first() + if not student: + raise HTTPException(status_code=404, detail="Student not found") + db.delete(student) + db.commit() + return {"message": "Student deleted"} + +# API эндпоинт для удаления группы +@router.delete("/groups/{group_id}") +def delete_group(group_id: int): + db = SessionLocal() + group = db.query(Group).filter(Group.id == group_id).first() + if not group: + raise HTTPException(status_code=404, detail="Group not found") + db.delete(group) + db.commit() + return {"message": "Group deleted"} + +# API эндпоинт для получения списка студентов +@router.get("/students/") +def get_students(): + db = SessionLocal() + students = db.query(Student).all() + return students + +# API эндпоинт для получения списка групп +@router.get("/groups/") +def get_groups(): + db = SessionLocal() + groups = db.query(Group).all() + return groups + +# API эндпоинт для добавления студента в группу +@router.post("/groups/{group_id}/students/{student_id}") +def add_student_to_group(group_id: int, student_id: int): + db = SessionLocal() + group = db.query(Group).filter(Group.id == group_id).first() + student = db.query(Student).filter(Student.id == student_id).first() + if not group: + raise HTTPException(status_code=404, detail="Group not found") + if not student: + raise HTTPException(status_code=404, detail="Student not found") + student.group_id = group_id + db.commit() + return {"message": "Student added to group"} + +# API эндпоинт для удаления студента из группы +@router.delete("/groups/{group_id}/students/{student_id}") +def remove_student_from_group(group_id: int, student_id: int): + db = SessionLocal() + student = db.query(Student).filter(Student.id == student_id).first() + if not student: + raise HTTPException(status_code=404, detail="Student not found") + student.group_id = None + db.commit() + return {"message": "Student removed from group"} + +# API эндпоинт для получения всех студентов в группе +@router.get("/groups/{group_id}/students/") +def get_students_in_group(group_id: int): + db = SessionLocal() + group = db.query(Group).filter(Group.id == group_id).first() + if not group: + raise HTTPException(status_code=404, detail="Group not found") + students = group.students + return students + +# API эндпоинт для перевода студента из группы A в группу B +@router.put("/students/{student_id}/move/{group_id}") +def move_student(student_id: int, group_id: int): + db = SessionLocal() + student = db.query(Student).filter(Student.id == student_id).first() + group = db.query(Group).filter(Group.id == group_id).first() + if not student: + raise HTTPException(status_code=404, detail="Student not found") + if not group: + raise HTTPException(status_code=404, detail="Group not found") + student.group_id = group_id + db.commit() + return {"message": "Student moved to another group"} \ No newline at end of file From 1bbec193aea1611308149722ffcb580a9296eb1b Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 23:27:10 +0300 Subject: [PATCH 12/20] =?UTF-8?q?=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B6?= =?UTF-8?q?=D0=B0=D0=B5=D0=BC=D1=8B=D1=85=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8?= =?UTF-8?q?=D0=BE=D1=82=D0=B5=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a544bf5..93e7c6e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ fastapi uvicorn sqlalchemy -databases[sqlite] +asyncpg +psycopg2-binary +pydantic python-dotenv \ No newline at end of file From 15371ed4b3219bf81ed86db1179ae160e6ae9dc5 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 23:27:27 +0300 Subject: [PATCH 13/20] =?UTF-8?q?=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=20readme.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7676e1b..1a5deb9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ $ .\myenv\Scripts\activate ``` Так же установить пакеты: ```shell -$ pip install fastapi uvicorn sqlalchemy databases[sqlite] python-dotenv +$ pip install fastapi uvicorn sqlalchemy asyncpg psycopg2 pydantic python-dotenv ``` Что бы собрать контейнер и запустить его воспользоваться командами: ```shell From 512fe6bb3a4d41ac37b8dcf7c402f2f39ece4442 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 23:27:41 +0300 Subject: [PATCH 14/20] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=20main.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 177 +------------------------------------------------------- 1 file changed, 3 insertions(+), 174 deletions(-) diff --git a/main.py b/main.py index 004715a..a9f4ab4 100644 --- a/main.py +++ b/main.py @@ -1,178 +1,7 @@ -from typing import List -from fastapi import FastAPI, HTTPException -from sqlalchemy import create_engine, Column, Integer, String, ForeignKey -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker, relationship -from pydantic import BaseModel +from fastapi import FastAPI +from app.routes import router app = FastAPI() -# Создание базовой модели -Base = declarative_base() +app.include_router(router, prefix="/api") -# Модель студента -class Student(Base): - __tablename__ = 'students' - id = Column(Integer, primary_key=True, index=True) - name = Column(String, nullable=False) - group_id = Column(Integer, ForeignKey('groups.id')) - group = relationship('Group', back_populates='students') - -# Модель группы -class Group(Base): - __tablename__ = 'groups' - id = Column(Integer, primary_key=True, index=True) - name = Column(String, nullable=False) - students = relationship('Student', back_populates='group') - -# Модель для создания студента -class StudentCreate(BaseModel): - name: str - group_id: int - -# Модель для создания группы -class GroupCreate(BaseModel): - name: str - -# Модель для ответа с информацией о студенте -class StudentResponse(BaseModel): - id: int - name: str - group_id: int - -# Модель для ответа с информацией о группе -class GroupResponse(BaseModel): - id: int - name: str - students: List[StudentResponse] - -# Подключение к базе данных -SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" -engine = create_engine(SQLALCHEMY_DATABASE_URL) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - -# Создание таблиц в базе данных -Base.metadata.create_all(bind=engine) - -# API эндпоинт для создания студента -@app.post("/students/") -def create_student(student: StudentCreate): - db = SessionLocal() - db_student = Student(name=student.name, group_id=student.group_id) - db.add(db_student) - db.commit() - db.refresh(db_student) - return {"id": db_student.id, "name": db_student.name, "group_id": db_student.group_id} - -# API эндпоинт для создания группы -@app.post("/groups/") -def create_group(group: GroupCreate): - db = SessionLocal() - db_group = Group(name=group.name) - db.add(db_group) - db.commit() - db.refresh(db_group) - return {"id": db_group.id, "name": db_group.name} - -# API эндпоинт для получения информации о студенте по его id -@app.get("/students/{student_id}") -def get_student(student_id: int): - db = SessionLocal() - student = db.query(Student).filter(Student.id == student_id).first() - if not student: - raise HTTPException(status_code=404, detail="Student not found") - return {"id": student.id, "name": student.name, "group_id": student.group_id} - -# API эндпоинт для получения информации о группе по ее id -@app.get("/groups/{group_id}") -def get_group(group_id: int): - db = SessionLocal() - group = db.query(Group).filter(Group.id == group_id).first() - if not group: - raise HTTPException(status_code=404, detail="Group not found") - return {"id": group.id, "name": group.name} - -# API эндпоинт для удаления студента -@app.delete("/students/{student_id}") -def delete_student(student_id: int): - db = SessionLocal() - student = db.query(Student).filter(Student.id == student_id).first() - if not student: - raise HTTPException(status_code=404, detail="Student not found") - db.delete(student) - db.commit() - return {"message": "Student deleted"} - -# API эндпоинт для удаления группы -@app.delete("/groups/{group_id}") -def delete_group(group_id: int): - db = SessionLocal() - group = db.query(Group).filter(Group.id == group_id).first() - if not group: - raise HTTPException(status_code=404, detail="Group not found") - db.delete(group) - db.commit() - return {"message": "Group deleted"} - -# API эндпоинт для получения списка студентов -@app.get("/students/") -def get_students(): - db = SessionLocal() - students = db.query(Student).all() - return students - -# API эндпоинт для получения списка групп -@app.get("/groups/") -def get_groups(): - db = SessionLocal() - groups = db.query(Group).all() - return groups - -# API эндпоинт для добавления студента в группу -@app.post("/groups/{group_id}/students/{student_id}") -def add_student_to_group(group_id: int, student_id: int): - db = SessionLocal() - group = db.query(Group).filter(Group.id == group_id).first() - student = db.query(Student).filter(Student.id == student_id).first() - if not group: - raise HTTPException(status_code=404, detail="Group not found") - if not student: - raise HTTPException(status_code=404, detail="Student not found") - student.group_id = group_id - db.commit() - return {"message": "Student added to group"} - -# API эндпоинт для удаления студента из группы -@app.delete("/groups/{group_id}/students/{student_id}") -def remove_student_from_group(group_id: int, student_id: int): - db = SessionLocal() - student = db.query(Student).filter(Student.id == student_id).first() - if not student: - raise HTTPException(status_code=404, detail="Student not found") - student.group_id = None - db.commit() - return {"message": "Student removed from group"} - -# API эндпоинт для получения всех студентов в группе -@app.get("/groups/{group_id}/students/") -def get_students_in_group(group_id: int): - db = SessionLocal() - group = db.query(Group).filter(Group.id == group_id).first() - if not group: - raise HTTPException(status_code=404, detail="Group not found") - students = group.students - return students - -# API эндпоинт для перевода студента из группы A в группу B -@app.put("/students/{student_id}/move/{group_id}") -def move_student(student_id: int, group_id: int): - db = SessionLocal() - student = db.query(Student).filter(Student.id == student_id).first() - group = db.query(Group).filter(Group.id == group_id).first() - if not student: - raise HTTPException(status_code=404, detail="Student not found") - if not group: - raise HTTPException(status_code=404, detail="Group not found") - student.group_id = group_id - db.commit() - return {"message": "Student moved to another group"} From f61e9b97cbec6471faf1fec6c2a734fe5886e3a4 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 23:28:28 +0300 Subject: [PATCH 15/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20.env?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 5 +++++ .env.example | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..a80ff41 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +DB_USER=postgres +DB_PASSWORD=12345 +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=mydatabase \ No newline at end of file diff --git a/.env.example b/.env.example index 91a690a..a80ff41 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ -DB_USER=myuser +DB_USER=postgres +DB_PASSWORD=12345 DB_HOST=localhost DB_PORT=5432 DB_NAME=mydatabase \ No newline at end of file From 373def372f03680ee2c40c4ea9330ee90f276eaa Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Wed, 7 Jun 2023 23:28:54 +0300 Subject: [PATCH 16/20] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D0=BE=D1=80=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 26963b1..5714c01 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,10 +6,10 @@ services: restart: always environment: POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: 12345 + POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: ${DB_NAME} ports: - - ${DB_PORT}:5432 + - 5432:5432 app: build: @@ -17,7 +17,11 @@ services: dockerfile: Dockerfile restart: always environment: - - SQLALCHEMY_DATABASE_URL=postgresql://${DB_USER}:12345@db/${DB_NAME} + - DB_USER=${DB_USER} + - DB_PASSWORD=${DB_PASSWORD} + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DB_NAME=${DB_NAME} ports: - 8000:8000 depends_on: From 144ba031db236f00e559994be0b3b5d343c1d1ff Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Thu, 8 Jun 2023 01:08:23 +0300 Subject: [PATCH 17/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20.env=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/config.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/config.py diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..204bfe6 --- /dev/null +++ b/app/config.py @@ -0,0 +1,14 @@ +from pydantic import BaseSettings + +class Settings(BaseSettings): + db_user: str + db_password: str + db_host: str + db_port: str + db_name: str + + class Config: + env_file = ".env" + env_file_encoding = "utf-8" + +settings = Settings() \ No newline at end of file From 8f7d1fbc0f6731f824f648c3955289902b17eb21 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Thu, 8 Jun 2023 01:08:41 +0300 Subject: [PATCH 18/20] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB=20url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/database.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/database.py b/app/database.py index 8bb2a05..a62bb97 100644 --- a/app/database.py +++ b/app/database.py @@ -1,10 +1,12 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker +from sqlalchemy.ext.declarative import declarative_base +from app.config import settings # Подключение к базе данных -SQLALCHEMY_DATABASE_URL = "postgresql://:@:5432/" +SQLALCHEMY_DATABASE_URL = f"postgresql://{settings.db_user}:{settings.db_password}@{settings.db_host}:{settings.db_port}/{settings.db_name}" engine = create_engine(SQLALCHEMY_DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - +Base = declarative_base() From b99b84466874896a346a2291f4ce2ca6f001eea1 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Thu, 8 Jun 2023 01:09:29 +0300 Subject: [PATCH 19/20] =?UTF-8?q?=D0=BD=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5714c01..c4d3652 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,8 +19,8 @@ services: environment: - DB_USER=${DB_USER} - DB_PASSWORD=${DB_PASSWORD} - - DB_HOST=${DB_HOST} - - DB_PORT=${DB_PORT} + - DB_HOST=localhost + - DB_PORT=5432 - DB_NAME=${DB_NAME} ports: - 8000:8000 From 86d1809270a5413f5b59eab6eada0fdca5a4e149 Mon Sep 17 00:00:00 2001 From: Chilipinas Date: Thu, 8 Jun 2023 01:10:43 +0300 Subject: [PATCH 20/20] =?UTF-8?q?=D0=BD=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models.py | 5 +---- app/routes.py | 47 ++++++++++++++++++++++------------------------- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/app/models.py b/app/models.py index 0850331..d6e5d5c 100644 --- a/app/models.py +++ b/app/models.py @@ -1,11 +1,8 @@ from typing import List from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from pydantic import BaseModel - -# Создание базовой модели -Base = declarative_base() +from app.database import Base # Модель студента class Student(Base): diff --git a/app/routes.py b/app/routes.py index fcc90b7..c131143 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,13 +1,21 @@ -from fastapi import APIRouter, HTTPException +from fastapi import APIRouter, HTTPException, Depends +from sqlalchemy.orm import Session from app.database import SessionLocal, engine from app.models import Student, Group, StudentCreate, GroupCreate, StudentResponse, GroupResponse router = APIRouter() +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + # API эндпоинт для создания студента @router.post("/students/") -def create_student(student: StudentCreate): - db = SessionLocal() +def create_student(student: StudentCreate, db: Session = Depends(get_db)): db_student = Student(name=student.name, group_id=student.group_id) db.add(db_student) db.commit() @@ -16,8 +24,7 @@ def create_student(student: StudentCreate): # API эндпоинт для создания группы @router.post("/groups/") -def create_group(group: GroupCreate): - db = SessionLocal() +def create_group(group: GroupCreate, db: Session = Depends(get_db)): db_group = Group(name=group.name) db.add(db_group) db.commit() @@ -26,8 +33,7 @@ def create_group(group: GroupCreate): # API эндпоинт для получения информации о студенте по его id @router.get("/students/{student_id}") -def get_student(student_id: int): - db = SessionLocal() +def get_student(student_id: int, db: Session = Depends(get_db)): student = db.query(Student).filter(Student.id == student_id).first() if not student: raise HTTPException(status_code=404, detail="Student not found") @@ -35,8 +41,7 @@ def get_student(student_id: int): # API эндпоинт для получения информации о группе по ее id @router.get("/groups/{group_id}") -def get_group(group_id: int): - db = SessionLocal() +def get_group(group_id: int, db: Session = Depends(get_db)): group = db.query(Group).filter(Group.id == group_id).first() if not group: raise HTTPException(status_code=404, detail="Group not found") @@ -44,8 +49,7 @@ def get_group(group_id: int): # API эндпоинт для удаления студента @router.delete("/students/{student_id}") -def delete_student(student_id: int): - db = SessionLocal() +def delete_student(student_id: int, db: Session = Depends(get_db)): student = db.query(Student).filter(Student.id == student_id).first() if not student: raise HTTPException(status_code=404, detail="Student not found") @@ -55,8 +59,7 @@ def delete_student(student_id: int): # API эндпоинт для удаления группы @router.delete("/groups/{group_id}") -def delete_group(group_id: int): - db = SessionLocal() +def delete_group(group_id: int, db: Session = Depends(get_db)): group = db.query(Group).filter(Group.id == group_id).first() if not group: raise HTTPException(status_code=404, detail="Group not found") @@ -66,22 +69,19 @@ def delete_group(group_id: int): # API эндпоинт для получения списка студентов @router.get("/students/") -def get_students(): - db = SessionLocal() +def get_students(db: Session = Depends(get_db)): students = db.query(Student).all() return students # API эндпоинт для получения списка групп @router.get("/groups/") -def get_groups(): - db = SessionLocal() +def get_groups(db: Session = Depends(get_db)): groups = db.query(Group).all() return groups # API эндпоинт для добавления студента в группу @router.post("/groups/{group_id}/students/{student_id}") -def add_student_to_group(group_id: int, student_id: int): - db = SessionLocal() +def add_student_to_group(group_id: int, student_id: int, db: Session = Depends(get_db)): group = db.query(Group).filter(Group.id == group_id).first() student = db.query(Student).filter(Student.id == student_id).first() if not group: @@ -94,8 +94,7 @@ def add_student_to_group(group_id: int, student_id: int): # API эндпоинт для удаления студента из группы @router.delete("/groups/{group_id}/students/{student_id}") -def remove_student_from_group(group_id: int, student_id: int): - db = SessionLocal() +def remove_student_from_group(group_id: int, student_id: int, db: Session = Depends(get_db)): student = db.query(Student).filter(Student.id == student_id).first() if not student: raise HTTPException(status_code=404, detail="Student not found") @@ -105,8 +104,7 @@ def remove_student_from_group(group_id: int, student_id: int): # API эндпоинт для получения всех студентов в группе @router.get("/groups/{group_id}/students/") -def get_students_in_group(group_id: int): - db = SessionLocal() +def get_students_in_group(group_id: int, db: Session = Depends(get_db)): group = db.query(Group).filter(Group.id == group_id).first() if not group: raise HTTPException(status_code=404, detail="Group not found") @@ -115,8 +113,7 @@ def get_students_in_group(group_id: int): # API эндпоинт для перевода студента из группы A в группу B @router.put("/students/{student_id}/move/{group_id}") -def move_student(student_id: int, group_id: int): - db = SessionLocal() +def move_student(student_id: int, group_id: int, db: Session = Depends(get_db)): student = db.query(Student).filter(Student.id == student_id).first() group = db.query(Group).filter(Group.id == group_id).first() if not student: