Introdução ao MongoDB e Sanic
Avalie esse Início rápido
Sanic é um Python 3.6+ servidor web assíncrono e estrutura web escritas para serem rápidas. O objetivo do projeto é fornecer uma maneira simples de instalar e executar um servidor HTTP de alto desempenho que seja fácil de construir, expandir e, finalmente, de escalar.
Infelizmente, por causa de seu nome e escolhas duvidosas na arte ASCII, Sanic não era visto por alguns como uma estrutura séria, mas amadureceu. Deve considerar se você precisa de um framework Python rápido e assíncrono.
Neste início rápido, criaremos um aplicativo CRUD (Criar, Ler, Atualizar, Excluir) mostrando como você pode integrar o MongoDB com seus projetos Sanic.
- 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-sanic.git
Você precisará instalar algumas dependências: Sanic, 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.
1 cd mongodb-with-sanic 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 Sanic.
1 python app.py
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 de conexão um pouco mais tarde no tutorial, mas se você quiser criar alguns dados agora para testar, você precisa enviar uma solicitação
POST
com um corpo JSON para o local URL.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.Vamos usar o pacote de motor sânico para envolver nosso cliente de motor para facilitar o uso. Portanto, precisamos fornecer algumas configurações ao criar nosso aplicativo Sanic.
1 app = Sanic(__name__) 2 3 settings = dict( 4 MOTOR_URI=os.environ["MONGODB_URL"], 5 LOGO=None, 6 ) 7 app.config.update(settings) 8 9 BaseModel.init_app(app) 10 11 12 class Student(BaseModel): 13 __coll__ = "students"
É improvável que os modelos do Sanic-Motor sejam muito semelhantes a qualquer outro modelo de banco de dados que você já usou. Eles não descrevem o esquema, por exemplo. Em vez disso, especificamos apenas o nome da coleção.
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 2 async def create_student(request): 3 student = request.json 4 student["_id"] = str(ObjectId()) 5 6 new_student = await Student.insert_one(student) 7 created_student = await Student.find_one( 8 {"_id": new_student.inserted_id}, as_raw=True 9 ) 10 11 return json_response(created_student)
Observe como estou convertendo o
ObjectId
em uma string antes de atribuí-la como _id
. O MongoDB armazena dados como BSON. No entanto, estamos codificando e decodificando nossos dados como strings JSON. O BSON tem suporte para outros tipos de dados não nativos JSON, incluindo ObjectId
. JSON não. Por isso, 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çãoPOST
. A Sanic converterá automaticamente essa string JSON de volta para um dicionário Python que podemos então passar para o wrapper sanic-driver.A resposta do método
insert_one
inclui o_id
do aluno recém-criado. Depois de inserir o aluno em nossa coleção, usamos o inserted_id
para encontrar o documento correto e devolvê-lo no json_response
.sanic-motor retorna os objetos de modelo relevantes de qualquer método
find
, inclusivefind_one
. Para substituir esse comportamento, especificamos as_raw=True
.O aplicativo tem duas rotas de leitura: uma para visualizar todos os alunos e outra para visualizar um aluno individual.
1 2 async def list_students(request): 3 students = await Student.find(as_raw=True) 4 return json_response(students.objects)
Em nosso código de exemplo, não estamos colocando limites no número de alunos retornados.Em um aplicativo real, você deve usar os argumentos
page
e per_page
do sanic-driver para paginar o número de alunos retornados.1 2 async def show_student(request, id): 3 if (student := await Student.find_one({"_id": id}, as_raw=True)) is not None: 4 return json_response(student) 5 6 raise NotFound(f"Student {id} not found")
A rota de detalhes do aluno tem um parâmetro de caminho
id
, que o Sanic 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, 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 a
id
especificada não existir, geramos uma exceçãoNotFound
que responderá à solicitação com uma resposta404
.1 2 async def update_student(request, id): 3 student = request.json 4 update_result = await Student.update_one({"_id": id}, {"$set": student}) 5 6 if update_result.modified_count == 1: 7 if ( 8 updated_student := await Student.find_one({"_id": id}, as_raw=True) 9 ) is not None: 10 return json_response(updated_student) 11 12 if ( 13 existing_student := await Student.find_one({"_id": id}, as_raw=True) 14 ) is not None: 15 return json_response(existing_student) 16 17 raise NotFound(f"Student {id} not found")
A rota
update_student
é como uma combinação das rotascreate_student
e show_student
. Ele recebe o 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 a id
. Um modified_count
de zero pode significar que não há nenhum documento com esse id
. Isso também pode significar que o documento existe, mas não precisou ser atualizado, pois os valores atuais são os mesmos fornecidos na solicitaçãoPUT
.Somente após essa falha final
find
é que levantamos uma exceção404
Not Found.1 2 async def delete_student(request, id): 3 delete_result = await Student.delete_one({"_id": id}) 4 5 if delete_result.deleted_count == 1: 6 return json_response({}, status=204) 7 8 raise NotFound(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
.Esperemos que esta introdução à Sanic com MongoDB tenha sido útil para você. Se quiser saber mais sobre a Sanic, consulte a documentação deles. Infelizmente, a documentação do sanic-driver está totalmente ausente no momento. Mas é um encapsulador relativamente fino em torno do driver do MongoDB Motor-que está bem documentado- então não deixe que isso o desanime.
Para ver como você pode integrar o MongoDB a outros frameworks assíncronos, confira alguns dos outros posts sobre Python no portal do desenvolvedor do 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.