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.


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).


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.


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

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


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

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 %}

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.


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?


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.
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.