Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
Pythonchevron-right

Introdução ao MongoDB e FastAPI

Aaron Bassett, Mark Smith7 min read • Published Feb 05, 2022 • Updated Jul 12, 2024
DjangoFastAPIMongoDBPython
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Início rápido
star-empty
star-empty
star-empty
star-empty
star-empty
Logotipo do QuickStart Python
A FastAPI é um framework Python 3.6+ moderno, de alto desempenho, fácil de aprender, rápido de codificar e pronto para produção. É usado para criar APIs com base nas dicas padrão do tipo Python. Embora possa não estar tão estabelecido quanto alguns outros frameworks Python, como Django, ele já está na produção em empresas como Uber, Netflix e Microsoft.
A FastAPI é assíncrona e, como o próprio nome indica, é super rápida; então, o MongoDB é o acompanhamento ideal. Neste início rápido, criaremos um aplicativo CRUD (Criar, Ler, Atualizar, Excluir) mostrando como você pode integrar o MongoDB com seus projetos FastAPI.

Pré-requisitos

  • Python 3.9.0
  • Um cluster do MongoDB Atlas. Siga o guia "Introdução ao Atlas" para criar sua conta e o cluster do MongoDB. Anote seu nome de usuário, senha e string, pois você precisará deles mais tarde.

Executando o exemplo

Para começar, você deve clonar o código de exemplo do GitHub.
1git clone git@github.com:mongodb-developer/mongodb-with-fastapi.git
Você precisará instalar algumas dependências: FastAPI, Motor etc. Sempre recomendo que você instale todas as dependências do Python em um virtualenv para o projeto. Antes de executar o pip, verifique se o virtualenv está ativo.
1cd mongodb-with-fastapi
2pip install -r requirements.txt
Pode levar alguns minutos para baixar e instalar suas dependências. Isso é normal, especialmente se você nunca instalou um pacote.
Depois de instalar as dependências, você precisará criar uma variável de ambiente para sua string de conexão do MongoDB.
1export MONGODB_URL="mongodb+srv://<username>:<password>@<url>/<db>?retryWrites=true&w=majority"
Lembre-se, sempre que você iniciar uma nova sessão de terminal, precisará definir essa variável de ambiente novamente. Eu uso direnv para tornar este processo mais fácil.
A etapa final é iniciar seu servidor FastAPI.
1uvicorn app:app --reload
Captura de tela do terminal executando a FastAPI
Após o início do aplicativo, você pode vê-lo em seu navegador em http://127.0.0.1:8000/docs.
Captura de tela do navegador e IU do Swagger
Quando tiver tido a chance de experimentar o exemplo, volte e analisaremos o código.

Criação do aplicativo

Todo o código do aplicativo de exemplo está dentr deo app.py. Vamos dividi-lo em seções e mostrar o que cada uma está fazendo.

Conectando ao MongoDB

Uma das primeiras coisas que fazemos é nos conectar ao nosso MongoDB database.
1client = motor.motor_asyncio.AsyncIOMotorClient(os.environ["MONGODB_URL"])
2db = client.get_database("college")
3student_collection = db.get_collection("students")
Estamos usando o driver de motor assíncrono para criar nosso cliente MongoDB e, em seguida, especificamos o nome do nosso banco de dados college.

_id Attribute e ObjectIds

1# Represents an ObjectId field in the database.
2# It will be represented as a `str` on the model so that it can be serialized to JSON.
3PyObjectId = Annotated[str, BeforeValidator(str)]
O MongoDB armazena dados como BSON. A FastAPI codifica e decodifica dados como strings JSON. BSON tem suporte para outros tipos de dados não nativos JSON, incluindo ObjectId que não podem ser codificados diretamente como JSON. Por isso, convertemos ObjectIds em strings antes de armazená-las como o campo id.

Modelos de banco de dados

Muitas pessoas pensam que o MongoDB não tem esquema, o que está errado. O MongoDB tem um esquema flexível. Ou seja, as coleções não impõem uma estrutura de documento por padrão, então você tem a flexibilidade de usar quaisquer opções de modelagem de dados que melhor correspondam ao seu aplicativo e aos requisitos de desempenho. Portanto, é comum criar modelos ao trabalhar com um MongoDB database. Nosso aplicativo tem três modelos, o StudentModel, o UpdateStudentModele o StudentCollection.
1class StudentModel(BaseModel):
2 """
3 Container for a single student record.
4 """
5
6 # The primary key for the StudentModel, stored as a `str` on the instance.
7 # This will be aliased to `_id` when sent to MongoDB,
8 # but provided as `id` in the API requests and responses.
9 id: Optional[PyObjectId] = Field(alias="_id", default=None)
10 name: str = Field(...)
11 email: EmailStr = Field(...)
12 course: str = Field(...)
13 gpa: float = Field(..., le=4.0)
14 model_config = ConfigDict(
15 populate_by_name=True,
16 arbitrary_types_allowed=True,
17 json_schema_extra={
18 "example": {
19 "name": "Jane Doe",
20 "email": "jdoe@example.com",
21 "course": "Experiments, Science, and Fashion in Nanophotonics",
22 "gpa": 3.0,
23 }
24 },
25 )
Esse é o modelo primário que usamos como modelo de resposta para a maioria dos nossos endpoints.
Quero chamar a atenção para o campoid neste modelo. O MongoDB usa _id, mas no Python, os sublinhados no início dos atributos têm um significado especial. Se você tiver um atributo em seu modelo que comece com um sublinhado, pydantic–a estrutura de validação de dados usada pelo FastAPI–assumirá que é uma variável privada, o que significa que você não poderá atribuir um valor a ele! Para contornar isso, nomeamos o campo id, mas damos a ele um alias de _id. Você também precisa definir populate_by_name como True no model_config
Definimos esse valor id automaticamente como None, portanto, você não precisa fornecê-lo ao criar um novo aluno.
1class UpdateStudentModel(BaseModel):
2 """
3 A set of optional updates to be made to a document in the database.
4 """
5
6 name: Optional[str] = None
7 email: Optional[EmailStr] = None
8 course: Optional[str] = None
9 gpa: Optional[float] = None
10 model_config = ConfigDict(
11 arbitrary_types_allowed=True,
12 json_encoders={ObjectId: str},
13 json_schema_extra={
14 "example": {
15 "name": "Jane Doe",
16 "email": "jdoe@example.com",
17 "course": "Experiments, Science, and Fashion in Nanophotonics",
18 "gpa": 3.0,
19 }
20 },
21 )
O UpdateStudentModel tem duas diferenças importantes em relação ao StudentModel:
  • Ele não tem um atributo id, pois isso não pode ser modificado.
  • Todos os campos são opcionais, então você só precisa fornecer os campos que deseja atualizar.
Por fim, StudentCollection é definido para encapsular uma lista de instâncias StudentModel. Em teoria, o endpoint poderia retornar uma lista de nível superior de StudentModels, mas há algumas vulnerabilidades associadas ao retorno de respostas JSON com listas de nível superior.
1class StudentCollection(BaseModel):
2 """
3 A container holding a list of `StudentModel` instances.
4
5 This exists because providing a top-level array in a JSON response can be a [vulnerability](https://haacked.com/archive/2009/06/25/json-hijacking.aspx/)
6 """
7
8 students: List[StudentModel]

Rotas de aplicativos

Nosso aplicativo tem cinco rotas:
  • POST /students/ - cria um novo aluno.
  • GET /students/ - ver uma lista de todos os alunos.
  • GET /students/{id} - ver um único aluno.
  • PUT /students/{id} - atualizar um aluno.
  • DELETE /students/{id} - exclui um aluno.

Criar rota do aluno

1@app.post(
2 "/students/",
3 response_description="Add new student",
4 response_model=StudentModel,
5 status_code=status.HTTP_201_CREATED,
6 response_model_by_alias=False,
7)
8async def create_student(student: StudentModel = Body(...)):
9 """
10 Insert a new student record.
11
12 A unique `id` will be created and provided in the response.
13 """
14 new_student = await student_collection.insert_one(
15 student.model_dump(by_alias=True, exclude=["id"])
16 )
17 created_student = await student_collection.find_one(
18 {"_id": new_student.inserted_id}
19 )
20 return created_student
A rota create_student recebe os dados do novo aluno como uma string JSON em uma solicitaçãoPOST. Temos que decodificar esse corpo da solicitação JSON em um dicionário Python antes de passá-lo ao nosso cliente MongoDB.
A resposta do método insert_one inclui o _id do aluno recém-criado (fornecido como id) porque esse ponto de extremidade especifica response_model_by_alias=False na chamada do decorador post. Depois de inserir o aluno em nossa coleção, usamos o inserted_id para encontrar o documento correto e devolvê-lo em nosso JSONResponse.
A FastAPI retorna um código de status HTTP 200 por padrão; mas neste caso, um 201 criado é mais apropriado.
Ler rotas
O aplicativo tem duas rotas de leitura: uma para visualizar todos os alunos e outra para visualizar um aluno individual.
1@app.get(
2 "/students/",
3 response_description="List all students",
4 response_model=StudentCollection,
5 response_model_by_alias=False,
6)
7async def list_students():
8 """
9 List all of the student data in the database.
10
11 The response is unpaginated and limited to 1000 results.
12 """
13 return StudentCollection(students=await student_collection.find().to_list(1000))
O método to_list do Motor requer um argumento de contagem máxima de documentos. Para este exemplo, codifiquei-o para 1000, mas em um aplicativo real, você usaria os parâmetros skip e limit em find para paginar seus resultados.
1@app.get(
2 "/students/{id}",
3 response_description="Get a single student",
4 response_model=StudentModel,
5 response_model_by_alias=False,
6)
7async def show_student(id: str):
8 """
9 Get the record for a specific student, looked up by `id`.
10 """
11 if (
12 student := await student_collection.find_one({"_id": ObjectId(id)})
13 ) is not None:
14 return student
15
16 raise HTTPException(status_code=404, detail=f"Student {id} not found")
A rota de detalhes do aluno tem um parâmetro de caminho id, que a FastAPI passa como argumento para a função show_student. Usamos id para tentar encontrar o aluno correspondente no banco de dados. A condicional nesta seção está usando uma expressão de atribuição, um acréscimo ao Python 3.8 e muitas vezes referida pelo apelido "operador walrus."
Se um documento com o _id especificado não existir, criaremos uma HTTPException com status de 404.
Atualizar rota
1@app.put(
2 "/students/{id}",
3 response_description="Update a student",
4 response_model=StudentModel,
5 response_model_by_alias=False,
6)
7async def update_student(id: str, student: UpdateStudentModel = Body(...)):
8 """
9 Update individual fields of an existing student record.
10
11 Only the provided fields will be updated.
12 Any missing or `null` fields will be ignored.
13 """
14 student = {
15 k: v for k, v in student.model_dump(by_alias=True).items() if v is not None
16 }
17
18 if len(student) >= 1:
19 update_result = await student_collection.find_one_and_update(
20 {"_id": ObjectId(id)},
21 {"$set": student},
22 return_document=ReturnDocument.AFTER,
23 )
24 if update_result is not None:
25 return update_result
26 else:
27 raise HTTPException(status_code=404, detail=f"Student {id} not found")
28
29 # The update is empty, but we should still return the matching document:
30 if (existing_student := await student_collection.find_one({"_id": id})) is not None:
31 return existing_student
32
33 raise HTTPException(status_code=404, detail=f"Student {id} not found")
A rota update_student é como uma combinação das rotascreate_student e show_student. Ela recebe o id do documento a ser atualizado, bem como os novos dados no corpo do JSON. Não queremos atualizar nenhum campo com valores vazios; portanto, em primeiro lugar, iteramos todos os itens no dicionário recebido e adicionamos apenas os itens que têm um valor ao nosso novo documento.
Se, depois de removermos os valores vazios, não houver campos para atualizar, procuramos um registro existente que corresponda a id e o retornamos inalterado. No entanto, se houver valores a serem atualizados, usamos find_one_and_update para $set os novos valores e, em seguida, retornamos o documento atualizado.
Se chegarmos ao final da função e não conseguirmos encontrar um documento correspondente para atualizar ou retornar, criaremos um erro 404 novamente.
Excluir rota
1@app.delete("/students/{id}", response_description="Delete a student")
2async def delete_student(id: str):
3 """
4 Remove a single student record from the database.
5 """
6 delete_result = await student_collection.delete_one({"_id": ObjectId(id)})
7
8 if delete_result.deleted_count == 1:
9 return Response(status_code=status.HTTP_204_NO_CONTENT)
10
11 raise HTTPException(status_code=404, detail=f"Student {id} not found")
Nossa rota final é delete_student. Novamente, como isso está afetando um único documento, temos que fornecer um id no URL. Se encontrarmos um documento correspondente e o excluirmos com êxito, retornaremos um status HTTP de 204 ou "Sem conteúdo". Nesse caso, não devolvemos um documento porque já o excluímos! No entanto, se não conseguirmos encontrar um aluno com o id especificado, retornamos um 404.

Nosso novo gerador de aplicativos FastAPI

Se você estiver ansioso para criar algo mais pronto para produção com FastAPI, React e MongoDB, acesse o repositório do Github para encontrar nosso novo gerador de aplicativos FastAPI e comece a transformar sua experiência de desenvolvimento web.

Encerrando

Espero que você tenha gostado dessa introdução à FastAPI com MongoDB. Se quiser saber mais, confira minha publicação sobre como introduzir a pilha FARM (FastAPI, React e MongoDB), bem como a documentação da FastAPI e esta lista bacana.
Se tiver dúvidas, acesse o website da comunidade de desenvolvedores, onde os engenheiros do MongoDB e da MongoDB Community ajudarão a criar sua próxima grande ideia com o MongoDB.

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Início rápido
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Crie uma Cocktail API com o Beanie e o MongoDB


Oct 01, 2024 | 6 min read
Tutorial

Impulsionando a IA: construa um chatbot sobre seus dados com o MongoDB Atlas Vector Search e os modelos LangChain usando o padrão RAG


Sep 18, 2024 | 7 min read
Tutorial

Como usar o MongoDB Atlas e os LLMs do IBM watsonx.ai em seus aplicativos de GenAI sem interrupções


Sep 18, 2024 | 9 min read
Tutorial

Como escolher o melhor modelo de incorporação para seu aplicativo LLM


Nov 07, 2024 | 16 min read
Sumário