Jak mogę uzyskać nazwę domeny mojej witryny w szablonie Django?


Odpowiedzi:


67

Myślę, że chcesz mieć dostęp do kontekstu żądania, zobacz RequestContext.


140
request.META['HTTP_HOST']daje Ci domenę. W szablonie byłoby to {{ request.META.HTTP_HOST }}.
— Daniel Roseman,

29
Uważaj, używając metadanych żądań. Pochodzi z przeglądarki i może zostać sfałszowany. Ogólnie rzecz biorąc, prawdopodobnie będziesz chciał skorzystać z tego, co sugeruje poniżej @CarlMeyer.
— Josh

2
Dla moich celów nie ma to luki w zabezpieczeniach.
— Paul Draper,

7
Wydaje mi się, że od Django 1.5 z ustawieniem dozwolonych hostów można go bezpiecznie używać. docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
— Daniel Backman

8
Czy ktoś może wyjaśnić, czym jest „dziura w zabezpieczeniach”? Jeśli użytkownik sfałszuje Host:nagłówek i otrzyma odpowiedź z fałszywą domeną gdzieś na stronie, w jaki sposób tworzy to lukę w zabezpieczeniach? Nie rozumiem, czym różni się to od tego, że użytkownik pobiera wygenerowany kod HTML i modyfikuje się przed przesłaniem go do własnej przeglądarki.
— user193130

105

Jeśli chcesz uzyskać rzeczywisty nagłówek hosta HTTP, zobacz komentarz Daniela Rosemana na temat odpowiedzi @ Phsiao. Inną alternatywą jest to, że jeśli korzystasz z platformy contrib.sites , możesz ustawić kanoniczną nazwę domeny dla Witryny w bazie danych (mapowanie domeny żądania do pliku ustawień z odpowiednim SITE_ID jest czymś, co musisz zrobić samodzielnie za pośrednictwem konfiguracja serwera WWW). W takim przypadku szukasz:

from django.contrib.sites.models import Site

current_site = Site.objects.get_current()
current_site.domain

musiałbyś sam umieścić obiekt current_site w kontekście szablonu, jeśli chcesz go użyć. Jeśli używasz go wszędzie, możesz to spakować w procesorze kontekstu szablonu.


3
Aby wyjaśnić komuś, kto ma te same problemy, co ja: sprawdź, czy twoje SITE_IDustawienie jest równe idatrybutowi bieżącej witryny w aplikacji Witryny (można ją znaleźć idw panelu administracyjnym Witryny). Kiedy dzwonisz get_current, Django pobiera twój SITE_IDi zwraca Siteobiekt z tym id z bazy danych.
— Dennis Golomazov

Żadne z nich nie działa dla mnie. print("get_current_site: ", get_current_site(request)) print("absolute uri: ", request.build_absolute_uri()) print("HTTP_HOST: ", request.META['HTTP_HOST']) get_current_site: localhost:8001 absolute uri: http://localhost:8001/... HTTP_HOST: localhost:8001
— user251242

86

Odkryłem {{ request.get_host }}metodę.


11
Zwróć uwagę, że ta odpowiedź ma te same problemy, co podejście Daniela Rosemana (można ją sfałszować), ale z pewnością jest bardziej kompletna, gdy host jest osiągany przez serwer proxy HTTP lub moduł równoważenia obciążenia, ponieważ uwzględnia HTTP_X_FORWARDED_HOSTnagłówek HTTP.
— furins

4
Użycie: „// {{request.get_host}} / cokolwiek / else / you / want” ... Pamiętaj, aby podać ustawienie ALLOWED_HOSTS (patrz docs.djangoproject.com/en/1.5/ref/settings/#allowed -hosts ).
— Seth

3
@Seth Better to use request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/ ... )
— MrKsn

60

Uzupełniając Carla Meyera, możesz zrobić taki procesor kontekstu:

module.context_processors.py

from django.conf import settings

def site(request):
    return {'SITE_URL': settings.SITE_URL}

local settings.py

SITE_URL = 'http://google.com' # this will reduce the Sites framework db call.

settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    "module.context_processors.site",
    ....
 )

szablony zwracające wystąpienie kontekstu witryna url to {{SITE_URL}}

możesz napisać własną rutynę, jeśli chcesz obsługiwać subdomeny lub SSL w procesorze kontekstu.


Wypróbowałem to rozwiązanie, ale jeśli masz kilka subdomen dla tej samej aplikacji, jest to niepraktyczne, bardzo przydatna okazała się odpowiedź danbruegge
— Jose Luis de la Rosa

w settings.py musisz wprowadzić swój procesor kontekstu w context_processors> OPTIONS> TEMPLATES
— yas17

24

Odmiana używanego przeze mnie procesora kontekstu to:

from django.contrib.sites.shortcuts import get_current_site
from django.utils.functional import SimpleLazyObject


def site(request):
    return {
        'site': SimpleLazyObject(lambda: get_current_site(request)),
    }

SimpleLazyObjectWrapper pilnuje wywołanie DB dzieje się tylko wtedy, gdy szablon faktycznie korzysta z siteobiektu. Spowoduje to usunięcie zapytania ze stron administratora. Zapisuje również wynik w pamięci podręcznej.

i uwzględnij to w ustawieniach:

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    "module.context_processors.site",
    ....
)

W szablonie możesz użyć, {{ site.domain }}aby uzyskać aktualną nazwę domeny.

edycja: aby obsługiwać również przełączanie protokołów, użyj:

def site(request):
    site = SimpleLazyObject(lambda: get_current_site(request))
    protocol = 'https' if request.is_secure() else 'http'

    return {
        'site': site,
        'site_root': SimpleLazyObject(lambda: "{0}://{1}".format(protocol, site.domain)),
    }

Nie musisz SimpleLazyObjecttutaj używać , ponieważ lambda nie zostanie wywołana, jeśli i tak nic nie uzyska dostępu do „witryny”.
— monokrom

Jeśli usuniesz SimpleLazyObject, każdy RequestContextzadzwoni get_current_site(), a tym samym wykona zapytanie SQL. Opakowanie zapewnia, że ​​zmienna jest oceniana tylko wtedy, gdy jest faktycznie używana w szablonie.
— vdboor

1
Ponieważ jest to funkcja, łańcuch hosta nie będzie przetwarzany, chyba że i tak zostanie użyty. Możesz więc po prostu przypisać funkcję do „site_root” i nie potrzebujesz SimpleLazyObject. Django wywoła funkcję, gdy jest używana. I tak utworzyłeś już niezbędną funkcję z lambdą tutaj.
— monokrom

Ach tak, działałaby tylko lambda. Ma SimpleLazyObjectto na celu uniknięcie ponownej oceny funkcji, która nie jest tak naprawdę potrzebna, ponieważ Siteobiekt jest buforowany.
— vdboor

Import jest terazfrom django.contrib.sites.shortcuts import get_current_site
— Hraban

22

Wiem, że to pytanie jest stare, ale natknąłem się na nie, szukając pythonowego sposobu na uzyskanie bieżącej domeny.

def myview(request):
    domain = request.build_absolute_uri('/')[:-1]
    # that will build the complete domain: http://foobar.com

4
build_absolute_urijest udokumentowane tutaj .
— Philipp Zedler,

19

Szybki i prosty, ale nie nadaje się do produkcji:

(w widoku)

    request.scheme               # http or https
    request.META['HTTP_HOST']    # example.com
    request.path                 # /some/content/1/

(w szablonie)

{{ request.scheme }} :// {{ request.META.HTTP_HOST }} {{ request.path }}

Pamiętaj, aby użyć RequestContext , co ma miejsce, jeśli używasz renderowania .

Nie ufaj request.META['HTTP_HOST']produkcji: te informacje pochodzą z przeglądarki. Zamiast tego użyj odpowiedzi @ CarlMeyer


Głosuję za tą odpowiedzią, ale podczas próby użycia pojawił się błąd request.scheme. Być może dostępne tylko w nowszych wersjach django.
— Matt Cremeens,

@MattCremeens request.schemezostał dodany w Django 1.7.
— S. Kirby,

16

{{ request.get_host }}powinien chronić przed atakami nagłówka HTTP Host, gdy jest używany razem z ALLOWED_HOSTSustawieniem (dodane w Django 1.4.4).

Pamiętaj, że {{ request.META.HTTP_HOST }}nie ma takiej samej ochrony. Zobacz dokumentację :

ALLOWED_HOSTS

Lista ciągów znaków reprezentujących nazwy hostów / domen, które może obsługiwać ta witryna Django. Jest to środek bezpieczeństwa zapobiegający atakom nagłówka HTTP Host , które są możliwe nawet w przypadku wielu pozornie bezpiecznych konfiguracji serwera WWW.

... Jeśli Hostnagłówek (lub X-Forwarded-Hostjeśli USE_X_FORWARDED_HOSTjest włączony) nie pasuje do żadnej wartości na tej liście, django.http.HttpRequest.get_host()metoda podniesie SuspiciousOperation.

... Ta walidacja ma zastosowanie tylko przez get_host(); jeśli Twój kod uzyskuje dostęp do nagłówka Hosta bezpośrednio od request.METACiebie, pomijasz to zabezpieczenie.


Jeśli chodzi o używanie requestw twoim szablonie, wywołania funkcji renderującej szablon zmieniły się w Django 1.8 , więc nie musisz już obsługiwać RequestContextbezpośrednio.

Oto jak wyrenderować szablon widoku za pomocą funkcji skrótu render():

from django.shortcuts import render

def my_view(request):
    ...
    return render(request, 'my_template.html', context)

Oto jak renderować szablon wiadomości e-mail, który IMO jest najczęstszym przypadkiem, w którym chcesz uzyskać wartość hosta:

from django.template.loader import render_to_string

def my_view(request):
    ...
    email_body = render_to_string(
        'my_template.txt', context, request=request)

Oto przykład dodawania pełnego adresu URL w szablonie wiadomości e-mail; request.scheme powinien otrzymać httplub w httpszależności od tego, czego używasz:

Thanks for registering! Here's your activation link:
{{ request.scheme }}://{{ request.get_host }}{% url 'registration_activate' activation_key %}

10

Używam niestandardowego tagu szablonu. Dodaj np . <your_app>/templatetags/site.py:

# -*- coding: utf-8 -*-
from django import template
from django.contrib.sites.models import Site

register = template.Library()

@register.simple_tag
def current_domain():
    return 'http://%s' % Site.objects.get_current().domain

Użyj go w szablonie takim jak ten:

{% load site %}
{% current_domain %}

Czy jest jakaś konkretna wada tego podejścia? Oprócz wywołania bazy danych Witryny na każde żądanie.
— kicker86

@ kicker86 Nie znam żadnego. get_currentjest udokumentowaną metodą: docs.djangoproject.com/en/dev/ref/contrib/sites/…
— Dennis Golomazov

3
'http://%s'może być problem w przypadku httpspołączenia; schemat nie jest dynamiczny w tym przypadku.
— Uszkodzony organiczny

4

Podobnie jak w przypadku odpowiedzi użytkownika panchicore, oto co zrobiłem na bardzo prostej stronie internetowej. Udostępnia kilka zmiennych i udostępnia je w szablonie.

SITE_URLtrzymałby wartość podobną do example.com
SITE_PROTOCOLtrzymałby wartość podobną http lub https
SITE_PROTOCOL_URLtrzymałby wartość podobną http://example.comlub https://example.com
SITE_PROTOCOL_RELATIVE_URLtrzymałby wartość podobną //example.com.

module / context_processors.py

from django.conf import settings

def site(request):

    SITE_PROTOCOL_RELATIVE_URL = '//' + settings.SITE_URL

    SITE_PROTOCOL = 'http'
    if request.is_secure():
        SITE_PROTOCOL = 'https'

    SITE_PROTOCOL_URL = SITE_PROTOCOL + '://' + settings.SITE_URL

    return {
        'SITE_URL': settings.SITE_URL,
        'SITE_PROTOCOL': SITE_PROTOCOL,
        'SITE_PROTOCOL_URL': SITE_PROTOCOL_URL,
        'SITE_PROTOCOL_RELATIVE_URL': SITE_PROTOCOL_RELATIVE_URL
    }

settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    "module.context_processors.site",
    ....
 )

SITE_URL = 'example.com'

Następnie na szablonach, należy je jak {{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}i{{ SITE_PROTOCOL_RELATIVE_URL }}


2

W szablonie Django możesz:

<a href="{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ request.path }}?{{ request.GET.urlencode }}" >link</a>

1
To zadziałało dla mnie, dziękuję. Musiałem włączyć żądanie w TEMPLATES, context_processors:, django.template.context_processors.requesttakże [ten poradnik pomógł] ( simpleisbetterthancomplex.com/tips/2016/07/20/… )
— ionescu77

Zgadzam się, blog Vitora Freitas jest świetnym źródłem dla programistów Django! :)
— Dos

2

Jeśli używasz procesora kontekstu „żądania” i korzystasz ze struktury witryn Django i masz zainstalowane oprogramowanie pośrednie witryny (tj. Twoje ustawienia obejmują te):

INSTALLED_APPS = [
    ...
    "django.contrib.sites",
    ...
]

MIDDLEWARE = [
    ...
     "django.contrib.sites.middleware.CurrentSiteMiddleware",
    ...
]

TEMPLATES = [
    {
        ...
        "OPTIONS": {
            "context_processors": [
                ...
                "django.template.context_processors.request",
                ...
            ]
        }
    }
]

... wtedy obiekt będzie requestdostępny w szablonach i będzie zawierał odniesienie do aktualnego Siteżądania jako request.site. Następnie możesz pobrać domenę w szablonie za pomocą:

    {{request.site.domain}}

1

A co z tym podejściem? Pracuje dla mnie. Jest również używany w rejestracji django .

def get_request_root_url(self):
    scheme = 'https' if self.request.is_secure() else 'http'
    site = get_current_site(self.request)
    return '%s://%s' % (scheme, site)

Ale wypróbowanie go localhostprzyniesie ci httpsschemat (jest uważany za bezpieczny), który nie zadziała, jeśli masz statyczny adres URL (tylko http://127.0.0.1jest prawidłowy, nie https://127.0.0.1). Więc nie jest idealny, gdy jest jeszcze w fazie rozwoju.
— ThePhi

0
from django.contrib.sites.models import Site
if Site._meta.installed:
    site = Site.objects.get_current()
else:
    site = RequestSite(request)

-5

Możesz użyć {{ protocol }}://{{ domain }}w swoich szablonach, aby uzyskać nazwę domeny.


Nie sądzę, żeby @Erwan zauważył, że zależy to od niestandardowego procesora kontekstu żądania.
— monokrom,

Nie mogłem tego zrobić, gdzie definiujesz protokół i domenę?
— Jose Luis de la Rosa
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.