django - dlaczego obiekt request.POST jest niezmienny?


109

Jak pyta tytuł, dlaczego chłopaki z Django zdecydowali się zaimplementować obiekt request.POST za pomocą querydict (co oczywiście sprawia, że ​​całość jest niezmienna?)

Wiem, że możesz to zmienić , wykonując kopię danych postu

post = request.POST.copy()

ale dlaczego to robisz? Z pewnością prościej byłoby po prostu pozwolić, aby i tak było to zmienne? A może jest używany również z innego powodu, który może powodować problem?


1
Dlaczego chcesz, aby był zmienny? Możesz pobrać z niego dane i używać / modyfikować je w swoim widoku. Dodając do niego dane, można stworzyć wrażenie, które request.POSTzostało przesłane z większą ilością danych niż w rzeczywistości.
Simeon Visser

11
Nie chodzi o to, że chcę , aby był zmienny. Nie więcej niż, powiedzmy, chciałbym, żeby lody były zimne. Jednak w przypadku lodów, jeśli nie zimne, topią się i wtedy zostajesz zbesztany za robienie wielkiego bałaganu. Ale z obiektem request.POST ... To znaczy, jeśli mam spieprzyć swój kod, to schrzanię to. Nie zdawałem sobie sprawy, że twórcy często dodają dane do obiektów POST i powodują problemy, więc wydaje się dziwne, aby „naprawić” cel.
bharal

Fajne pytanie; nigdy o tym nie myślałem.
Burhan Khalid,

1
Zdarzało mi się to sporadycznie, ponieważ mój klient czasami przesyłał dane JSON (zmienne), a czasami wiadomości zakodowane w formacie URL (niezmienne).
owenfi

2
W przypadku osób nie mówiących po angielsku „mutify” nie jest słowem - prawidłowe wyrażenie to „możesz to zmienić” lub „możesz to zmodyfikować”. Nie ma też potrzeby przypisywania płci programistom - możesz użyć określenia „zespół Django” lub „core devs” zamiast „guys”.
alexmuller

Odpowiedzi:


131

To trochę tajemnica, prawda? Kilka powierzchownie wiarygodnych teorii okazuje się błędnych w badaniu:

  1. Aby POSTobiekt nie musiał implementować metod mutacji? Nie: POSTprzedmiot należący do django.http.QueryDictklasy , która implementuje pełen zestaw metod mutacji tym __setitem__, __delitem__, popi clear. Implementuje niezmienność, sprawdzając flagę podczas wywoływania jednej z metod mutacji. A kiedy wywołasz copymetodę, otrzymasz kolejną QueryDictinstancję z włączoną flagą mutable.

  2. Aby poprawić wydajność? Nie: QueryDictklasa nie zyskuje na wydajności, gdy flaga mutacji jest wyłączona.

  3. Aby POSTobiekt mógł służyć jako klucz słownika? Nie: QueryDictobiekty nie podlegają hashowaniu.

  4. Aby POSTdane mogły być budowane leniwie (bez zobowiązania się do przeczytania całej odpowiedzi), jak tu twierdzono ? Nie widzę tego dowody w kodzie: o ile mogę powiedzieć, cała odpowiedź jest zawsze czytać, albo bezpośrednio , albo za pośrednictwem MultiPartParserza multipartodpowiedzi.

  5. Aby uchronić Cię przed błędami programistycznymi? Widziałem, jak to twierdzono, ale nigdy nie widziałem dobrego wyjaśnienia, czym są te błędy i jak niezmienność chroni cię przed nimi.

W każdym razie, POSTto nie zawsze niezmienne : gdy odpowiedź jest multipart, to POSTjest zmienny. To wydaje się stawiać kibosha na większości teorii, o których możesz pomyśleć. (Chyba że takie zachowanie jest przeoczeniem).

Podsumowując, nie widzę jasnego uzasadnienia w Django, aby POSTobiekt był niezmienny dla nie- multipartżądań.


W Django zauważyłem mnóstwo ostrych krawędzi. Jednak w pewnym momencie musiało to mieć dla kogoś sens.
Dan Passaro,

2
Znalazłem to w innej odpowiedzi stosu: „I musi być niezmienna, aby można ją było leniwie budować. Kopia wymusza pobranie wszystkich danych POST. Do czasu skopiowania może nie zostać pobrana całość. Ponadto, dla wielowątkowego WSGI serwer działa w miarę dobrze, dobrze jest, jeśli jest to niezmienne ”
Seaux,

12
@Seaux, nie powinieneś leniwie czytać odpowiedzi SO, gdy masz zamiar je skomentować. ;-)
Chris Wesseling

3
@ChrisWesseling Widzę, co tam zrobiłeś
Seaux

2
Co więcej, querydict jest zmienny, gdy wysyłam żądanie do klienta testowego django.
user1158559

82

Jeśli żądanie było wynikiem formprzesłania Django , wówczas POST immutablepowinien zapewnić integralność danych między wysłaniem formularza a walidacją formularza . Jeśli jednak żądanie nie zostało wysłane za pośrednictwem przesłania Django form, POST jest, mutableponieważ nie ma walidacji formularza.

Zawsze możesz zrobić coś takiego: (zgodnie z komentarzem @ leo-the-manic )

#  .....
mutable = request.POST._mutable
request.POST._mutable = True
request.POST['some_data'] = 'test data'
request.POST._mutable = mutable
# ......

3
@JoshK: Wydaje mi się, że komentator chciał, aby POST był zmienny, a fragment kodu w tej odpowiedzi pomógł.
ShreevatsaR

Możesz dodać nowy klucz, wartość, ale nie możesz zmienić istniejących danych.
Vamsidhar Muggulla

Miły. Jestem pewien, że ktokolwiek używa tego kodu, wie, co robi.
John Pang,

@VamsidharMuggulla Możliwe jest zarówno dodawanie, jak i zmiana. Dozwolone jest nawet usuwanie.
Antony Hatchkins

5

Aktualizacja :

Gareth Rees miał rację, że punkty 1 i 3 nie były ważne w tym przypadku. Chociaż myślę, że punkty 2 i 4 są nadal aktualne, dlatego tezy zostawię tutaj.

(Zauważyłem, że request.POSTobiekt zarówno Pyramid (Pylon), jak i Django jest jakąś formą MultiDict. Więc być może jest to bardziej powszechna praktyka niż uczynienie request.POSTniezmiennym.)


Nie mogę mówić w imieniu chłopaków z Django, chociaż wydaje mi się, że może to z kilku następujących powodów:

  1. Wydajność . Niezmienne obiekty są „szybsze” niż zmienne, ponieważ pozwalają na znaczące optymalizacje. Obiekt jest niezmienny, co oznacza, że ​​możemy przydzielić mu miejsce w czasie tworzenia , a wymagania przestrzenne się nie zmieniają. Z tego powodu ma również takie rzeczy, jak wydajność kopiowania i wydajność porównania. Edycja : tak nie jest,QueryDictjak wskazał Gareth Rees.
  2. W przypadku request.POSTwydaje się, że żadna aktywność po stronie serwera nie powinna powodować konieczności zmiany danych żądania . A zatem niezmienne obiekty są bardziej odpowiednie, nie wspominając o tym, że mają znaczną przewagę wydajnościową.
  3. Niezmienne obiekty mogą być używane jako dictklucze, które ja przypuszczam może być bardzo przydatna gdzieś w Django .. Edit : moja pomyłka, niezmienne bezpośrednio nie sugerować hashable ; obiekty, które można mieszać , są jednak również zwykle niezmienne .
  4. Kiedy mijasz request.POST(szczególnie do wtyczek innych firm i poza nimi), możesz spodziewać się, że obiekt żądania od użytkownika pozostanie niezmieniony.

W pewnym sensie te powody są również ogólnymi odpowiedziami na pytanie „niezmienny czy zmienny?” pytanie. Jestem pewien, że w przypadku Django istnieje znacznie więcej rozważań projektowych niż powyżej.


1
Ostatni przypadek jest naprawdę ważny. Naprawdę chodzi o bezpieczeństwo. Dlatego Django zapewnia sessionskrótkotrwały sposób uzyskiwania i modyfikowania danych między stanami.
CppLearner

2
Twój punkt (1) nie może być odpowiedzią w tym przypadku, ponieważ POSTjest QueryDictobiektem , a te obiekty nie mają żadnej korzyści z wydajności, będąc niezmiennymi. Twój punkt (3) nie może być odpowiedzią, ponieważ QueryDictobiekty nie są hashowane, a więc nie mogą być używane jako klucze słownikowe.
Gareth Rees,

@GarethRees Dzięki za wskazanie tych rzeczy. Rzeczywiście się myliłem. Zaktualizowałem odpowiedź, aby je poprawić. Powinienem był poświęcić więcej uwagi, QueryDictzanim odpowiedziałem.
KZ

7
@CppLearner Punkt bezpieczeństwa wydaje się dyskusyjny, np.requests.POST._mutable = True; requests.POST['foo'] = 'bar'; request.POST._mutable = False
Dan Passaro

4

Podoba mi się, że domyślnie jest niezmienny. Jak wskazano, możesz uczynić go zmiennym, jeśli potrzebujesz, ale musisz to wyraźnie powiedzieć. To coś w stylu „Wiem, że debugowanie formularza może stać się koszmarem, ale wiem, co teraz robię”.


2

Znalazłem to w komentarzu do odpowiedzi stosu https://stackoverflow.com/a/2339963

I musi być niezmienny, żeby można go było leniwie budować. Kopia wymusza pobranie wszystkich danych POST. Do czasu skopiowania nie wszystko może zostać pobrane. Ponadto, aby wielowątkowy serwer WSGI działał w miarę dobrze, pomocne jest, jeśli jest to niezmienne


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.