Bagaimana saya bisa mendapatkan nama domain situs saya dalam template Django?

156

Bagaimana cara mendapatkan nama domain dari situs saya saat ini dari dalam template Django? Saya sudah mencoba mencari di tag dan filter tetapi tidak ada.

Jean-François Fabre
sumber

Jawaban:

67

Saya pikir yang Anda inginkan adalah memiliki akses ke konteks permintaan, lihat RequestContext.

phsiao
sumber
140
request.META['HTTP_HOST']memberi Anda domain. Dalam sebuah templat akan seperti itu {{ request.META.HTTP_HOST }}.
Daniel Roseman
29
Berhati-hatilah dengan menggunakan metadata permintaan. Itu berasal dari browser dan dapat dipalsukan. Secara umum, Anda mungkin ingin mengikuti apa yang disarankan di bawah ini oleh @CarlMeyer.
Josh
2
Untuk tujuan saya, ini tidak memiliki lubang keamanan.
Paul Draper
7
Saya kira, karena Django 1.5 dengan pengaturan host yang diizinkan aman untuk digunakan. docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
Daniel Backman
8
Bisakah seseorang menguraikan apa "lubang keamanan" itu? Jika pengguna menipu Host:header dan mendapat respons kembali dengan domain palsu di suatu tempat di halaman, bagaimana cara membuat lubang keamanan? Saya tidak melihat bagaimana itu berbeda dari pengguna yang mengambil HTML yang dihasilkan dan memodifikasi dirinya sendiri sebelum mengumpankannya ke browser miliknya.
user193130
105

Jika Anda ingin tajuk Host HTTP asli, lihat komentar Daniel Roseman pada jawaban @ Phsiao. Alternatif lain adalah jika Anda menggunakan kerangka contrib.sites , Anda dapat menetapkan nama domain kanonik untuk suatu Situs dalam basis data (memetakan domain permintaan ke file pengaturan dengan SITE_ID yang tepat adalah sesuatu yang harus Anda lakukan sendiri melalui Anda pengaturan server web). Dalam hal ini yang Anda cari:

from django.contrib.sites.models import Site

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

Anda harus menempatkan sendiri objek current_site ke dalam konteks template jika Anda ingin menggunakannya. Jika Anda menggunakannya di mana-mana, Anda bisa mengemasnya dalam prosesor konteks templat.

Carl Meyer
sumber
3
Untuk mengklarifikasi bagi seseorang yang memiliki masalah yang sama dengan yang saya miliki: periksa apakah SITE_IDpengaturan Anda sama dengan idatribut situs saat ini di aplikasi Situs (Anda dapat menemukan iddi panel admin Situs). Ketika Anda menelepon get_current, Django mengambil SITE_IDdan mengembalikan Siteobjek dengan id itu dari database.
Dennis Golomazov
Tak satu pun dari ini bekerja untuk saya. 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

Saya telah menemukan {{ request.get_host }}metodenya.

danbruegge
sumber
11
Harap dicatat bahwa jawaban ini memiliki masalah yang sama dengan pendekatan Daniel Roseman (dapat dipalsukan) tetapi pasti lebih lengkap ketika tuan rumah dicapai melalui proxy HTTP atau load balancer karena memperhitungkan HTTP_X_FORWARDED_HOSTheader HTTP.
Furins
4
Penggunaan: "// {{request.get_host}} / anything / else / you / want" ... Pastikan untuk mengisi pengaturan ALLOWED_HOSTS Anda (lihat docs.djangoproject.com/en/1.5/ref/settings/#allowed -hosting ).
Seth
3
@Seth lebih baik menggunakan request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/… )
MrKsn
60

Melengkapi Carl Meyer, Anda dapat membuat pemroses konteks seperti ini:

module.context_processors.py

from django.conf import settings

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

pengaturan lokal .py

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

settings.py

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

templat yang mengembalikan konteks misalnya situs url adalah {{SITE_URL}}

Anda dapat menulis rutine Anda sendiri jika ingin menangani subdomain atau SSL di pemroses konteks.

panchicore
sumber
Saya mencoba solusi ini tetapi jika Anda memiliki beberapa subdomain untuk aplikasi yang sama itu tidak praktis, saya menemukan jawaban yang sangat berguna oleh danbruegge
Jose Luis de la Rosa
di settings.py Anda harus memperkenalkan pemroses konteks Anda di context_processors> OPTIONS> TEMPLATES
yas17
24

Variasi dari pengolah konteks yang saya gunakan adalah:

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)),
    }

The SimpleLazyObjectwrapper memastikan panggilan DB hanya terjadi ketika template benar-benar menggunakansite objek. Ini menghapus kueri dari halaman admin. Itu juga cache hasilnya.

dan sertakan dalam pengaturan:

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

Di dalam templat, Anda bisa menggunakan {{ site.domain }} untuk mendapatkan nama domain saat ini.

sunting: untuk mendukung pengalihan protokol juga, gunakan:

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
sumber
Anda tidak perlu menggunakan di SimpleLazyObjectsini, karena lambda tidak akan dipanggil jika tidak ada yang mengakses 'situs'.
monokrom
Jika Anda menghapus SimpleLazyObject, masing RequestContext- masing akan memanggil get_current_site(), dan karenanya menjalankan query SQL. Wrapper memastikan variabel hanya dievaluasi ketika sebenarnya digunakan dalam template.
vdboor
1
Karena fungsi, string host tidak akan diproses kecuali jika digunakan pula. Jadi, Anda bisa menetapkan fungsi ke 'site_root' dan Anda tidak perlu SimpleLazyObject. Django akan memanggil fungsi saat digunakan. Anda sudah membuat fungsi yang diperlukan dengan lambda di sini.
monokrom
Ah ya, hanya lambda yang akan bekerja. The SimpleLazyObjectada untuk menghindari reevaluasi fungsi, yang tidak benar-benar diperlukan karena Siteobjek cache.
vdboor
Impor sekarangfrom django.contrib.sites.shortcuts import get_current_site
Hraban
22

Saya tahu pertanyaan ini sudah lama, tetapi saya menemukan itu mencari cara pythonic untuk mendapatkan domain saat ini.

def myview(request):
    domain = request.build_absolute_uri('/')[:-1]
    # that will build the complete domain: http://foobar.com
salah bicara
sumber
4
build_absolute_urididokumentasikan di sini .
Philipp Zedler
19

Cepat dan sederhana, tetapi tidak baik untuk produksi:

(dalam tampilan)

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

(dalam templat)

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

Pastikan untuk menggunakan RequestContext , yang merupakan kasus jika Anda menggunakan render .

Jangan percaya request.META['HTTP_HOST']pada produksi: info itu berasal dari browser. Sebagai gantinya, gunakan jawaban @ CarlMeyer

Edward Newell
sumber
Saya membatalkan jawaban ini tetapi saya menerima kesalahan saat mencoba menggunakan request.scheme. Mungkin hanya tersedia di Django versi terbaru.
Matt Cremeens
@MattCremeens request.schemeditambahkan dalam Django 1.7.
S. Kirby
16

{{ request.get_host }}harus melindungi terhadap serangan header HTTP Host ketika digunakan bersama-sama dengan ALLOWED_HOSTSpengaturan (ditambahkan dalam Django 1.4.4).

Perhatikan bahwa {{ request.META.HTTP_HOST }}tidak memiliki perlindungan yang sama. Lihat dokumen :

ALLOWED_HOSTS

Daftar string yang mewakili nama host / domain yang dapat dilayani oleh situs Django ini. Ini adalah langkah keamanan untuk mencegah serangan header HTTP Host , yang dimungkinkan bahkan di bawah banyak konfigurasi server web yang tampaknya aman.

... Jika Hosttajuk (atau X-Forwarded-Hostjika USE_X_FORWARDED_HOSTdiaktifkan) tidak cocok dengan nilai apa pun dalam daftar ini, django.http.HttpRequest.get_host()metode akan naik SuspiciousOperation.

... Validasi ini hanya berlaku melalui get_host(); jika kode Anda mengakses header Host langsung dari request.METAAnda melewati perlindungan keamanan ini.


Adapun untuk menggunakan requestdalam template Anda, panggilan fungsi rendering template telah berubah di Django 1.8 , sehingga Anda tidak perlu lagi menangani RequestContextsecara langsung.

Berikut cara merender templat untuk tampilan, menggunakan fungsi pintasan render():

from django.shortcuts import render

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

Berikut cara merender templat untuk email, IMO mana yang merupakan kasus paling umum di mana Anda menginginkan nilai host:

from django.template.loader import render_to_string

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

Berikut ini contoh menambahkan URL lengkap dalam templat email; request.scheme harus mendapatkan httpatau httpsbergantung pada apa yang Anda gunakan:

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

Saya menggunakan tag templat khusus. Tambahkan ke misalnya <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

Gunakan dalam template seperti ini:

{% load site %}
{% current_domain %}
Dennis Golomazov
sumber
Apakah ada kelemahan khusus dari pendekatan ini? Terlepas dari panggilan ke Situs db pada setiap permintaan.
kicker86
@ kicker86 Saya tidak tahu. get_currentadalah metode yang terdokumentasi: docs.djangoproject.com/en/dev/ref/contrib/sites/…
Dennis Golomazov
3
'http://%s'bisa menjadi masalah dalam hal httpskoneksi; Skema tidak dinamis dalam hal ini.
Rusak Organik
4

Mirip dengan balasan pengguna panchicore, ini adalah apa yang saya lakukan di situs web yang sangat sederhana. Ini memberikan beberapa variabel dan membuatnya tersedia di templat.

SITE_URLakan memegang nilai suka example.com
SITE_PROTOCOLakan memegang nilai suka http atau https
SITE_PROTOCOL_URLakan memegang nilai suka http://example.comatau https://example.com
SITE_PROTOCOL_RELATIVE_URLakan memegang nilai suka//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'

Kemudian, pada template Anda, menggunakannya sebagai {{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}dan{{ SITE_PROTOCOL_RELATIVE_URL }}

Julián Landerreche
sumber
2

Dalam template Django yang dapat Anda lakukan:

<a href="{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ request.path }}?{{ request.GET.urlencode }}" >link</a>
Dos
sumber
1
Ini bekerja untuk saya, terima kasih. Saya harus mengaktifkan permintaan di TEMPLATES, context_processors:, django.template.context_processors.requestjuga [ini bagaimana-untuk membantu] ( simpleisbetterthancomplex.com/tips/2016/07/20/ ... )
ionescu77
Setuju, blog Vitor Freitas adalah sumber yang bagus untuk pengembang Django! :)
Dos
2

Jika Anda menggunakan pemroses konteks "permintaan" , dan sedang menggunakan kerangka kerja situs Django , dan instal middleware Situs (mis. Pengaturan Anda mencakup ini):

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

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

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

... maka Anda akan memiliki requestobjek yang tersedia di template, dan itu akan berisi referensi ke Sitepermintaan saat ini sebagai request.site. Anda kemudian dapat mengambil domain dalam templat dengan:

    {{request.site.domain}}
pengguna85461
sumber
1

Bagaimana dengan pendekatan ini? Bekerja untukku. Ini juga digunakan dalam pendaftaran 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)

sumber
Tetapi mencobanya dengan localhostakan membuat Anda httpsskema (Itu dianggap aman) yang tidak akan berfungsi jika Anda punya url statis (hanya http://127.0.0.1valid, tidak https://127.0.0.1). Jadi tidak ideal saat masih dalam pengembangan.
ThePhi
0
from django.contrib.sites.models import Site
if Site._meta.installed:
    site = Site.objects.get_current()
else:
    site = RequestSite(request)
Muneeb Ahmad
sumber
-5

Anda dapat menggunakan {{ protocol }}://{{ domain }}template Anda untuk mendapatkan nama domain Anda.

Erwan
sumber
Saya tidak berpikir bahwa @Erwan memperhatikan bahwa ini tergantung pada prosesor konteks permintaan non-standar.
monokrom
Saya tidak bisa membuat ini berfungsi, di mana Anda mendefinisikan protokol dan domain?
Jose Luis de la Rosa