Django: Jak przekierować wiadomość i przekazać jej dane

80

Podczas przetwarzania żądania POST w pliku Django views.py, czasami muszę przekierować go do innego adresu URL. Ten adres URL, do którego przekierowuję, jest obsługiwany przez inną funkcję w tym samym pliku Django views.py. Czy można to zrobić i zachować oryginalne dane POST?

UPDATE: Więcej wyjaśnień, dlaczego chcę to zrobić. Mam dwie aplikacje internetowe (nazwijmy je AppA i AppB), które akceptują dane wprowadzone w polu tekstowym przez użytkownika. Gdy użytkownik kliknie przycisk Prześlij, dane są przetwarzane i wyświetlane są szczegółowe wyniki. AppA i AppB oczekują różnych typów danych. Czasami użytkownik omyłkowo wysyła dane typu AppB do AppA. Kiedy tak się stanie, chcę przekierować ich do AppB i pokazać wyniki AppB lub przynajmniej wypełnić je danymi, które wprowadzili do AppA.

Również:

  • Klient chce mieć dwie oddzielne aplikacje, zamiast łączyć je w jedną.

  • Nie mogę pokazać kodu, ponieważ należy on do klienta.

UPDATE 2: Zdecydowałem, że KISS jest tutaj najlepszą zasadą. Połączyłem te dwie aplikacje w jedną, dzięki czemu wszystko jest prostsze i solidniejsze; Powinienem być w stanie przekonać klienta, że ​​to też najlepszy sposób. Dzięki za wszystkie wspaniałe opinie. Gdybym miał zachować dwie aplikacje zgodnie z opisem, myślę, że sesje byłyby sposobem na to - dzięki Matthew J Morrisonowi za zasugerowanie tego. Dzięki Dzidzie, ponieważ jego uwagi skłoniły mnie do zastanowienia się nad projektem i uproszczeniem.

FunLovinCoder
źródło
czy naprawdę musisz wysłać przekierowanie do klienta, czy też można to zrobić, po prostu wywołując funkcję i przekazując do niej wszystkie dane postu?
Matthew J Morrison
Muszę zmienić adres URL w przeglądarce klienta, więc jest to jedyny sposób, w jaki mogę to zrobić.
FunLovinCoder
i nie możesz po prostu wykonać całego przetwarzania z danymi pocztowymi, a następnie przekierować po fakcie?
Matthew J Morrison
Mam podobną sytuację, ale dane przesłane w POST są lub nie są dopasowane do istniejących danych. Jeśli pasuje, otrzymuję identyfikator tych danych, a następnie przekazuję ten identyfikator do skryptu za pośrednictwem zmiennej GET w przekierowaniu. Zapisuję również dane POST w SESSION. Teraz przekierowana strona ładuje dane, do których odwołuje się idGET, a także ma dostęp do innych danych przesłanych przez POST.
Buttle Butkus,

Odpowiedzi:

57

Jeśli napotkałeś taki problem, istnieje niewielka szansa, że ​​będziesz musiał zrewidować swoje projekty.

Jest to ograniczenie protokołu HTTP, którego dane POST nie mogą przejść z przekierowaniami.

Czy możesz opisać, co próbujesz osiągnąć, a może wtedy pomyśleć o jakimś zgrabnym rozwiązaniu.

Jeśli nie chcesz używać sesji, jak sugerował Matthew, możesz przekazać parametry POST w GET do nowej strony (rozważ pewne ograniczenia, takie jak bezpieczeństwo i maksymalna długość parametrów GET w ciągu zapytania).

ZAKTUALIZUJ swoją aktualizację :) Dziwnie mi się wydaje, że masz 2 aplikacje internetowe i te aplikacje używają jednego views.py (mam rację?). W każdym razie rozważ przekazanie danych z POST w GET do odpowiedniego widoku (oczywiście w przypadku, gdy dane nie są wrażliwe).

dzida
źródło
2
Widzę, że to, co próbuje zrobić, może być ważne, jeśli próbuje obsłużyć wygasły login, który zmusi użytkownika do zalogowania się po przesłaniu formularza ... w takim przypadku będzie chciał zachować dane, które zostały przesłane i nie zmuszają użytkownika do ponownego wprowadzania wszystkich danych po wypełnieniu ekranu logowania.
Matthew J Morrison,
Nie jestem pewien, czy rozumiem, ale w tym przypadku operację logowania można wykonać za pomocą pierwszego widoku z niewielką modyfikacją kodu i bez zbędnych przekierowań. Byłoby wspaniale przeczytać istniejący kod, aby uzyskać dokładniejsze porady.
dzida
Mówię, że jeśli prześlesz formularz, a nie jesteś zalogowany, zostaniesz przekierowany do formularza logowania ... w tym scenariuszu stracisz wszystko, co przesłałeś. Zgadzam się, żebym mógł zobaczyć istniejący kod.
Matthew J Morrison
1
to nie od ciebie zależy, czy przypadek użycia jest ważny, czy nie, mam taką samą sytuację, w której przekierowanie ze świeżym postem POST jest idealnym rozwiązaniem pod względem prostoty i modułowości.
Rabih Kodeih
54

Myślę, że prawdopodobnie poradziłbym sobie z tą sytuacją, gdyby zapisał dane posta w sesji, a następnie usunął go, gdy już go nie potrzebuję. W ten sposób mogę uzyskać dostęp do oryginalnych danych postu po przekierowaniu, mimo że ten post zniknął.

Czy to zadziała w przypadku tego, co próbujesz zrobić?

Oto przykład kodu tego, co sugeruję: (pamiętaj, że jest to kod nieprzetestowany)

def some_view(request):
    #do some stuff
    request.session['_old_post'] = request.POST
    return HttpResponseRedirect('next_view')

def next_view(request):
    old_post = request.session.get('_old_post')
    #do some stuff using old_post

Jeszcze jedna rzecz, o której należy pamiętać ... jeśli robisz to i przesyłasz pliki, nie zrobiłbym tego w ten sposób.

Matthew J Morrison
źródło
1
Nigdy nie korzystałem z sesji, ale przyjrzę się temu dzięki.
FunLovinCoder
1
to nie jest najlepsza praktyka, ale nadal pomaga. Myślę, że w tym numerze nie dostaniemy nic ładniejszego.
Guilherme David da Costa
@GuilhermeDaviddaCosta dlaczego mówisz, że to nie jest najlepsza praktyka? Czy możesz nam podpowiedzieć?
Buttle Butkus,
ponieważ przechowywanie zbyt dużej ilości danych podczas sesji nie wydaje się dobrym pomysłem. Ale jak też powiedziałem, nie mogę wymyślić nic piękniejszego.
Guilherme David da Costa
4
Cóż, to zależy od tego, gdzie przechowujesz sesję. Ostatnio widziałem ludzi używających całego serwera z memcached do sesji i równoważenia obciążenia (przy użyciu okrężnego) każdego żądania. Nie chcę dawać Ci rady, której nie mogę się bronić, ale zapisałbym plik jako tymczasowy i otrzymałbym tylko link do niego w sesji. Wygląda na to, że obecnie nikomu nie brakuje pamięci RAM.
Guilherme David da Costa
23

Musisz użyć tymczasowego przekierowania HTTP 1.1 (307).

Niestety, Django redirect()i HTTPResponseRedirect (na stałe) zwracają tylko 301 lub 302. Musisz to zaimplementować samodzielnie:

from django.http import HttpResponse, iri_to_uri
class HttpResponseTemporaryRedirect(HttpResponse):
    status_code = 307

    def __init__(self, redirect_to):
        HttpResponse.__init__(self)
        self['Location'] = iri_to_uri(redirect_to)

Zobacz także moduł django.http .

Edytować:

w najnowszych wersjach Django zmień iri_to_uriimport na:

from django.utils.encoding import iri_to_uri
Lloeki
źródło
Nowsze wersje Django mają stałe przekierowanie HttpResponsePermanentRedirect, ale nie jestem pewien, czy rozwiązuje pierwotny problem docs.djangoproject.com/en/dev/ref/request-response/ ...
JiminyCricket
9

użyj requestspakietu.Jest bardzo łatwy do wdrożenia

pip install requests

wtedy możesz wywoływać adresy URL dowolną metodą i przesyłać dane

w Twoich widokach żądań importu

import requests

aby publikować dane, postępuj zgodnie z formatem

r = requests.post('http://yourdomain/path/', data = {'key':'value'})

aby uzyskać bezwzględny adres URL w widoku django, użyj

request.build_absolute_uri(reverse('view_name'))

Tak wygląda kod widoku django

r = requests.post(
            request.build_absolute_uri(reverse('view_name')), 
            data = {'key':'value'}
    )

gdzie rjest obiektem odpowiedzi z atrybutem status_codei content. r.status_codepodaje kod statusu (w przypadku pomyślnego zakończenia będzie to 200) i r.contenttreść odpowiedzi. Istnieje metoda json ( r.json()), która konwertuje odpowiedź na format json

upraszanie

request.post

Aneesh RS
źródło
4

Po prostu wywołaj nowy widok ze starego widoku, używając tego samego obiektu żądania. Oczywiście nie spowoduje to przekierowania jako takiego, ale jeśli wszystko, na czym Ci zależy, to „przesyłanie” danych z jednego widoku do drugiego, to powinno działać.
Przetestowałem następujący fragment kodu i działa.

from django.views.generic import View

class MyOldView(View):
    def post(self, request):
        return MyNewView().post(request)

class MyNewView(View):
    def post(self, request):
        my_data = request.body
        print "look Ma; my data made it over here:", my_data
Komu
źródło
1

Możesz z nim użyć renderowania i kontekstu :

Render(request,"your template path",        {'vad name' : var value}

Możesz otrzymać vars w szablonie:

{% If var name %}
 {{ var name }}
{% endif %}
Omid reza
źródło
1

Ostatnio miałem podobny problem.

Zasadniczo miałem formularz A, po przesłaniu go pojawił się inny formularz B, który zawiera niektóre wyniki + formularz. Po przesłaniu B chciałem wyświetlić alert użytkownikowi i zachować go tylko na B.

Sposób, w jaki to rozwiązałem, polega na wyświetleniu wyników w <output>polu, w B.

<output name="xyz" value="xyz">{{xyz}}</output>

I użyłem tego samego widoku dla A-> B i B-> B. Teraz musiałem tylko rozróżnić, czy żądanie pochodzi z A czy B i odpowiednio wyrenderować.

def view1(request):
    if "xyz" in request.POST:
        # request from B
        # do some processing
        return render(request, 'page.html', {"xyz":request.POST["xyz"]})
    else:
        # request from A
        res = foo() # some random function
        return render(request, 'page.html', {"xyz":res})

Ale to działa tylko wtedy, gdy forma B jest mała i nie tak dynamiczna.

rkp768
źródło
0

Jeśli używasz przekierowania po przetworzeniu POST do AppB, możesz faktycznie uciec z wywołaniem AppBmetody z AppAmetody.

Przykład:

def is_appa_request(request):
    ## do some magic.
    return False or True
is_appb_request = is_appa_request

def AppA(request):
    if is_appb_request(request):
       return AppB(request)
    ## Process AppA.
    return HttpResponseRedirect('/appa/thank_you/')

def AppB(request):
    if is_appa_request(request):
       return AppA(request)
    ## Process AppB.
    return HttpResponseRedirect('/appb/thank_you/')

Powinno to zapewnić przejrzystość dla użytkownika końcowego, a klient, który Cię zatrudnił, prawdopodobnie nigdy nie zauważy różnicy.

Jeśli nie przekierowujesz po POST, czy nie martwisz się zduplikowanymi danymi z powodu odświeżania strony przez użytkownika?

Jack M.
źródło
Byłoby wspaniale, gdyby takie proste rozwiązanie zadziałało. Muszę jednak wyświetlić szczegółowe wyniki po przetworzeniu danych. Wymagałoby to sesji (zaproponowanych przez Matthew J Morrisona), prawda?
FunLovinCoder,
1
Możesz to zrobić na trzy sposoby. # 1, zapisz dane w bazie danych i przekaż pknowy wpis podczas przekierowywania. # 2, zapisz dane na cachezapleczu i ponownie przekaż klucz. # 3, zapisz go w sesji. Każda z nich jest całkowicie normalna dla aplikacji internetowej, nawet jeśli jest tymczasowa. Jeśli dane formularza są nietrywialne do przeanalizowania, przyspieszyłoby to również system, gdyby dane wyjściowe były już buforowane.
Jack M.