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

156

Comment obtenir le nom de domaine de mon site actuel à partir d'un modèle Django? J'ai essayé de regarder dans la balise et les filtres mais rien là-bas.

Jean-François Fabre
la source

Réponses:

67

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

phsiao
la source
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.

Carl Meyer
la source
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.

danbruegge
la source
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.

panchicore
la source
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)),
    }
vdboor
la source
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
monsieur
la source
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

Edward Newell
la source
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 %}
S. Kirby
la source
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 %}
Dennis Golomazov
la source
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 }}

Julián Landerreche
la source
2

Dans un modèle Django, vous pouvez faire:

<a href="{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ request.path }}?{{ request.GET.urlencode }}" >link</a>
Dos
la source
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}}
user85461
la source
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)

la source
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)
Muneeb Ahmad
la source
-5

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

Erwan
la source
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