Làm cách nào tôi có thể nhận được tên miền của trang web của mình trong mẫu Django?


156

Làm cách nào để có được tên miền của trang web hiện tại của tôi từ trong mẫu Django? Tôi đã thử tìm kiếm trong thẻ và bộ lọc nhưng không có gì ở đó.

Câu trả lời:


67

Tôi nghĩ những gì bạn muốn là có quyền truy cập vào bối cảnh yêu cầu, xem RequestContext.


140
request.META['HTTP_HOST']cung cấp cho bạn tên miền. Trong một mẫu nó sẽ là {{ request.META.HTTP_HOST }}.
Daniel Roseman

29
Hãy cẩn thận với việc sử dụng siêu dữ liệu yêu cầu. Nó đến từ một trình duyệt và có thể bị giả mạo. Nói chung, có lẽ bạn sẽ muốn đi với những gì được đề xuất dưới đây bởi @CarlMeyer.
Josh

2
Đối với mục đích của tôi, điều này không có lỗ hổng bảo mật.
Paul Draper

7
Tôi đoán, vì Django 1.5 với các máy chủ được phép cài đặt nên nó an toàn để sử dụng. docs.djangoproject.com/en/1.5/ref/sinstall/#allowed-hosts
Daniel Backman

8
Ai đó có thể giải thích "lỗ hổng bảo mật" là gì không? Nếu người dùng giả mạo Host:tiêu đề và nhận được phản hồi với tên miền giả mạo ở đâu đó trên một trang, làm thế nào điều đó tạo ra một lỗ hổng bảo mật? Tôi không thấy điều đó khác với người dùng lấy HTML được tạo và tự sửa đổi trước khi đưa nó vào trình duyệt của chính mình.
dùng193130

104

Nếu bạn muốn tiêu đề Máy chủ HTTP thực tế, hãy xem nhận xét của Daniel Roseman về câu trả lời của @ Phsiao. Cách khác là nếu bạn đang sử dụng khung contrib.sites , bạn có thể đặt tên miền chính cho Trang web trong cơ sở dữ liệu (ánh xạ miền yêu cầu thành tệp cài đặt với SITE_ID phù hợp là điều bạn phải tự thực hiện thông qua thiết lập máy chủ web). Trong trường hợp đó, bạn đang tìm kiếm:

from django.contrib.sites.models import Site

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

bạn phải tự đặt đối tượng current_site vào ngữ cảnh mẫu nếu bạn muốn sử dụng nó. Nếu bạn đang sử dụng nó ở mọi nơi, bạn có thể gói nó trong bộ xử lý bối cảnh mẫu.


3
Để làm rõ cho ai đó có cùng vấn đề như tôi đã gặp: hãy kiểm tra xem SITE_IDcài đặt của bạn có bằng idthuộc tính của trang hiện tại trong ứng dụng Trang web không (bạn có thể tìm thấy idtrong bảng quản trị Trang web). Khi bạn gọi get_current, Django lấy của bạn SITE_IDvà trả về Siteđối tượng có id đó từ cơ sở dữ liệu.
Dennis Golomazov

Không ai trong số này làm việc cho tôi. 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
dùng251242

86

Tôi đã khám phá ra {{ request.get_host }}phương pháp.


11
Xin lưu ý rằng câu trả lời này có cùng các vấn đề của cách tiếp cận Daniel Roseman (có thể bị giả mạo) nhưng chắc chắn sẽ hoàn thiện hơn khi máy chủ được truy cập thông qua proxy HTTP hoặc bộ cân bằng tải vì nó có tính đến HTTP_X_FORWARDED_HOSTtiêu đề HTTP.
furins

4
Cách sử dụng: "// {{request.get_host}} / anything / other / you / muốn" ... Hãy chắc chắn điền vào cài đặt ALLOWED_HOSTS của bạn (xem docs.djangoproject.com/en/1.5/ref/sinstall/#allowed -hosts ).
Seth

3
@Seth tốt hơn để sử dụng request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/ gợi ý )
MrKsn

60

Bổ sung cho Carl Meyer, bạn có thể tạo một bộ xử lý bối cảnh như thế này:

module.context_ Processors.py

from django.conf import settings

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

cài đặt cục bộ

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

cài đặt

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

mẫu trả về thể hiện ngữ cảnh trang web url là {{SITE_URL}}

bạn có thể viết rutine của riêng bạn nếu muốn xử lý tên miền phụ hoặc SSL trong bộ xử lý ngữ cảnh.


Tôi đã thử giải pháp này nhưng nếu bạn có một số tên miền phụ cho cùng một ứng dụng thì không thực tế, tôi thấy câu trả lời rất hữu ích của danbruegge
Jose Luis de la Rosa

trong settings.txt, bạn phải giới thiệu bộ xử lý ngữ cảnh của mình trong bối
cảnh_Quản

24

Biến thể của bộ xử lý bối cảnh tôi sử dụng là:

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

Trình SimpleLazyObjectbao bọc đảm bảo cuộc gọi DB chỉ xảy ra khi mẫu thực sự sử dụng siteđối tượng. Điều này loại bỏ truy vấn từ các trang quản trị. Nó cũng lưu trữ kết quả.

và bao gồm nó trong cài đặt:

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

Trong mẫu, bạn có thể sử dụng {{ site.domain }}để lấy tên miền hiện tại.

chỉnh sửa: để hỗ trợ chuyển đổi giao thức quá, sử dụng:

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

Bạn không cần phải sử dụng SimpleLazyObjectở đây, vì lambda sẽ không được gọi nếu không có gì truy cập 'trang web'.
đơn sắc

Nếu bạn loại bỏ SimpleLazyObject, mỗi cái RequestContextsẽ gọi get_current_site()và do đó thực hiện một truy vấn SQL. Trình bao bọc đảm bảo biến chỉ được đánh giá khi nó thực sự được sử dụng trong mẫu.
vdboor

1
Vì nó là một hàm, chuỗi máy chủ sẽ không được xử lý trừ khi nó được sử dụng. Vì vậy, bạn chỉ có thể gán một hàm cho 'site_root' và bạn không cần SimpleLazyObject. Django sẽ gọi hàm khi nó được sử dụng. Bạn đã tạo chức năng cần thiết với lambda ở đây.
đơn sắc

À vâng, chỉ có lambda sẽ hoạt động. Có SimpleLazyObjectđể tránh đánh giá lại chức năng, điều này không thực sự cần thiết vì Siteđối tượng được lưu trữ.
vdboor

Nhập khẩu bây giờfrom django.contrib.sites.shortcuts import get_current_site
Hraban

22

Tôi biết câu hỏi này đã cũ, nhưng tôi tình cờ tìm thấy một cách dễ hiểu để có được tên miền hiện tại.

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

4
build_absolute_uriđược ghi lại ở đây .
Philipp Zedler

19

Nhanh chóng và đơn giản, nhưng không tốt cho sản xuất:

(trong một cái nhìn)

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

(trong một mẫu)

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

Hãy chắc chắn sử dụng RequestContext , đây là trường hợp nếu bạn đang sử dụng kết xuất .

Không tin tưởng request.META['HTTP_HOST']vào sản xuất: thông tin đó đến từ trình duyệt. Thay vào đó, hãy sử dụng câu trả lời của @ CarlMeyer


Tôi đang nâng cao câu trả lời này nhưng tôi đã gặp lỗi khi thử sử dụng request.scheme. Có lẽ chỉ có sẵn trong các phiên bản mới hơn của django.
Matt Cremeens

@MattCremeens request.schemeđã được thêm vào Django 1.7.
S. Kirby

16

{{ request.get_host }}phải bảo vệ chống lại các cuộc tấn công tiêu đề Máy chủ HTTP khi được sử dụng cùng với ALLOWED_HOSTScài đặt (được thêm vào trong Django 1.4.4).

Lưu ý rằng {{ request.META.HTTP_HOST }}không có sự bảo vệ tương tự. Xem tài liệu :

ALLOWED_HOSTS

Danh sách các chuỗi đại diện cho tên máy chủ / tên miền mà trang Django này có thể phục vụ. Đây là một biện pháp bảo mật để ngăn chặn các cuộc tấn công tiêu đề Máy chủ HTTP , có thể xảy ra ngay cả dưới nhiều cấu hình máy chủ web có vẻ an toàn.

... Nếu Hosttiêu đề (hoặc X-Forwarded-Hostnếu USE_X_FORWARDED_HOSTđược bật) không khớp với bất kỳ giá trị nào trong danh sách này, django.http.HttpRequest.get_host()phương thức sẽ tăng SuspiciousOperation.

... Xác nhận này chỉ áp dụng thông qua get_host(); nếu mã của bạn truy cập vào tiêu đề Máy chủ trực tiếp từ request.METAbạn thì bỏ qua bảo vệ bảo mật này.


Đối với việc sử dụng requesttrong mẫu của bạn, các lệnh gọi chức năng kết xuất mẫu đã thay đổi trong Django 1.8 , do đó bạn không còn phải xử lý RequestContexttrực tiếp.

Dưới đây là cách hiển thị mẫu cho chế độ xem, sử dụng chức năng phím tắt render():

from django.shortcuts import render

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

Đây là cách kết xuất mẫu cho email, IMO là trường hợp phổ biến hơn mà bạn muốn giá trị máy chủ:

from django.template.loader import render_to_string

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

Đây là một ví dụ về việc thêm một URL đầy đủ trong một mẫu email; request.scheme sẽ nhận được httphoặc httpstùy thuộc vào những gì bạn đang sử dụng:

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

10

Tôi sử dụng một thẻ mẫu tùy chỉnh. Thêm vào ví dụ <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

Sử dụng nó trong một mẫu như thế này:

{% load site %}
{% current_domain %}

Có bất kỳ nhược điểm cụ thể của phương pháp này? Ngoài cuộc gọi đến db trang web trên mỗi yêu cầu.
kicker86

@ kicker86 Tôi không biết gì cả. get_currentlà một phương pháp được ghi lại: docs.djangoproject.com/en/dev/ref/contrib/sites/ mẹo
Dennis Golomazov

3
'http://%s'có thể là một vấn đề trong trường hợp httpskết nối; Đề án không năng động trong trường hợp này.
Hư hỏng hữu cơ

4

Tương tự như câu trả lời của người dùng panchicore, đây là những gì tôi đã làm trên một trang web rất đơn giản. Nó cung cấp một vài biến và làm cho chúng có sẵn trên mẫu.

SITE_URLsẽ giữ một giá trị như example.com
SITE_PROTOCOLsẽ giữ một giá trị như http hoặc https
SITE_PROTOCOL_URLsẽ giữ một giá trị như http://example.comhoặc https://example.com
SITE_PROTOCOL_RELATIVE_URLsẽ giữ một giá trị như thế nào //example.com.

mô-đun / bối cảnh

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
    }

cài đặt

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

SITE_URL = 'example.com'

Sau đó, trên các mẫu của bạn, sử dụng chúng như {{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}{{ SITE_PROTOCOL_RELATIVE_URL }}


2

Trong mẫu Django bạn có thể làm:

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

1
Điều này làm việc cho tôi cảm ơn bạn. Tôi đã để cho phép yêu cầu trong MẪU, context_processors: django.template.context_processors.request, cũng [how-to này giúp] ( simpleisbetterthancomplex.com/tips/2016/07/20/... )
ionescu77

Đồng ý, blog Vitor Freitas là một nguồn tuyệt vời cho các nhà phát triển Django! :)
Dos

2

Nếu bạn sử dụng bộ xử lý ngữ cảnh "yêu cầu" và đang sử dụng khung trang web Django và cài đặt phần mềm trung gian Trang web (tức là cài đặt của bạn bao gồm các cài đặt này):

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

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

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

... sau đó bạn sẽ có requestđối tượng có sẵn trong các mẫu và nó sẽ chứa một tham chiếu đến hiện tại Sitecho yêu cầu như request.site. Sau đó, bạn có thể truy xuất tên miền trong một mẫu với:

    {{request.site.domain}}

1

Phương pháp này thì sao? Làm việc cho tôi. Nó cũng được sử dụng trong đăng ký 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)

Nhưng dùng thử nó localhostsẽ giúp bạn có một httpslược đồ (Nó được coi là an toàn) sẽ không hoạt động nếu bạn có url tĩnh (chỉ http://127.0.0.1hợp lệ, không phải https://127.0.0.1). Vì vậy, nó không lý tưởng khi vẫn còn trong quá trình phát triển.
ThePhi

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

-5

Bạn có thể sử dụng {{ protocol }}://{{ domain }}trong các mẫu của bạn để có được tên miền của bạn.


Tôi không nghĩ rằng @Erwan thông báo rằng điều này phụ thuộc vào bộ xử lý bối cảnh yêu cầu không chuẩn.
đơn sắc

Tôi không thể thực hiện công việc này, bạn định nghĩa giao thức và tên miền ở đâu?
Jose Luis de la Rosa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.