Estou usando Django Rest Framework e AngularJs para fazer upload de um arquivo. Meu arquivo de visualização é assim:
class ProductList(APIView):
authentication_classes = (authentication.TokenAuthentication,)
def get(self,request):
if request.user.is_authenticated():
userCompanyId = request.user.get_profile().companyId
products = Product.objects.filter(company = userCompanyId)
serializer = ProductSerializer(products,many=True)
return Response(serializer.data)
def post(self,request):
serializer = ProductSerializer(data=request.DATA, files=request.FILES)
if serializer.is_valid():
serializer.save()
return Response(data=request.DATA)
Como a última linha do método post deve retornar todos os dados, tenho várias perguntas:
- como verificar se há algo dentro
request.FILES
? - como serializar o campo do arquivo?
- como devo usar o analisador?
Respostas:
Use o FileUploadParser , está tudo na solicitação. Em vez disso, use um método put. Você encontrará um exemplo na documentação :)
fonte
Estou usando a mesma pilha e também procurando um exemplo de upload de arquivo, mas meu caso é mais simples, pois uso o ModelViewSet em vez do APIView. A chave acabou sendo o gancho pre_save. Acabei usando junto com o módulo de upload de arquivo angular assim:
fonte
Finalmente, consigo fazer upload de imagens usando Django. Aqui está meu código de trabalho
views.py
urls.py
pedido curl para fazer upload
fonte
with open('/Users/Username/' + up_file.name, 'wb+') as destination:
e remover totalmente o fechamentoModelViewSet
. Além disso, eles provavelmente o implementaram melhor.FileUploadParser
é necessário, masMultiPartParser
!Depois de passar 1 dia nisso, descobri que ...
Para alguém que precisa fazer upload de um arquivo e enviar alguns dados, não há uma maneira direta de fazer isso funcionar. Há um problema aberto nas especificações da API JS para isso. Uma possibilidade que vi é usar
multipart/related
como mostrado aqui , mas acho muito difícil implementá-lo no DRF.Por fim, o que implementei foi enviar a solicitação como
formdata
. Você enviaria cada arquivo como arquivo e todos os outros dados como texto. Agora, para enviar os dados como texto, você tem duas opções. caso 1) você pode enviar cada dado como par de valores-chave ou caso 2) você pode ter uma única chave chamada dados e enviar o json inteiro como string em valor.O primeiro método funcionaria imediatamente se você tiver campos simples, mas será um problema se você tiver serializações aninhadas. O analisador multipartes não será capaz de analisar os campos aninhados.
Abaixo, estou fornecendo a implementação para ambos os casos
Models.py
serializers.py -> nenhuma mudança especial necessária, não mostrando meu serializador aqui como muito extenso devido à implementação gravável do campo ManyToMany.
views.py
Agora, se você estiver seguindo o primeiro método e apenas enviando dados não Json como pares de valores-chave, não precisará de uma classe de analisador customizada. O MultipartParser do DRF fará o trabalho. Mas para o segundo caso, ou se você tiver serializadores aninhados (como eu mostrei), você precisará do analisador personalizado conforme mostrado abaixo.
utils.py
Este serializador basicamente analisaria qualquer conteúdo json nos valores.
O exemplo de solicitação no correio para ambos os casos: caso 1 ,
Caso 2
fonte
Resolvi esse problema com ModelViewSet e ModelSerializer. Espero que isso ajude a comunidade.
Eu também prefiro ter validação e login de Objeto-> JSON (e vice-versa) no próprio serializador em vez de em visualizações.
Vamos entender por exemplo.
Digamos, eu quero criar a API FileUploader. Onde ele irá armazenar campos como id, file_path, file_name, size, owner etc no banco de dados. Veja o modelo de amostra abaixo:
Agora, para APIs, isso é o que eu quero:
Quando eu disparar o endpoint GET, quero todos os campos acima para cada arquivo carregado.
Mas para o usuário criar / fazer upload de arquivo, por que ela precisa se preocupar em passar todos esses campos. Ela pode simplesmente fazer upload do arquivo e, então, suponho que o serializador pode obter o restante dos campos de FILE carregado.
Searilizer: Pergunta: Eu criei o serializador abaixo para servir ao meu propósito. Mas não tenho certeza se é a maneira certa de implementá-lo.
Viewset para referência:
fonte
FileUploaderSerializer.validate
método contém?Pela minha experiência, você não precisa fazer nada em particular sobre os campos do arquivo, apenas diga a ele para usar o campo do arquivo:
e você está pronto para fazer upload de arquivos:
Adicione
-F field=value
para cada campo extra que seu modelo possui. E não se esqueça de adicionar autenticação.fonte
Se alguém estiver interessado no exemplo mais fácil com ModelViewset for Django Rest Framework.
O modelo é,
O serializador,
E a visão é,
Teste no Postman,
fonte
No django-rest-framework, os dados do pedido são analisados pelo
Parsers
.http://www.django-rest-framework.org/api-guide/parsers/
Por padrão, o django-rest-framework leva classe de analisador
JSONParser
. Ele analisará os dados em json. portanto, os arquivos não serão analisados com ele.Se quisermos que os arquivos sejam analisados junto com outros dados, devemos usar uma das classes de analisador abaixo.
fonte
application/json
,application/x-www-form-urlencoded
emultipart/form-data
.fonte
fonte
Gostaria de escrever outra opção que considero mais limpa e fácil de manter. Estaremos usando o defaultRouter para adicionar urls CRUD para nosso conjunto de visualizações e adicionaremos mais um url fixo especificando a visualização do uploader dentro do mesmo conjunto de visualizações.
Urls.py principal do projeto
.- LEIA-ME.
A mágica acontece quando adicionamos @action decorator ao nosso método de classe 'uploader'. Ao especificar o argumento "methods = ['put']", estamos permitindo apenas solicitações PUT; perfeito para upload de arquivos.
Também adicionei o argumento "parser_classes" para mostrar que você pode selecionar o analisador que analisará seu conteúdo. Eu adicionei CSVParser do pacote rest_framework_csv, para demonstrar como podemos aceitar apenas certos tipos de arquivos se essa funcionalidade for necessária, no meu caso, estou aceitando apenas "Content-Type: text / csv". Nota: Se você estiver adicionando analisadores personalizados, você precisará especificá-los em parsers_classes no ViewSet porque a solicitação irá comparar o media_type permitido com os analisadores principais (classe) antes de acessar os analisadores do método uploader.
Agora precisamos dizer ao Django como ir para esse método e onde ele pode ser implementado em nossos urls. É quando adicionamos o url fixo (fins simples). Este Url terá um argumento "nome do arquivo" que será passado no método posteriormente. Precisamos passar este método "uploader", especificando o protocolo http ('PUT') em uma lista para o método PostsViewSet.as_view.
Quando pousamos no seguinte url
ele irá esperar uma solicitação PUT com cabeçalhos especificando "Content-Type" e Content-Disposition: attachment; filename = "alguma coisa.csv".
fonte
parser_classes
não existe para limitar quais arquivos podem ser carregados. Permite que você decida quais formatos podem ser usados para fazer solicitações. Pensando bem, a maneira como você lida com o upload ... parece que você está colocando dados do CSV no banco de dados. Não foi o que OP perguntou.Esta é a abordagem que apliquei, espero que ajude.
fonte
Você pode generalizar a resposta do @Nithin para trabalhar diretamente com o sistema serializador existente do DRF, gerando uma classe de analisador para analisar campos específicos que são alimentados diretamente nos serializadores DRF padrão:
Isso é usado como:
fonte
Se você estiver usando ModelViewSet, na verdade você terminou! Ele lida com todas as coisas para você! Você só precisa colocar o campo em seu ModelSerializer e definir
content-type=multipart/form-data;
em seu cliente.MAS como você sabe não é possível enviar arquivos no formato json. (quando o tipo de conteúdo é definido como application / json em seu cliente). A menos que você use o formato Base64.
Então você tem duas opções:
ModelViewSet
eModelSerializer
cuide do trabalho e envie a solicitação usandocontent-type=multipart/form-data;
ModelSerializer
comoBase64ImageField (or) Base64FileField
e diga ao seu cliente para codificar o arquivo paraBase64
e definir ocontent-type=application/json
fonte
models.py
serializers.py
views.py
urls.py
settings.py
Envie uma solicitação de postagem para
api/files
com seu arquivo anexado a umform-data
campofile
. O arquivo será carregado para a/media
pasta e um registro db será adicionado com o id e o nome do arquivo.fonte