ฉันจะรับชื่อโดเมนของเว็บไซต์ของฉันภายในเทมเพลต Django ได้อย่างไร


156

ฉันจะรับชื่อโดเมนของไซต์ปัจจุบันของฉันจากภายในแม่แบบ Django ได้อย่างไร ฉันลองค้นหาในแท็กและตัวกรองแล้ว แต่ไม่มีอะไรอยู่

คำตอบ:


67

ฉันคิดว่าสิ่งที่คุณต้องการคือการเข้าถึงบริบทคำขอดูที่ RequestContext


140
request.META['HTTP_HOST']ช่วยให้คุณโดเมน {{ request.META.HTTP_HOST }}ในแม่มันจะ
Daniel Roseman

29
ระมัดระวังการใช้เมตาดาต้าของคำขอ มันมาจากเบราว์เซอร์และสามารถปลอมแปลงได้ โดยทั่วไปคุณอาจต้องการไปกับสิ่งที่แนะนำโดย @CarlMeyer
Josh

2
สำหรับวัตถุประสงค์ของฉันนี้ไม่มีช่องโหว่ด้านความปลอดภัย
พอลเดรเปอร์

7
ฉันเดาตั้งแต่ Django 1.5 ด้วยการตั้งค่าโฮสต์ที่อนุญาตมันปลอดภัยที่จะใช้ docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
Daniel Backman

8
ใครบางคนสามารถอธิบายรายละเอียดเกี่ยวกับสิ่งที่ "ความปลอดภัย" คืออะไร? หากผู้ใช้หลอกHost:ส่วนหัวและได้รับการตอบกลับด้วยโดเมนปลอมบางแห่งในหน้านั้นจะสร้างช่องโหว่ความปลอดภัยได้อย่างไร ฉันไม่เห็นความแตกต่างจากผู้ใช้ที่ใช้ HTML ที่สร้างขึ้นและแก้ไขตัวเองก่อนส่งไปยังเบราว์เซอร์ของเขาเอง
user193130

105

หากคุณต้องการส่วนหัวโฮสต์ HTTP จริงโปรดดูความคิดเห็นของ Daniel Roseman บนคำตอบของ @ Phsiao อีกทางเลือกหนึ่งคือถ้าคุณใช้เฟรมเวิร์ก contrib.sitesคุณสามารถตั้งชื่อโดเมนแบบบัญญัติสำหรับเว็บไซต์ในฐานข้อมูล (จับคู่โดเมนคำขอกับไฟล์การตั้งค่าด้วย SITE_ID ที่เหมาะสมเป็นสิ่งที่คุณต้องทำด้วยตัวเองผ่านทาง การตั้งค่าเว็บเซิร์ฟเวอร์) ในกรณีที่คุณกำลังมองหา:

from django.contrib.sites.models import Site

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

คุณต้องใส่วัตถุ current_site ลงในบริบทเทมเพลตด้วยตนเองหากคุณต้องการใช้ หากคุณกำลังใช้งานอยู่ทุกที่คุณสามารถจัดทำแพ็กเกจนั้นในตัวประมวลผลบริบทเทมเพลต


3
หากต้องการชี้แจงให้คนที่มีปัญหาแบบเดียวกับที่ฉันมี: ตรวจสอบว่าSITE_IDการตั้งค่าของคุณเท่ากับidคุณลักษณะของไซต์ปัจจุบันในแอป Sites (คุณสามารถค้นหาได้idในแผงผู้ดูแลระบบของไซต์) เมื่อคุณเรียกget_currentใช้ Django จะนำของคุณSITE_IDและส่งคืนSiteวัตถุที่มี id นั้นจากฐานข้อมูล
Dennis Golomazov

ไม่มีงานเหล่านี้สำหรับฉัน 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

ฉันได้ค้นพบ{{ request.get_host }}วิธีการ


11
โปรดทราบว่าคำตอบนี้มีปัญหาแบบเดียวกันกับวิธีของ Daniel Roseman (สามารถปลอมแปลงได้) แต่จะเสร็จสมบูรณ์มากขึ้นเมื่อโฮสต์นั้นเข้าถึงผ่านพร็อกซี HTTP หรือโหลดบาลานเซอร์เนื่องจากคำนึงถึงHTTP_X_FORWARDED_HOSTส่วนหัว HTTP
furins

4
การใช้งาน: "// {{request.get_host}} / อะไรก็ได้ / อื่น / คุณ / ต้องการ" ... อย่าลืมกรอกการตั้งค่า ALLOWED_HOSTS ของคุณ (ดูdocs.djangoproject.com/en/1.5/ref/settings/#allowed - โฮสต์ )
เซท

3
@Seth ดีกว่าที่จะใช้request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/ ...... )
MrKsn

60

การเสริม Carl Meyer คุณสามารถสร้างตัวประมวลผลบริบทดังนี้:

module.context_processors.py

from django.conf import settings

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

settings.py ท้องถิ่น

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

settings.py

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

เทมเพลตที่ส่งคืนอินสแตนซ์บริบทไซต์ url คือ {{SITE_URL}}

คุณสามารถเขียนรูตินของคุณเองหากต้องการจัดการโดเมนย่อยหรือ SSL ในตัวประมวลผลบริบท


ฉันลองวิธีนี้ แต่ถ้าคุณมีหลายโดเมนย่อยสำหรับแอปพลิเคชันเดียวกันมันไม่สามารถใช้งานได้จริงฉันพบว่าคำตอบที่มีประโยชน์มากโดย danbruegge
Jose Luis de la Rosa

ใน settings.py คุณต้องแนะนำตัวประมวลผลบริบทของคุณใน context_processors> OPTIONS> TEMPLATES
yas17

24

รูปแบบของตัวประมวลผลบริบทที่ฉันใช้คือ:

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

SimpleLazyObjectเสื้อคลุมทำให้แน่ใจว่าสาย DB จะเกิดขึ้นเมื่อแม่แบบจริงใช้siteวัตถุ สิ่งนี้จะลบแบบสอบถามออกจากหน้าผู้ดูแลระบบ นอกจากนี้ยังแคชผลลัพธ์

และรวมไว้ในการตั้งค่า:

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

ในเทมเพลตคุณสามารถใช้{{ site.domain }}เพื่อรับชื่อโดเมนปัจจุบัน

แก้ไข:เพื่อรองรับการสลับโปรโตคอลด้วยให้ใช้:

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

คุณไม่จำเป็นต้องใช้SimpleLazyObjectที่นี่เพราะแลมบ์ดาจะไม่ถูกเรียกถ้าไม่มีการเข้าถึง 'ไซต์' อยู่ดี
ขาวดำ

หากคุณลบเครื่องหมายSimpleLazyObjectแต่ละอันRequestContextจะเรียกใช้get_current_site()และดำเนินการแบบสอบถาม SQL wrapper ทำให้แน่ใจว่าตัวแปรจะถูกประเมินเฉพาะเมื่อมันถูกใช้จริงในเทมเพลต
vdboor

1
เนื่องจากมันเป็นฟังก์ชั่นสตริงโฮสต์จะไม่ถูกประมวลผลจนกว่าจะมีการใช้งาน ดังนั้นคุณสามารถกำหนดฟังก์ชันให้กับ 'site_root' และคุณไม่จำเป็นต้องใช้ SimpleLazyObject Django จะเรียกใช้ฟังก์ชันเมื่อใช้งาน คุณได้สร้างฟังก์ชันที่จำเป็นด้วยแลมบ์ดาแล้วที่นี่
ขาวดำ

อ่าใช่แลมบ์ดาจะใช้ได้ SimpleLazyObjectจะมีการประเมินหลีกเลี่ยงการทำงานที่ไม่จำเป็นจริงๆตั้งแต่Siteวัตถุแคช
vdboor

การนำเข้าอยู่ในขณะนี้from django.contrib.sites.shortcuts import get_current_site
Hraban

22

ฉันรู้ว่าคำถามนี้เก่า แต่ฉันสะดุดเมื่อมองหาวิธี pythonic เพื่อรับโดเมนปัจจุบัน

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

4
build_absolute_uriเป็นเอกสารที่นี่
Philipp Zedler

19

รวดเร็วและเรียบง่าย แต่ไม่ดีสำหรับการผลิต:

(ในมุมมอง)

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

(ในเทมเพลต)

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

ตรวจสอบให้แน่ใจว่าใช้RequestContextซึ่งเป็นกรณีนี้หากคุณใช้การแสดงผลการแสดงผล

อย่าเชื่อถือrequest.META['HTTP_HOST']ในการผลิต: ข้อมูลนั้นมาจากเบราว์เซอร์ ใช้คำตอบของ @ CarlMeyer แทน


ฉันกำลัง upvoting คำตอบนี้ request.schemeแต่ผมได้รับข้อผิดพลาดเมื่อพยายามที่จะใช้ อาจมีเฉพาะใน django เวอร์ชันใหม่กว่าเท่านั้น
Matt Cremeens

@MattCremeens request.schemeถูกเพิ่มใน Django 1.7
S. Kirby

16

{{ request.get_host }}ควรป้องกันการโจมตีส่วนหัวของโฮสต์ HTTP เมื่อใช้ร่วมกับการALLOWED_HOSTSตั้งค่า (เพิ่มใน Django 1.4.4)

โปรดทราบว่า{{ request.META.HTTP_HOST }}ไม่มีการป้องกันเดียวกัน ดูเอกสาร :

ALLOWED_HOSTS

รายการสตริงที่แสดงถึงชื่อโฮสต์ / โดเมนที่ไซต์ Django นี้สามารถให้บริการได้ นี่เป็นมาตรการรักษาความปลอดภัยเพื่อป้องกันการโจมตีส่วนหัวของโฮสต์ HTTPซึ่งเป็นไปได้แม้จะอยู่ภายใต้การกำหนดค่าเว็บเซิร์ฟเวอร์ที่ปลอดภัย

... ถ้าHostส่วนหัว (หรือX-Forwarded-Hostถ้าUSE_X_FORWARDED_HOSTเปิดใช้งาน) ไม่ตรงกับค่าใด ๆ ในรายการนี้วิธีการจะเพิ่มdjango.http.HttpRequest.get_host()SuspiciousOperation

... การตรวจสอบนี้ใช้ผ่านทางget_host(); หากรหัสของคุณเข้าถึงส่วนหัวของโฮสต์โดยตรงจากrequest.METAคุณกำลังข้ามการป้องกันความปลอดภัยนี้


สำหรับการใช้requestในเทมเพลตของคุณการเรียกฟังก์ชั่นการแสดงผลเทมเพลตนั้นมีการเปลี่ยนแปลงใน Django 1.8ดังนั้นคุณจึงไม่ต้องจัดการอีกต่อไปRequestContextโดยตรง

นี่คือวิธีการแสดงเทมเพลตสำหรับมุมมองโดยใช้ฟังก์ชันทางลัดrender():

from django.shortcuts import render

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

ต่อไปนี้เป็นวิธีสร้างเทมเพลตสำหรับอีเมลซึ่ง IMO เป็นกรณีทั่วไปที่คุณต้องการมูลค่าโฮสต์:

from django.template.loader import render_to_string

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

นี่คือตัวอย่างของการเพิ่ม URL แบบเต็มในเทมเพลตอีเมล request.schemeควรได้รับhttpหรือhttpsขึ้นอยู่กับสิ่งที่คุณใช้:

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

10

ฉันใช้เทมเพลตแท็กที่กำหนดเอง เพิ่มไปยังเช่น<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

ใช้ในเทมเพลตเช่นนี้:

{% load site %}
{% current_domain %}

มีข้อเสียเป็นพิเศษสำหรับวิธีนี้หรือไม่? นอกเหนือจากการเรียกไปยัง Site db ในทุกคำขอ
kicker86

@ kicker86 ฉันไม่รู้อะไรเลย get_currentเป็นวิธีการบันทึกไว้: docs.djangoproject.com/en/dev/ref/contrib/sites/…
Dennis Golomazov

3
'http://%s'อาจเป็นปัญหาในกรณีที่มีการhttpsเชื่อมต่อ; แบบแผนไม่ได้เป็นแบบไดนามิกในกรณีนี้
ออร์แกนิกได้รับความเสียหาย

4

คล้ายกับคำตอบของผู้ใช้ panchicore นี่คือสิ่งที่ฉันทำในเว็บไซต์ที่ง่ายมาก มันมีตัวแปรน้อยและทำให้พวกเขามีอยู่บนแม่แบบ

SITE_URLจะถือเป็นค่าที่เหมือนexample.com
SITE_PROTOCOLจะถือเป็นค่าที่เหมือนhttp หรือhttps
SITE_PROTOCOL_URLจะถือเป็นค่าที่เหมือนhttp://example.comหรือจะถือเป็นค่าเช่นhttps://example.com
SITE_PROTOCOL_RELATIVE_URL//example.com

โมดูล / 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'

จากนั้นในแม่แบบของคุณใช้พวกเขาเป็น{{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}และ{{ SITE_PROTOCOL_RELATIVE_URL }}


2

ในแม่แบบ Django ที่คุณสามารถทำได้:

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

1
สิ่งนี้ได้ผลสำหรับฉันขอบคุณ ผมได้มีการเปิดใช้งานการร้องขอในแม่แบบ context_processors: django.template.context_processors.requestยัง [นี้วิธีการช่วย] ( simpleisbetterthancomplex.com/tips/2016/07/20/... )
ionescu77

เห็นด้วยบล็อก Vitor Freitas เป็นแหล่งที่ดีสำหรับนักพัฒนา Django! :)
Dos

2

ถ้าคุณใช้"คำขอ" โปรเซสเซอร์บริบทและใช้กรอบเว็บไซต์ Djangoและมีตัวกลางเว็บไซต์ที่ติดตั้ง (เช่นการตั้งค่าของคุณรวมถึงเหล่านี้):

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

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

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

... แล้วคุณจะมีrequestวัตถุที่มีอยู่ในแม่แบบและก็จะมีการอ้างอิงถึงในปัจจุบันสำหรับการร้องขอเป็นSite request.siteจากนั้นคุณสามารถดึงข้อมูลโดเมนในแม่แบบด้วย:

    {{request.site.domain}}

1

แล้ววิธีนี้ล่ะ ได้ผลสำหรับฉัน นอกจากนี้ยังใช้ใน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)

แต่การลองด้วยlocalhostจะทำให้คุณมีhttpsรูปแบบ (ถือว่าปลอดภัย) ซึ่งจะไม่ทำงานหากคุณมี URL คงที่ (ใช้ได้เฉพาะhttp://127.0.0.1ไม่ใช่https://127.0.0.1) ดังนั้นจึงไม่เหมาะเมื่อยังอยู่ในการพัฒนา
ThePhi


-5

คุณสามารถใช้{{ protocol }}://{{ domain }}ในแม่แบบของคุณเพื่อรับชื่อโดเมนของคุณ


ฉันไม่คิดว่า @Erwan สังเกตว่านี้ขึ้นอยู่กับหน่วยประมวลผลคำขอบริบทที่ไม่ได้มาตรฐาน
monokrome

ฉันไม่สามารถใช้งานได้คุณกำหนดโปรโตคอลและโดเมนที่ไหน
Jose Luis de la Rosa
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.