Primeiros passos no MongoDB e Starlette
Avalie esse Início rápido
Starlette é um framework/kit de ferramentas ASGI leve, ideal para construir serviços assíncronos de alto desempenho. Ele fornece tudo o que você precisa para criar APIs JSON, com muito pouco código padrão. No entanto, se você preferir uma estrutura web assíncrona que seja um pouco mais "pilhas incluídas", não deixe de ler meu tutorial sobre Introdução ao MongoDB e FastAPI.
Neste início rápido, criaremos um aplicativo CRUD (Criar, Ler, Atualizar, Excluir) mostrando como você pode integrar o MongoDB com seus projetos Starlette.
- 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.
1 git clone git@github.com:mongodb-developer/mongodb-with-starlette.git
Você precisará instalar algumas dependências: StarletteMotor Python , Antes de executar o pip, verifique se o virtualenv está ativo.
1 cd mongodb-with-starlette 2 pip 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.
1 export 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 Starlette.
1 uvicorn app:app --reload
Após o início do aplicativo, você pode visualizá-lo em seu navegador em http://127.0.0.1:8000/. Não haverá muito para ver no momento, pois você não tem nenhum dado! Veremos cada um dos pontos finais um pouco mais tarde no tutorial; mas se você quiser criar alguns dados agora para testar, será necessário enviar uma solicitação
POST
com um corpo JSON para o URL local.1 curl -X "POST" "http://localhost:8000/" \ 2 -H 'Accept: application/json' \ 3 -H 'Content-Type: application/json; charset=utf-8' \ 4 -d '{ 5 "name": "Jane Doe", 6 "email": "jdoe@example.com", 7 "gpa": "3.9" 8 }'
Tente criar alguns alunos por meio destas
POST
solicitações e atualize seu navegador.Todo o código do aplicativo de exemplo está em
app.py
. Vou dividi-lo em seções e explicar o que cada uma está fazendo.Uma das primeiras coisas que fazemos é nos conectar ao nosso MongoDB database.
1 client = motor.motor_asyncio.AsyncIOMotorClient(os.environ["MONGODB_URL"]) 2 db = client.college
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
.Nosso aplicativo tem cinco rotas:
- POST / - cria um novo aluno.
- GET / - ver uma lista de todos os alunos.
- GET /{id} - veja um único aluno.
- PUT /{id} - atualizar um aluno.
- DELETE /{id} - exclui um aluno.
1 async def create_student(request): 2 student = await request.json() 3 student["_id"] = str(ObjectId()) 4 new_student = await db["students"].insert_one(student) 5 created_student = await db["students"].find_one({"_id": new_student.inserted_id}) 6 return JSONResponse(status_code=201, content=created_student)
Observe como estou convertendo
ObjectId
em uma string antes de atribuí-la como _id
. MongoDB armazena dados como BSON; Starlette codifica e decodifica dados como strings JSON. BSON tem suporte para tipos de dados adicionais não nativos de JSON, incluindo ObjectId
, mas JSON não. Felizmente,os valores_id
do MongoDB não precisam ser ObjectIDs. Por causa disso, para simplificar, convertemos ObjectIds em strings antes de armazená-los.A rota
create_student
recebe os dados do novo aluno como uma string JSON em uma solicitação POST
. A funçãorequest.json
converte essa string JSON de volta para um dicionário Python que podemos passar para nosso cliente MongoDB.A resposta do método
insert_one
inclui o _id
do aluno recém-criado. Depois de inserirmos o aluno em nossa coleção, usamos o inserted_id
para encontrar o documento correto e retorná-lo em nosso JSONResponse
.Starlette retorna um código de status HTTP
200
por padrão, mas, nessa instância, um 201
criado é mais apropriado.O aplicativo tem duas rotas de leitura: uma para visualizar todos os alunos e outra para visualizar um aluno individual.
1 async def list_students(request): 2 students = await db["students"].find().to_list(1000) 3 return JSONResponse(students)
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 async def show_student(request): 2 id = request.path_params["id"] 3 if (student := await db["students"].find_one({"_id": id})) is not None: 4 return JSONResponse(student) 5 6 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 o Starlette passa como argumento para a função show_student
. Usamos o id para tentar encontrar o aluno correspondente no banco de dados. A condicional nesta seção está usando uma expressão de atribuição, uma adição recente ao Python (introduzida na versão 3.8) e muitas vezes referida pelo apelido incrivelmente fofo "operador walrus."Se um documento com o
id
especificado não existir, criaremos uma HTTPException
com status de 404
.1 async def update_student(request): 2 id = request.path_params["id"] 3 student = await request.json() 4 update_result = await db["students"].update_one({"_id": id}, {"$set": student}) 5 6 if update_result.modified_count == 1: 7 if (updated_student := await db["students"].find_one({"_id": id})) is not None: 8 return JSONResponse(updated_student) 9 10 if (existing_student := await db["students"].find_one({"_id": id})) is not None: 11 return JSONResponse(existing_student) 12 13 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
. Ele recebe a ID do documento para atualizar, bem como os novos dados no corpo JSON.Tentamos
$set
os novos valores no documento correto comupdate_one
e, em seguida, verificar se ele modificou corretamente um único documento. Se sim, encontramos o documento que acabou de ser atualizado e o retornamos.Se
modified_count
não for igual a um, ainda verificaremos se há um documento correspondente ao ID. Um modified_count
de zero pode MEAN que não há nenhum documento com esse ID, mas também pode MEAN que o documento existe, mas não precisou ser atualizado porque os valores atuais são os mesmos que os fornecidos na solicitaçãoPUT
.Somente depois que a localização final falha é que levantamos uma exceção
404
Not Found.1 async def delete_student(request): 2 id = request.path_params["id"] 3 delete_result = await db["students"].delete_one({"_id": id}) 4 5 if delete_result.deleted_count == 1: 6 return JSONResponse(status_code=204) 7 8 raise HTTPException(status_code=404, detail=f"Student {id} not found")
Nossa última rota é
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
.1 app = Starlette( 2 debug=True, 3 routes=[ 4 Route("/", create_student, methods=["POST"]), 5 Route("/", list_students, methods=["GET"]), 6 Route("/{id}", show_student, methods=["GET"]), 7 Route("/{id}", update_student, methods=["PUT"]), 8 Route("/{id}", delete_student, methods=["DELETE"]), 9 ], 10 )
O pedaço final de código cria uma instância do Starlette e inclui cada uma das rotas que definimos. Você pode ver que muitas das rotas compartilham a mesma URL, mas usam métodos HTTP diferentes. Por exemplo, uma solicitação
GET
para/{id}
retornará o documento do aluno correspondente para você visualizar, enquanto uma solicitaçãoDELETE
para o mesmo URL o excluirá. Portanto, tenha muito cuidado com o método HTTP que você usa para cada solicitação!Esperemos que esta introdução ao Starlette com MongoDB tenha sido útil para você. Agora é um momento fascinante para os desenvolvedores de Python, à medida que mais e mais estruturas, novas e antigas, começam a tirar proveito da assíncrona.
Se você quiser saber mais e levar seu conhecimento do MongoDB e da Starlette para o próximo nível, confira o tutorial detalhado do Ado sobre como construir um site de reserva de propriedades com a Starlette, MongoDB e Twilio. Além disso, se você estiver interessado no FastAPI (uma estrutura da Web criada com base no Starlette), veja meu tutorial sobre como começar a usar a pilhaFARM: FastAPI, React e MongoDB.
Se tiver dúvidas, acesse o site da nossa comunidade de desenvolvedores, no qual os engenheiros e a comunidade do MongoDB ajudarão você a desenvolver sua próxima grande ideia com o MongoDB.