Comment concaténer des chaînes dans des modèles django?

191

Je veux concaténer une chaîne dans une balise de modèle Django, comme:

{% extend shop/shop_name/base.html %}

Ici shop_name ma variable et je veux concaténer cela avec le reste du chemin.

Supposons que j'aie shop_name=example.comet que je veuille que le résultat s'étende shop/example.com/base.html.

Ahsan
la source

Réponses:

380

Utiliser avec:

{% with "shop/"|add:shop_name|add:"/base.html" as template %}
{% include template %}
{% endwith %}
Steven
la source
2
J'ai été totalement confus par cette réponse car elle utilise la balise include au lieu de la balise extend, mais apparemment, cela fonctionne. Bien que je recommande la propre réponse d'Ahsan car elle fonctionne également et est (à mon avis) sémantiquement plus correcte et soulève moins de confusion.
gitaarik
15
Cela peut fonctionner mais ne doit pas être considéré comme une réponse générale à la concaténation de chaînes dans les modèles django. Voir stackoverflow.com/a/23783666/781695
utilisateur
Comme le dit le dicton dans la documentation de Django, "Les chaînes qui peuvent être forcées en nombres entiers seront additionnées, non concaténées" Donc, par exemple, si vous voulez concaténer les clés primaires d'un objet modèle (peut être utile pour créer une clé de cache unique), cela ne fonctionne pas. travail.
zen11625
Je pense que cela n'échappe pas shop_namedu tout, donc c'est dangereux.
Flimm le
Attention, comme déjà mentionné, cela ne fonctionne qu'avec des chaînes! Si vous traduisez shop_nameavant de le transmettre au contexte d'une vue, get_context_dataassurez-vous qu'il est traduit en utilisant ugettextau lieu de ugettext_lazy.
Kim
111

Ne pas utiliser addpour les chaînes, vous devez définir une balise personnalisée comme celle-ci:

Créez un fichier: <appname>\templatetags\<appname>_extras.py

from django import template

register = template.Library()

@register.filter
def addstr(arg1, arg2):
    """concatenate arg1 & arg2"""
    return str(arg1) + str(arg2)

puis utilisez-le comme @Steven dit

{% load <appname>_extras %}

{% with "shop/"|addstr:shop_name|addstr:"/base.html" as template %}
    {% include template %}
{% endwith %}

Raison pour éviter add:

Selon la documentation

Ce filtre essaiera d' abord de forcer les deux valeurs à des entiers ... Les chaînes qui peuvent être forcées à des entiers seront additionnées, non concaténées ...

Si les deux variables sont des entiers, le résultat serait inattendu.

utilisateur
la source
Cela ne devrait-il pas être @ register.filter (name = 'addstr')?
seddonym
6
Cela devrait être marqué comme la meilleure réponse car cela fonctionne correctement avec des valeurs qui peuvent être forcées par Python sous forme d'entiers.
zen11625
2
Je ne sais pas pourquoi vous n'êtes pas celui qui a le plus de "up" parce que c'est votre réponse qui est juste, le " add" seul n'utilise tout simplement pas str()en premier lieu et ne fonctionne pas du tout pour moi alors que votre solution fonctionne parfaitement
Olivier Pons
1
Votre réponse m'a sauvé!
Ljubisa Livac
6
N'oubliez pas de charger votre filtre personnalisé en haut de votre fichier de modèle:{% load <appname>_extras %}
Susanne Peng
13

J'ai changé la hiérarchie des dossiers

/shop/shop_name/base.html Vers /shop_name/shop/base.html

et puis ci-dessous fonctionnerait.

{% extends shop_name|add:"/shop/base.html"%} 

Maintenant, il est capable d'étendre la page base.html.

Ahsan
la source
7

Reportez-vous à Concaténation de chaînes dans les modèles Django :

  1. Pour les versions antérieures de Django:

    {{ "Mary had a little"|stringformat:"s lamb." }}

"Mary avait un petit agneau."

  1. Autre:

    {{ "Mary had a little"|add:" lamb." }}

"Mary avait un petit agneau."

bing
la source
3

Jetez un œil au addfiltre .

Modifier: vous pouvez enchaîner les filtres, vous pouvez donc le faire "shop/"|add:shop_name|add:"/base.html". Mais cela ne fonctionnera pas car il appartient à la balise de modèle d'évaluer les filtres dans les arguments, et ne le fait pas.

Je suppose que vous ne pouvez pas faire cela dans les modèles.

Daniel Hepper
la source
ça ne va pas marcher. je veux ajouter ma variable au milieu du chemin.
Ahsan
ajouter le filtre seulement additionné pas concaténer selon django docs
Ahsan
Les documents disent que "les chaînes qui peuvent être converties en nombres entiers seront additionnées". Les autres chaînes sont concaténées. Mais cela n'a pas vraiment d'importance de toute façon car vous ne pouvez pas utiliser le filtre :(
Daniel Hepper
2

À partir de la documentation:

Cette balise peut être utilisée de deux manières:

  • {% extends "base.html" %} (avec guillemets) utilise la valeur littérale "base.html" comme nom du modèle parent à étendre.
  • {% extends variable %}utilise la valeur de variable. Si la variable correspond à une chaîne, Django utilisera cette chaîne comme nom du modèle parent. Si la variable évalue un objet Template, Django utilisera cet objet comme modèle parent.

Il semble donc que vous ne pouvez pas utiliser de filtre pour manipuler l'argument. Dans la vue appelante, vous devez soit instancier le modèle ancêtre, soit créer une variable de chaîne avec le chemin correct et la transmettre avec le contexte.

Paulo Scardine
la source
1

La réponse de @ error est fondamentalement juste, vous devriez utiliser une balise de modèle pour cela. Cependant, je préfère une balise de modèle légèrement plus générique que je peux utiliser pour effectuer tout type d'opérations similaires à celle-ci:

from django import template
register = template.Library()


@register.tag(name='captureas')
def do_captureas(parser, token):
    """
    Capture content for re-use throughout a template.
    particularly handy for use within social meta fields 
    that are virtually identical. 
    """
    try:
        tag_name, args = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError("'captureas' node requires a variable name.")
    nodelist = parser.parse(('endcaptureas',))
    parser.delete_first_token()
    return CaptureasNode(nodelist, args)


class CaptureasNode(template.Node):
    def __init__(self, nodelist, varname):
        self.nodelist = nodelist
        self.varname = varname

    def render(self, context):
        output = self.nodelist.render(context)
        context[self.varname] = output
        return ''

et ensuite vous pouvez l'utiliser comme ceci dans votre modèle:

{% captureas template %}shop/{{ shop_name }}/base.html{% endcaptureas %}
{% include template %}

Comme le commentaire le mentionne, cette balise de modèle est particulièrement utile pour les informations qui sont répétables dans tout un modèle mais qui nécessitent une logique et d'autres éléments qui accrocheront vos modèles, ou dans les cas où vous souhaitez réutiliser les données passées entre les modèles via des blocs:

{% captureas meta_title %}{% spaceless %}{% block meta_title %}
    {% if self.title %}{{ self.title }}{% endif %}
    {% endblock %}{% endspaceless %} - DEFAULT WEBSITE NAME
{% endcaptureas %}

puis:

<title>{{ meta_title }}</title>
<meta property="og:title" content="{{ meta_title }}" />
<meta itemprop="name" content="{{ meta_title }}">
<meta name="twitter:title" content="{{ meta_title }}">

Le crédit pour le tag captureas est dû ici: https://www.djangosnippets.org/snippets/545/

K3TH3R
la source
1

J'ai trouvé que travailler avec l' {% with %}étiquette était assez compliqué. Au lieu de cela, j'ai créé la balise de modèle suivante, qui devrait fonctionner sur des chaînes et des entiers.

from django import template

register = template.Library()


@register.filter
def concat_string(value_1, value_2):
    return str(value_1) + str(value_2)

Ensuite, chargez la balise de modèle dans votre modèle en haut en utilisant ce qui suit:

{% load concat_string %}

Vous pouvez ensuite l'utiliser de la manière suivante:

<a href="{{ SOME_DETAIL_URL|concat_string:object.pk }}" target="_blank">123</a>

J'ai personnellement trouvé que c'était beaucoup plus propre à travailler.

Bono
la source
0

Vous ne pouvez pas faire de manipulation de variables dans les modèles django. Vous avez deux options, soit écrire votre propre tag de modèle, soit le faire en vue,

Damir
la source
mon exigence est de le faire uniquement dans des modèles afin que l'option vues ne soit pas utile. J'ai également essayé via une balise de modèle personnalisé, mais {% load concat%} devrait après la balise {% extend ....%}. alors comment puis-je le faire maintenant?
Ahsan
Ecrivez une balise extended_extends qui accepte un format de chaîne et des arguments.
Paulo Scardine
pouvez-vous s'il vous plaît me donner un exemple de la façon d'écrire des balises personnalisées pour celles par défaut?
Ahsan
0

extendsn'a aucune facilité pour cela. Placez le chemin du modèle entier dans une variable de contexte et utilisez-la, ou copiez la balise de modèle exist et modifiez-la de manière appropriée.

Ignacio Vazquez-Abrams
la source
merci pour la réponse! pour la variable de contexte, je dois définir dans view.py ce que je ne peux pas en raison des exigences de mon projet. et veuillez donner un exemple du deuxième.
Ahsan
0

Et concaténation multiple:

from django import template
register = template.Library()


@register.simple_tag
def concat_all(*args):
    """concatenate all args"""
    return ''.join(map(str, args))

Et dans le modèle:

{% concat_all 'x' 'y' another_var as string_result %}
concatenated string: {{ string_result }}
Gassan
la source