django - por que o objeto request.POST é imutável?

109

Como o título pergunta, por que os caras do Django decidiram implementar o objeto request.POST com um querydict (que, é claro, por sua vez, torna tudo imutável?)

Eu sei que você pode modificá- lo fazendo uma cópia dos dados da postagem

post = request.POST.copy()

mas por que fazer isso? Certamente seria mais simples permitir que a coisa fosse mutável de qualquer maneira? Ou está sendo usado por algum outro motivo que pode causar problemas?

bharal
fonte
1
Por que você quer que seja mutável? Você pode obter os dados dele e usá-los / modificá-los em sua visualização. Ao adicionar dados a ele, você pode criar a impressão de que request.POSTfoi enviado com mais dados do que realmente foi.
Simeon Visser
11
Não é que eu queira que seja mutável. Não mais do que, digamos, eu gostaria que o sorvete estivesse frio. Porém, no caso do sorvete, se não estiver frio, ele derrete e você é repreendido por fazer uma bagunça enorme. Mas com o objeto request.POST ... Quer dizer, se vou bagunçar meu código, vou bagunçar tudo. Eu não sabia que havia uma endemia de desenvolvedores adicionando dados a objetos POST e causando problemas, então parece uma coisa estranha para "consertar".
bharal
Boa pergunta; nunca pensei nisso realmente.
Burhan Khalid
1
Isso surgiu esporadicamente para mim porque meu cliente às vezes enviava dados JSON (mutáveis) e às vezes mensagens codificadas por formulário de URL (imutáveis).
owenfi
2
Para quem não fala inglês, "mutificar" não é uma palavra - a frase correta é "você pode alterar" ou "você pode modificar". Também não há necessidade de definir o gênero dos desenvolvedores - você pode usar "Django team" ou "core devs" ao invés de "guys".
alexmuller

Respostas:

131

É um pouco misterioso, não é? Várias teorias superficialmente plausíveis revelaram-se erradas na investigação:

  1. Para que o POSTobjeto não precise implementar métodos de mutação? Não: o POSTobjeto pertence à django.http.QueryDictclasse , que implementa um conjunto completo de métodos de mutação, incluindo __setitem__, __delitem__, pope clear. Ele implementa a imutabilidade verificando um sinalizador quando você chama um dos métodos de mutação. E quando você liga para ocopy método, você obtém outra QueryDictinstância com o sinalizador mutável ativado.

  2. Para melhoria de desempenho? Não: oQueryDict classe não ganha nenhum benefício de desempenho quando o sinalizador mutável é desativado.

  3. Para que o POST objeto possa ser usado como chave de dicionário? Não: os QueryDictobjetos não são hashable.

  4. Para que os POSTdados possam ser construídos preguiçosamente (sem se comprometer a ler toda a resposta), como afirmado aqui ? Não vejo evidência disso no código: pelo que posso dizer, toda a resposta é sempre lida, diretamente ou por meio MultiPartParserde multipartrespostas.

  5. Para protegê-lo contra erros de programação? Já vi isso ser reivindicado, mas nunca vi uma boa explicação sobre o que são esses erros e como a imutabilidade o protege contra eles.

Em qualquer caso, POSTé não sempre imutável : quando a resposta é multipart, em seguida, POSTé mutável. Isso parece acabar com a maioria das teorias que você possa imaginar. (A menos que esse comportamento seja um descuido.)

Em resumo, não consigo ver nenhuma justificativa clara no Django para o POSTobjeto ser imutável para não- multipartrequisições.

Gareth Rees
fonte
Eu percebi toneladas de arestas como essa no Django. Deve ter feito sentido para alguém em algum momento, no entanto.
Dan Passaro
2
Encontrei isso em outra resposta do Stack: "E deve ser imutável para que possa ser construído lentamente. A cópia força a obtenção de todos os dados POST. Até a cópia, nem todos podem ser buscados. Além disso, para um WSGI multithread servidor funcione razoavelmente bem, é útil se for imutável "
Seaux
12
@Seaux você não deve ler as respostas do SO preguiçosamente quando pretende comentá-las. ;-)
Chris Wesseling
3
@ChrisWesseling Vejo o que você fez lá
Seaux
2
Melhor ainda, o querydict é mutável quando eu envio a requisição processando o cliente de teste django.
user1158559
82

Se a solicitação foi o resultado de um formenvio do Django , então é razoável para o POST immutablegarantir a integridade dos dados entre o envio do formulário e a validação do formulário . No entanto, se a solicitação não foi enviada por meio de um formenvio do Django , o POST émutable pois não há validação do formulário.

Você sempre pode fazer algo assim: (de acordo com o comentário de @leo-the-manic )

#  .....
mutable = request.POST._mutable
request.POST._mutable = True
request.POST['some_data'] = 'test data'
request.POST._mutable = mutable
# ......
un33k
fonte
3
@JoshK: Acho que o comentador queria tornar o POST mutável, e o trecho de código nesta resposta ajudou.
ShreevatsaR
Você pode adicionar uma nova chave, valor, mas não pode alterar os dados existentes.
Vamsidhar Muggulla
Agradável. E tenho certeza de que quem usa esse código sabe o que está fazendo.
John Pang de
@VamsidharMuggulla É possível adicionar e alterar. Até mesmo a exclusão é permitida.
Antony Hatchkins
5

Atualização :

Gareth Rees estava certo ao dizer que os pontos 1 e 3 não eram válidos neste caso. Embora eu ache que os pontos 2 e 4 ainda sejam válidos, deixarei as teses aqui.

(Percebi que o request.POSTobjeto de ambos Pyramid (Pylon) e Django é alguma forma de MultiDict. Portanto, talvez seja uma prática mais comum do que tornar request.POSTimutável.)


Não posso falar pelos caras do Django, embora me pareça que poderia por alguns destes motivos:

  1. Performance . objetos imutáveis ​​são "mais rápidos" do que os mutáveis, pois permitem otimizações substanciais. Um objeto é imutável significa que podemos alocar espaço para ele no momento da criação e os requisitos de espaço não estão mudando. Ele também tem coisas como eficiência de cópia e eficiência de comparação por causa disso. Edit : este não é o caso deQueryDictcomo Gareth Rees apontou.
  2. No caso de request.POST, parece que nenhuma atividade no lado do servidor deve precisar alterar os dados da solicitação . E, portanto, os objetos imutáveis ​​são mais adequados, sem mencionar que têm uma vantagem substancial de desempenho.
  3. Objetos imutáveis ​​podem ser usados ​​como dictchaves, o que eu suponho que podem ser muito úteis em algum lugar do Django .. Edit : meu erro, imutável não implica diretamente em hash ; objetos hashable , entretanto, são tipicamente imutáveis também.
  4. Ao passar request.POST(especialmente para plug-ins de terceiros e outros), você pode esperar que esse objeto de solicitação do usuário permaneça inalterado.

De alguma forma, essas razões também são respostas genéricas para "imutável versus mutável?" questão. Estou certo de que há muito mais considerações de design do que acima no caso do Django.

KZ
fonte
1
O último caso é muito importante. É realmente sobre segurança. É por isso que o Django fornece sessionsuma maneira curta de obter e modificar dados entre estados.
CppLearner
2
Seu ponto (1) não pode ser a resposta neste caso, porque POSTé um QueryDictobjeto , e esses objetos não obtêm nenhum benefício de desempenho por serem imutáveis. E seu ponto (3) não pode ser a resposta, porque os QueryDictobjetos não têm hash e, portanto, não podem ser usados ​​como chaves de dicionário.
Gareth Rees
@GarethRees Obrigado por apontar isso. Na verdade, eu estava errado. Eu atualizei minha resposta para corrigir isso. Eu deveria ter prestado mais atenção QueryDictantes de responder.
KZ
7
@CppLearner O ponto de segurança parece discutível, por exemplorequests.POST._mutable = True; requests.POST['foo'] = 'bar'; request.POST._mutable = False
Dan Passaro
4

Eu gosto de ser imutável por padrão. Como apontado, você pode torná-lo mutável se precisar, mas deve ser explícito sobre isso. É como 'Eu sei que posso tornar a depuração de meu formulário um pesadelo, mas sei o que estou fazendo agora.'

Pawelmech
fonte
2

Eu encontrei isso em um comentário na Stack Answer https://stackoverflow.com/a/2339963

E deve ser imutável para que possa ser construído preguiçosamente. A cópia força a obtenção de todos os dados POST. Até a cópia, nem tudo pode ser obtido. Além disso, para um servidor WSGI multi-threaded funcionar razoavelmente bem, é útil se isso for imutável

Seaux
fonte