Skip to content

Commit a0ba5c7

Browse files
Implementa projeto
1 parent 0539dca commit a0ba5c7

13 files changed

+408
-1
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# arquivos a serem ignorados
2+
.DS_Store
3+
*.pyc
4+
__pycache__/
5+
venv/
6+
*.env

README.md

+87-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,87 @@
1-
"# FastAPI_Filmes"
1+
# API Gerenciadora de Filmes e suas respectivas Avaliações
2+
3+
Este é um projeto foi criado para que o usuário use uma api que possa fazer o cadastramento de filmes e o cadastramento de avaliações desses filmes. Além disso, foi utilizado todos os requisitos para o CRUD e o API RESTful. Veja abaixo o modelo de entidade-relacionamento desse gerenciamento:
4+
5+
<img src="img/ER_projeto.png">
6+
7+
Observe pelo modelo de entidade-relacionamento, que avaliações possui um forte dependência, pois sempre estará linkada a um filme. Entretanto, um filme pode estar linkado a várias avaliações.
8+
9+
Ademais, o projeto consiste em duas etapas, a primeira consolida suas informações em arquivos .txt sem a utilização de banco de dados. Veja abaixo:
10+
11+
### **Link para vídeo da primeira parte implementada:**
12+
Nesse vídeo é possível encontrar requisitos adequados de práticas iniciais do uso da API e como utilizá-la no mundo real, como por exemplo, conexão com o front-end. <br>
13+
https://www.youtube.com/watch?v=pYtyoPe8YtU
14+
15+
Nesse contexto, o projeto busca a melhores práticas do mercado, portanto, é importante a utilização de banco de dados. Logo, na segunda parte do projeto é utilizado `MySQL`, entretanto, qualquer outro database pode ser implementado. Visualize abaixo a segunda parte implementada e explicada:
16+
17+
### **Link para vídeo da segunda parte implementada:**
18+
Nesse vídeo é possível encontrar requisitos adequados de práticas do uso da API conectadas a um banco de dados. <br>
19+
https://youtu.be/c97G6mM7Quk
20+
21+
---
22+
## Utilização do Projeto
23+
Com isso, para rodar o projeto em sua máquina local siga as etapas abaixo:
24+
25+
1. Uitlize o prompt de comando para Clonar o repositório em um diretório da sua preferência:
26+
```
27+
git clone https://github.com/insper-classroom/23-1-projeto-sql-insper-inside-team.git
28+
```
29+
2. Crie seu ambiente virtual no prompt
30+
```
31+
python -m venv name_env
32+
```
33+
3. Ative o seu ambiente virtual
34+
```
35+
name_env\Scripts\activate.bat
36+
```
37+
4. Instale todas suas dependências
38+
```
39+
pip install -r requirements.txt
40+
```
41+
5. Crie seu database com suas tabelas no seu DBMS(Database Management System) com a query abaixo:
42+
```sql
43+
CREATE DATABASE IF NOT EXISTS filmes;
44+
45+
USE filmes;
46+
47+
CREATE TABLE IF NOT EXISTS filmes (
48+
id_filme INTEGER PRIMARY KEY NOT NULL auto_increment,
49+
nome VARCHAR(255) UNIQUE NOT NULL,
50+
ano INTEGER NOT NULL,
51+
duracao FLOAT NOT NULL,
52+
descricao TEXT NOT NULL
53+
);
54+
55+
CREATE TABLE IF NOT EXISTS avaliacoes (
56+
id_avaliacao INTEGER PRIMARY KEY NOT NULL auto_increment,
57+
comentario TEXT NOT NULL,
58+
nota FLOAT NOT NULL CHECK (nota >= 0 AND nota <= 10),
59+
id_filme INTEGER,
60+
FOREIGN KEY (id_filme) REFERENCES filmes(id_filme) ON DELETE CASCADE
61+
);
62+
```
63+
6. Crie um arquivo `.env` no seu diretório com suas credenciais. Observe que o banco de dados utilizado é o `MySQL`, caso esteja utilizando outro, pesquise quais características seu arquivo `.env` deve ter e modifique o arquivo [database.py](sql_app/database.py) de acordo com as especificações. Caso esteja utilizando o mesmo, basta criá-lo com o seguinte conteúdo (lembre-se de adicionar o que está em branco):
64+
```
65+
DB_PROVIDER=mysql
66+
DB_DRIVER=mysqlconnector
67+
DB_DATABASE_NAME=filmes
68+
DB_USER=
69+
DB_PASSWORD=
70+
DB_HOST=localhost
71+
DB_PORT=3306
72+
DB_CONNECTION_STRING=${DB_PROVIDER}+${DB_DRIVER}://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_DATABASE_NAME}
73+
```
74+
6. Execute o projeto
75+
```
76+
uvicorn main:app --reload
77+
```
78+
7. Abra o seu browser em: <br>
79+
http://127.0.0.1:8000/docs <br>
80+
81+
Deve aparecer a seguinte tela:
82+
83+
<img src="img/interface.png">
84+
85+
<BR>
86+
87+
## <center> SE DIVIRTA !!! </center>

img/ER_projeto.png

83.5 KB
Loading

img/interface.png

35.6 KB
Loading

main.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from fastapi import FastAPI
2+
from sql_app.routes import routeAvaliacao, routeFilme
3+
4+
app = FastAPI(
5+
title="Cadastre Filmes e os Avalie !!!",
6+
description="API para gerenciar filmes e avaliações",
7+
version="1.0.0",
8+
)
9+
10+
# Adiciona as rotas para as classes Movie e Rating
11+
app.include_router(routeFilme.router, tags=["Filmes"])
12+
app.include_router(routeAvaliacao.router, tags=["Avaliações"])

requirements.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
python-dotenv==1.0.0
2+
SQLAlchemy==1.4.32
3+
fastapi==0.95.0
4+
pydantic==1.10.6
5+
typing-extensions==4.5.0

sql_app/__init__.py

Whitespace-only changes.

sql_app/crud.py

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from sqlalchemy.orm import Session
2+
3+
from . import models, schemas
4+
5+
############################### CRUD Filmes ################################################
6+
def read_filme(db: Session, filme_id: int):
7+
return db.query(models.Filme).filter(models.Filme.id_filme == filme_id).first()
8+
9+
def read_filmes(db: Session, skip: int = 0, limit: int = 100):
10+
return db.query(models.Filme).offset(skip).limit(limit).all()
11+
12+
def create_filme(db: Session, filme: schemas.FilmeCreate):
13+
db_filme = models.Filme(**filme.dict())
14+
db.add(db_filme)
15+
db.commit()
16+
db.refresh(db_filme)
17+
return db_filme
18+
19+
def update_filme(db: Session, filme_id: int, filme: schemas.FilmeUpdate):
20+
db_filme = db.query(models.Filme).filter(models.Filme.id_filme == filme_id).first()
21+
for key, value in filme.dict().items():
22+
setattr(db_filme, key, value)
23+
db.commit()
24+
db.refresh(db_filme)
25+
return db_filme
26+
27+
def delete_filme(db: Session, filme_id: int):
28+
db_filme = db.query(models.Filme).filter(models.Filme.id_filme == filme_id).first()
29+
db.delete(db_filme)
30+
db.commit()
31+
return {"Filme deletado": db_filme}
32+
33+
############################### CRUD Avaliacoes ###########################################
34+
def read_avaliacoes(db: Session, skip: int = 0, limit: int = 100):
35+
return db.query(models.Avaliacao).offset(skip).limit(limit).all()
36+
37+
def read_avaliacao(db: Session, filme_id: int):
38+
return db.query(models.Avaliacao).filter(models.Avaliacao.id_filme == filme_id).all()
39+
40+
def create_avaliacao(db: Session, avaliacao: schemas.AvaliacaoCreate):
41+
db_avaliacao = models.Avaliacao(**avaliacao.dict())
42+
db.add(db_avaliacao)
43+
db.commit()
44+
db.refresh(db_avaliacao)
45+
return db_avaliacao
46+
47+
def update_avaliacao(db: Session, avaliacao_id: int, avaliacao: schemas.AvaliacaoUpdate):
48+
db_avaliacao = db.query(models.Avaliacao).filter(models.Avaliacao.id_avaliacao == avaliacao_id).first()
49+
for key, value in avaliacao.dict().items():
50+
setattr(db_avaliacao, key, value)
51+
db.commit()
52+
db.refresh(db_avaliacao)
53+
return db_avaliacao
54+
55+
def delete_avaliacao(db: Session, avaliacao_id: int):
56+
db_avaliacao = db.query(models.Avaliacao).filter(models.Avaliacao.id_avaliacao == avaliacao_id).first()
57+
db.delete(db_avaliacao)
58+
db.commit()
59+
return {"Avaliação deletada": db_avaliacao}

sql_app/database.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from sqlalchemy import create_engine
2+
from sqlalchemy.ext.declarative import declarative_base
3+
from sqlalchemy.orm import sessionmaker
4+
from dotenv import dotenv_values
5+
6+
env = dict(dotenv_values(".env"))
7+
8+
SQLALCHEMY_DATABASE_URL = env.get("DB_CONNECTION_STRING")
9+
10+
engine = create_engine(
11+
SQLALCHEMY_DATABASE_URL
12+
)
13+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
14+
15+
Base = declarative_base()

sql_app/models.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from sqlalchemy import Float, Column, ForeignKey, Integer, String, CheckConstraint
2+
from sqlalchemy.orm import relationship
3+
4+
from .database import Base
5+
6+
################################## Filme ############################################
7+
class Filme(Base):
8+
__tablename__ = "filmes"
9+
10+
id_filme = Column(Integer, primary_key=True, nullable=True, unique=True, autoincrement=True)
11+
nome = Column(String, nullable=True, unique=True)
12+
ano = Column(Integer, nullable=True)
13+
duracao = Column(Float, nullable=True)
14+
descricao = Column(String, nullable=True)
15+
16+
avaliacoes = relationship("Avaliacao", backref='parent', passive_deletes=True)
17+
18+
################################## Avaliacao ########################################
19+
class Avaliacao(Base):
20+
__tablename__ = "avaliacoes"
21+
22+
id_avaliacao = Column(Integer, primary_key=True, nullable=True, unique=True, autoincrement=True)
23+
comentario = Column(String, nullable=True)
24+
nota = Column(Float, nullable=True)
25+
id_filme = Column(Integer, ForeignKey("filmes.id_filme", ondelete='CASCADE'))
26+
27+
filme = relationship("Filme", back_populates="avaliacoes")
28+
29+
__table_args__ = (
30+
CheckConstraint('nota >= 0 AND nota <= 10', name='check_nota'),
31+
)

sql_app/routes/routeAvaliacao.py

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from fastapi import APIRouter,Depends, FastAPI, HTTPException
2+
from sqlalchemy.orm import Session
3+
from .. import crud, models, schemas
4+
from ..database import SessionLocal, engine
5+
6+
models.Base.metadata.create_all(bind=engine)
7+
8+
router = APIRouter()
9+
10+
# Dependency
11+
def get_db():
12+
db = SessionLocal()
13+
try:
14+
yield db
15+
finally:
16+
db.close()
17+
18+
################################ AVALIACOES ##########################################################
19+
@router.get("/avaliacoes", summary="Liste todas as avaliações")
20+
async def listar_avaliacoes(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
21+
"""
22+
Responsável por retornar todas as avaliacoes do nosso banco de dados
23+
Não possui argumentos.
24+
"""
25+
26+
avaliacoes = crud.read_avaliacoes(db, skip=skip, limit=limit)
27+
return avaliacoes
28+
29+
@router.get("/avaliacoes/{id_filme}", summary="Obtenha a avaliação do seu filme")
30+
async def obter_avaliacao(id_filme: int, db: Session = Depends(get_db)):
31+
"""
32+
Obtenha uma avaliação específica de um filme com seu determinado id.
33+
"""
34+
35+
db_filme = crud.read_avaliacao(db=db, filme_id=id_filme)
36+
if db_filme is None:
37+
raise HTTPException(status_code=404, detail="Filme not found")
38+
return {"Avaliações do Filme escolhido": db_filme}
39+
40+
@router.post("/avaliacoes", summary="Adicione uma avaliação")
41+
async def adicionar_avaliacao(avaliacao: schemas.AvaliacaoCreate, db: Session = Depends(get_db)):
42+
"""
43+
Adiciona avaliações com suas características.
44+
- id_avaliacao => Apesar de não visível, está autoincrementado no database
45+
- id_filme => Identificador do Filme
46+
- comentario => Deixe sua opinião sobre esse filme
47+
- nota => Forneça uma nota de 0 a 10 para o seu filme
48+
"""
49+
db_filme = crud.read_filme(db=db, filme_id=avaliacao.id_filme)
50+
if db_filme is None:
51+
raise HTTPException(status_code=404, detail="Filme not found")
52+
return crud.create_avaliacao(db=db, avaliacao=avaliacao)
53+
54+
55+
@router.put("/avaliacoes/{id_avaliacao}", summary="Atualize a sua avaliação do filme")
56+
async def atualizar_avaliacao(id_avaliacao: int, avaliacao: schemas.AvaliacaoUpdate, db: Session = Depends(get_db)):
57+
"""
58+
Atualize uma determinada avaliação com todas as suas caracteríticas.
59+
"""
60+
61+
db_avaliacao = crud.update_avaliacao(db=db, avaliacao_id=id_avaliacao, avaliacao=avaliacao)
62+
if db_avaliacao is None:
63+
raise HTTPException(status_code=404, detail="Avaliacao not found")
64+
return db_avaliacao
65+
66+
@router.delete("/avaliacoes/{id_avaliacao}", summary="Delete a sua avaliação do filme")
67+
async def remover_avaliacao(id_avaliacao: int, db: Session = Depends(get_db)):
68+
"""
69+
Delete uma avaliação com o seu determinado Identificador.
70+
"""
71+
72+
db_avaliacao = crud.delete_avaliacao(db=db, avaliacao_id=id_avaliacao)
73+
if db_avaliacao is None:
74+
raise HTTPException(status_code=404, detail="Avaliacao not found")
75+
return db_avaliacao

sql_app/routes/routeFilme.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from fastapi import APIRouter,Depends, FastAPI, HTTPException
2+
from sqlalchemy.orm import Session
3+
from .. import crud, models, schemas
4+
from ..database import SessionLocal, engine
5+
models.Base.metadata.create_all(bind=engine)
6+
7+
router = APIRouter()
8+
9+
# Dependency
10+
def get_db():
11+
db = SessionLocal()
12+
try:
13+
yield db
14+
finally:
15+
db.close()
16+
17+
################################ FILMES #############################################################
18+
@router.get("/filmes", summary="Liste todos os filmes")
19+
def listar_filmes(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
20+
"""
21+
Responsável por retornar todas os filmes do nosso banco de dados
22+
23+
Não possui argumentos.
24+
"""
25+
26+
filmes = crud.read_filmes(db, skip=skip, limit=limit)
27+
return filmes
28+
29+
@router.get("/filmes/{id_filme}", summary="Obtenha o seu filme")
30+
def obter_filme(id_filme: int, db: Session = Depends(get_db)):
31+
"""
32+
Obtenha um filme específico com seu determinado id.
33+
"""
34+
35+
db_filme = crud.read_filme(db=db, filme_id=id_filme)
36+
if db_filme is None:
37+
raise HTTPException(status_code=404, detail="Filme not found")
38+
return db_filme
39+
40+
@router.post("/filmes", summary="Adicione um filme")
41+
def adicionar_filme(filme: schemas.FilmeCreate, db: Session = Depends(get_db)):
42+
"""
43+
Adiciona filmes com suas características.
44+
45+
- id_filme => Apesar de não estar visível, ele está autoincrementado no database.
46+
- nome => Nome do filme
47+
- ano => Ano de lançamento do filme
48+
- duracao => Duração em horas do filme
49+
- descricao => Descrição da sinopse do filme
50+
"""
51+
52+
# TODO: Não faz sentido verificar se o id nao existe entao
53+
# poderia fazer verificação por nome
54+
55+
return crud.create_filme(db=db, filme=filme)
56+
57+
58+
@router.put("/filmes/{id_filme}", summary="Atualize um filme")
59+
async def atualizar_filme(id_filme: int, filme: schemas.FilmeUpdate, db: Session = Depends(get_db)):
60+
"""
61+
Atualize um determinado filme com todas as suas caracteríticas.
62+
"""
63+
64+
db_filme = crud.update_filme(db=db, filme_id=id_filme, filme=filme)
65+
if db_filme is None:
66+
raise HTTPException(status_code=404, detail="Filme not found")
67+
return db_filme
68+
69+
@router.delete("/filmes/{id_filme}", summary="Delete um filme")
70+
async def remover_filme(id_filme: int, db: Session = Depends(get_db)):
71+
"""
72+
Delete um filme com o seu determinado Identificador. Lembre que caso remova um filme,
73+
não haverá mais comentários relacionados a ele.
74+
"""
75+
76+
db_filme = crud.delete_filme(db=db, filme_id=id_filme)
77+
if db_filme is None:
78+
raise HTTPException(status_code=404, detail="Filme not found")
79+
return db_filme

0 commit comments

Comments
 (0)