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


Jawaban:


67

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


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.


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.


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.


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

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

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


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

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

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


2

Dalam template Django yang dapat Anda lakukan:

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

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

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)

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)

-5

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


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.