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.

Réponses:


380

Utiliser avec:

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

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.


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.



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.


ç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.


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/


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.


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,


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.


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 }}
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.