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

156

Jak uzyskać nazwę domeny mojej bieżącej witryny z szablonu Django? Próbowałem poszukać w tagu i filtrach, ale nic tam nie ma.

Jean-François Fabre
źródło

Odpowiedzi:

67

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

phsiao
źródło
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.

Carl Meyer
źródło
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ę.

danbruegge
źródło
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.

panchicore
źródło
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)),
    }
vdboor
źródło
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
misterte
źródło
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

Edward Newell
źródło
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 %}
S. Kirby
źródło
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 %}
Dennis Golomazov
źródło
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 }}

Julián Landerreche
źródło
2

W szablonie Django możesz:

<a href="{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ request.path }}?{{ request.GET.urlencode }}" >link</a>
Dos
źródło
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}}
user85461
źródło
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)

źródło
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)
Muneeb Ahmad
źródło
-5

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

Erwan
źródło
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