Comment puis-je obtenir le nom de domaine de mon site dans un modèle Django?


Réponses:


67

Je pense que ce que vous voulez, c'est avoir accès au contexte de la demande, voir RequestContext.


140
request.META['HTTP_HOST']vous donne le domaine. Dans un modèle, ce serait {{ request.META.HTTP_HOST }}.
Daniel Roseman

29
Soyez prudent lorsque vous utilisez des métadonnées de requête. Il provient d'un navigateur et peut être usurpé. En général, vous voudrez probablement suivre ce qui est suggéré ci-dessous par @CarlMeyer.
Josh

2
Pour mes besoins, cela n'a aucune faille de sécurité.
Paul Draper

7
Je suppose que depuis Django 1.5 avec le paramètre d'hôtes autorisés, son utilisation est sûre. docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
Daniel Backman

8
Quelqu'un peut-il expliquer ce qu'est le «trou de sécurité»? Si l'utilisateur usurpe l'en- Host:tête et obtient une réponse avec le domaine usurpé quelque part sur une page, comment cela crée-t-il une faille de sécurité? Je ne vois pas en quoi cela diffère d'un utilisateur prenant le HTML généré et se modifiant avant de le nourrir dans son propre navigateur.
user193130

105

Si vous voulez l'en-tête HTTP Host réel, consultez le commentaire de Daniel Roseman sur la réponse de @ Phsiao. L'autre alternative est que si vous utilisez le framework contrib.sites , vous pouvez définir un nom de domaine canonique pour un site dans la base de données (mapper le domaine de requête à un fichier de paramètres avec le SITE_ID approprié est quelque chose que vous devez faire vous-même via votre configuration du serveur Web). Dans ce cas, vous recherchez:

from django.contrib.sites.models import Site

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

vous devrez placer vous-même l'objet current_site dans un contexte de modèle si vous souhaitez l'utiliser. Si vous l'utilisez partout, vous pouvez l'empaqueter dans un processeur de contexte de modèle.


3
Pour clarifier pour quelqu'un qui a les mêmes problèmes que moi: vérifiez que votre SITE_IDparamètre est égal à l' idattribut du site actuel dans l'application Sites (vous pouvez le trouver iddans le panneau d'administration de Sites). Lorsque vous appelez get_current, Django prend votre SITE_IDet renvoie l' Siteobjet avec cet identifiant de la base de données.
Dennis Golomazov

Aucun de ces éléments ne fonctionne pour moi. 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

J'ai découvert la {{ request.get_host }}méthode.


11
Veuillez noter que cette réponse présente les mêmes problèmes que l'approche de Daniel Roseman (elle peut être usurpée) mais elle est sûrement plus complète lorsque l'hôte est atteint via un proxy HTTP ou un équilibreur de charge car il prend en compte l' HTTP_X_FORWARDED_HOSTen-tête HTTP.
furins

4
Utilisation: "// {{request.get_host}} / tout / autre / vous / voulez" ... Assurez-vous de remplir votre paramètre ALLOWED_HOSTS (voir docs.djangoproject.com/en/1.5/ref/settings/#allowed -hôtes ).
Seth

3
@Seth mieux utiliser request.build_absolute_uri( docs.djangoproject.com/en/dev/ref/request-response/… )
MrKsn

60

En complément de Carl Meyer, vous pouvez créer un processeur de contexte comme celui-ci:

module.context_processors.py

from django.conf import settings

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

paramètres locaux.py

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

settings.py

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

modèles renvoyant une instance de contexte dont l'URL du site est {{SITE_URL}}

vous pouvez écrire votre propre rutine si vous souhaitez gérer des sous-domaines ou SSL dans le processeur de contexte.


J'ai essayé cette solution mais si vous avez plusieurs sous-domaines pour la même application ce n'est pas pratique, j'ai trouvé très utile la réponse de danbruegge
Jose Luis de la Rosa

dans settings.py vous devez introduire votre processeur de contexte dans context_processors> OPTIONS> TEMPLATES
yas17

24

La variante du processeur de contexte que j'utilise est:

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

L' SimpleLazyObjectencapsuleur garantit que l'appel de base de données se produit uniquement lorsque le modèle utilise réellement l' siteobjet. Cela supprime la requête des pages d'administration. Il met également en cache le résultat.

et incluez-le dans les paramètres:

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

Dans le modèle, vous pouvez utiliser {{ site.domain }}pour obtenir le nom de domaine actuel.

edit: pour prendre en charge également la commutation de protocole, utilisez:

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

Vous n'avez pas besoin de l'utiliser SimpleLazyObjectici, car le lambda ne sera pas appelé si rien n'accède de toute façon au «site».
monokrome

Si vous supprimez le SimpleLazyObject, chacun RequestContextappellera get_current_site()et exécutera donc une requête SQL. L'encapsuleur s'assure que la variable n'est évaluée que lorsqu'elle est réellement utilisée dans le modèle.
vdboor

1
Puisqu'il s'agit d'une fonction, la chaîne hôte ne sera pas traitée à moins qu'elle ne soit utilisée de toute façon. Ainsi, vous pouvez simplement attribuer une fonction à 'site_root' et vous n'avez pas besoin de SimpleLazyObject. Django appellera la fonction lorsqu'elle sera utilisée. Vous avez déjà créé la fonction nécessaire avec un lambda ici de toute façon.
monokrome

Ah oui, seul un lambda fonctionnerait. Le SimpleLazyObjectest là pour éviter la réévaluation de la fonction, ce qui n'est pas vraiment nécessaire puisque l' Siteobjet est mis en cache.
vdboor

L'importation est maintenantfrom django.contrib.sites.shortcuts import get_current_site
Hraban

22

Je sais que cette question est ancienne, mais je suis tombée dessus à la recherche d'un moyen pythonique pour obtenir le domaine actuel.

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

4
build_absolute_uriest documenté ici .
Philipp Zedler

19

Rapide et simple, mais pas bon pour la production:

(dans une vue)

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

(dans un modèle)

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

Assurez-vous d'utiliser un RequestContext , ce qui est le cas si vous utilisez render .

Ne faites pas confiance request.META['HTTP_HOST']à la production: ces informations proviennent du navigateur. Utilisez plutôt la réponse de @ CarlMeyer


Je vote pour cette réponse mais j'ai reçu une erreur en essayant d'utiliser request.scheme. Peut-être uniquement disponible dans les versions plus récentes de django.
Matt Cremeens

@MattCremeens a request.schemeété ajouté dans Django 1.7.
S. Kirby

16

{{ request.get_host }}doit protéger contre les attaques d'en-tête HTTP Host lorsqu'il est utilisé avec le ALLOWED_HOSTSparamètre (ajouté dans Django 1.4.4).

Notez que {{ request.META.HTTP_HOST }}n'a pas la même protection. Voir la documentation :

ALLOWED_HOSTS

Une liste de chaînes représentant les noms d'hôte / de domaine que ce site Django peut servir. Il s'agit d'une mesure de sécurité pour empêcher les attaques d'en-tête HTTP Host , qui sont possibles même dans de nombreuses configurations de serveur Web apparemment sûres.

... Si l'en- Hosttête (ou X-Forwarded-Hosts'il USE_X_FORWARDED_HOSTest activé) ne correspond à aucune valeur de cette liste, la django.http.HttpRequest.get_host()méthode augmentera SuspiciousOperation.

... Cette validation s'applique uniquement via get_host(); si votre code accède à l'en-tête Host directement à partir de request.METAvous, vous contournez cette protection de sécurité.


En ce qui concerne l'utilisation de requestdans votre modèle, les appels de la fonction de rendu de modèle ont changé dans Django 1.8 , vous n'avez donc plus à gérer RequestContextdirectement.

Voici comment rendre un modèle pour une vue, à l'aide de la fonction de raccourci render():

from django.shortcuts import render

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

Voici comment rendre un modèle pour un e-mail, lequel IMO est le cas le plus courant où vous souhaitez la valeur d'hôte:

from django.template.loader import render_to_string

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

Voici un exemple d'ajout d'une URL complète dans un modèle d'e-mail; request.scheme devrait obtenir httpou httpsselon ce que vous utilisez:

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

10

J'utilise une balise de modèle personnalisée. Ajouter à par exemple <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

Utilisez-le dans un modèle comme celui-ci:

{% load site %}
{% current_domain %}

Y a-t-il un inconvénient particulier à cette approche? Hormis l'appel au Site db à chaque demande.
kicker86

@ kicker86 Je n'en connais pas. get_currentest une méthode documentée: docs.djangoproject.com/en/dev/ref/contrib/sites/…
Dennis Golomazov

3
'http://%s'pourrait être un problème en cas de httpsconnexion; schéma n'est pas dynamique dans ce cas.
Endommagé bio

4

Semblable à la réponse de l'utilisateur panchicore, c'est ce que j'ai fait sur un site Web très simple. Il fournit quelques variables et les rend disponibles sur le modèle.

SITE_URLtiendrait une valeur comme example.com
SITE_PROTOCOLtiendrait une valeur comme http ou https
SITE_PROTOCOL_URLcontiendrait une valeur comme http://example.comou https://example.com
SITE_PROTOCOL_RELATIVE_URLtiendrait une valeur comme //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'

Ensuite, sur vos modèles, les utiliser comme {{ SITE_URL }}, {{ SITE_PROTOCOL }}, {{ SITE_PROTOCOL_URL }}et{{ SITE_PROTOCOL_RELATIVE_URL }}


2

Dans un modèle Django, vous pouvez faire:

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

1
Cela a fonctionné pour moi merci. J'ai dû activer la demande dans TEMPLATES, context_processors:, django.template.context_processors.requestaussi [ce guide a aidé] ( simpleisbetterthancomplex.com/tips/2016/07/20/… )
ionescu77

D'accord, le blog Vitor Freitas est une excellente source pour les développeurs Django! :)
Dos

2

Si vous utilisez le processeur de contexte "request" , et que vous utilisez le framework de sites Django et que le middleware du site est installé (c'est-à-dire que vos paramètres les incluent):

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

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

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

... alors vous aurez l' requestobjet disponible dans des modèles, et il contiendra une référence au courant Sitepour la demande sous forme de request.site. Vous pouvez ensuite récupérer le domaine dans un modèle avec:

    {{request.site.domain}}

1

Et cette approche? Travaille pour moi. Il est également utilisé dans django-registration .

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)

Mais l'essayer avec localhostvous donnera un httpsschéma (il est considéré comme sécurisé) qui ne fonctionnera pas si vous avez une URL statique (seule http://127.0.0.1est valide, non https://127.0.0.1). Ce n'est donc pas idéal quand il est encore en développement.
ThePhi

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

-5

Vous pouvez utiliser {{ protocol }}://{{ domain }}dans vos modèles pour obtenir votre nom de domaine.


Je ne pense pas que @Erwan remarque que cela dépend d'un processeur de contexte de requête non standard.
monokrome

Je ne pourrais pas faire ce travail, où définissez-vous le protocole et le domaine?
Jose Luis de la Rosa
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.