{
"path": "/upload-de-arquivos-no-django-entendendo-os-modos-de-leitura.html",
"site": "at://did:plc:3272gdrjsuikiff7qsgokgas/site.standard.publication/3mjaxtes2yf2v",
"tags": [
"pt",
"Python",
"Django",
"Backend"
],
"$type": "site.standard.document",
"title": "Upload de arquivos no Django: entendendo os modos de leitura",
"publishedAt": "2016-02-26T00:00:00.000Z",
"textContent": "_Post originalmente publicado no Python Club._\n\n *\n\nEm uma conversa com a galera do Welcome to the Django acabei experimentando e aprendendo – na prática — sobre _csv_, _strings_, _bytes_, _file object_ e a maneira como uploads funcionam. Registrei minha exploração e espero que mais gente possa encontrar uma ou outra coisa nova aqui!\n\nO problema\n\nFui alterar um projeto feito com Django, atualizando do Python 2 para o Python 3, e me deparei com um pedaço de uma _view_ que, como o Henrique Bastos falou, funcionava “por acaso” no Python 2:\n\nEssa _view_ recebe um arquivo CSV (upload do usuáio) e só processa as linhas do arquivo, sem salvá-lo em disco. No Python 3, esse trecho da _view_ passou a dar erro:\n\nO Henrique, além de falar que o código funcionava “por acaso”, me lembrou que o csv.reader(…) já recebe um arquivo aberto. Assim fui explorar a maneira que o Django estava me entregando os arquivos no HttpRequest (no caso da minha _view_, o que eu tinha em mãos no request.FILES['file.csv']).\n\nSimulando o ambiente da _view_\n\nPara explorar isso, eu precisava simular o ambiente da minha _view_. Comecei criando um arquivo simples, teste.txt:\n\nDepois fui ler a documentação do HttpRequest.FILES e descobri que os arquivos ali disponíveis são instâncias de UploadedFile.\n\nLogo, se eu criar uma instância da classe UploadedFile, posso acessar um objeto do mesmo tipo que eu acessava na _view_ pelo request.FILES['file.csv']. Para criar essa instância, preciso de um arquivo aberto, algo como open(file_path, modo). Para continuar a simulação, eu precisava saber de que forma o Django abre o arquivo do upload quando instancia ele no HttpRequest.FILES.\n\nEu desconfiava que não era em texto (r), que era em binário (rb). A documentação do curl, por exemplo, indicava que os arquivos eram enviados como binários. A documentação da Requests tem um aviso grande, em vermelho, desencorajando qualquer um usar outro modo que não o binário.\n\nLendo mais sobre o UploadedFile descobri que esse objeto tem um atributo file que, é uma referência ao file object nativo do Python que a classe UploadFile envolve. E esse atributo file, por sua vez, tem o atributo mode que me diz qual o modo foi utilizado na abertura do arquivo. Fui lá na minha _view_ e dei um print(request.FILES['file.csv'].file.mode) e obtive rb como resposta.\n\nPronto! Finalmente eu tinha tudo para simular o ambiente da _view_ no meu IPython:\n\nAssim testei o trecho que dava problema…\n\n… e obtive o mesmo erro.\n\nSolução\n\nComo já tinha ficado claro, o arquivo estava aberto como binário. Isso dá erro na hora de usar o csv.reader(…), pois o csv.reader(…) espera um texto, _string_ como argumento. Aqui nem precisei ler a documentação, só lembrei da mensagem de erro: _did you open the file in text mode?_ – ou seja, _você abriu o arquivo no modo texto?_\n\nLendo a documentação do UploadedFile e do File do Django (já que a primeira herda da segunda), achei dois métodos úteis: o close() e o open(). Com eles fechei o arquivo que estava aberto no modo rb e (re)abri o mesmo arquivo como r:\n\nAgora sim o arquivo está pronto para o csv.reader(…):\n\nEnfim, esse métodos UploadedFile.close() e UploadedFile.open(mode=mode) podem ser muito úteis quando queremos fazer algo diferente de gravar os arquivos recebidos em disco.\n\n> Quem aprendeu alguma coisa nova?\n>\n> — Raymond Hettinger",
"canonicalUrl": "https://cuducos.me/upload-de-arquivos-no-django-entendendo-os-modos-de-leitura.html"
}