Formatar números em modelos de django

154

Estou tentando formatar números. Exemplos:

1     => 1
12    => 12
123   => 123
1234  => 1,234
12345 => 12,345

Parece uma coisa bastante comum, mas não consigo descobrir qual filtro devo usar.

Edit: Se você tem uma maneira genérica de Python para fazer isso, fico feliz em adicionar um campo formatado no meu modelo.

Oli
fonte
3
Tenha cuidado se seus usuários de destino também estiverem na Europa. Alguns países europeus como a Alemanha usam como marca decimal.
tobltobs

Respostas:

312

O aplicativo humanizado contribuído pelo Django faz isso:

{% load humanize %}
{{ my_num|intcomma }}

Certifique-se de adicionar 'django.contrib.humanize'à sua INSTALLED_APPSlista no settings.pyarquivo.

Ned Batchelder
fonte
17
Existe uma razão para que esses poucos filtros simples não façam parte dos filtros internos?
PawelRoman
8
@PawelRoman Para evitar o carregamento de um monte de filtros e tags raramente usado (e assim fazer a renderização de template mais rápido)
Maxime Lorant
Eu adicionei 'django.contrib.humanize' no INSTALLED_APPS e também incluí {% load humanize%} no meu modelo também e reiniciei o servidor. No entanto, quando tento usar o filtro "intcomma", recebo este erro - Filtro inválido: 'intcomma' O rastreamento de depuração mostra 'django.contrib.humanize' incluído em settings.py. Qual poderia ser o possível problema?
Manish
1
Tenho resposta para o meu próprio problema aqui - eu incluíra {% load humanize%} no modelo base (já que preciso dele para muitos modelos). Parece que não funciona - quando o adicionei nos modelos relevantes, funcionou bem! :-)
Manish
Se não funcionar, pode ser devido à localização. Experimente {{my_num | intcomma: False}} nesse caso
Jose Cherian
105

Com base em outras respostas, para estender isso aos carros alegóricos, você pode:

{% load humanize %}
{{ floatvalue|floatformat:2|intcomma }}

Documentação: floatformat, intcomma.

Vinod Kurup
fonte
59

Em relação à solução de Ned Batchelder, aqui estão duas casas decimais e um cifrão. Isso vai a algum lugar comomy_app/templatetags/my_filters.py

from django import template
from django.contrib.humanize.templatetags.humanize import intcomma

register = template.Library()

def currency(dollars):
    dollars = round(float(dollars), 2)
    return "$%s%s" % (intcomma(int(dollars)), ("%0.2f" % dollars)[-3:])

register.filter('currency', currency)

Então você pode

{% load my_filters %}
{{my_dollars | currency}}
Dave Aaron Smith
fonte
3
Bug no código acima, tente o seguinte:>>> currency(0.99958) u'$0.00'
Ahsan 05/04
1
Esse código deve estar em seu próprio arquivo em [your_project] / [your_app] / templatetags / [filtername] .py. Em seguida, ele deve ser carregado no modelo com {% load [filtername]%}. Consulte docs.djangoproject.com/en/1.10/howto/custom-template-tags para obter informações mais úteis e específicas.
Mightypile 5/03
20

Tente adicionar a seguinte linha em settings.py:

USE_THOUSAND_SEPARATOR = True

Isso deve funcionar.

Consulte a documentação .


atualização em 16/04/2018:

Há também uma maneira python de fazer isso:

>>> '{:,}'.format(1000000)
'1,000,000'
carton.swing
fonte
10
Tenha cuidado ao usar isso. Também irá formatar números em urls e outros que você adicionar ao modelo
Christoffer
4
Não posso enfatizar o comentário de @Christoffer o suficiente - em modelos em que os IDs são renderizados e usados ​​em links, isso pode causar grandes dores de cabeça!
LaundroMat 11/09/18
Esta não é apropriado para modelos de Django
Ben
@ Ben, o que não é apropriado? "a maneira python" ou o sinalizador de mil separadores nas configurações? o sinalizador de mil separadores deve funcionar bem para modelos do Django. qual é a versão do seu Django?
carton.swing
1
O formato de string (o python) é porque você não pode usá-lo diretamente em modelos - você teria que colocá-lo em uma tag de modelo (que é uma boa resposta), mas apenas jogar isso fora é de pouca utilidade como resposta completa.
Ben
11

Se você não deseja se envolver com as localidades, aqui está uma função que formata números:

def int_format(value, decimal_points=3, seperator=u'.'):
    value = str(value)
    if len(value) <= decimal_points:
        return value
    # say here we have value = '12345' and the default params above
    parts = []
    while value:
        parts.append(value[-decimal_points:])
        value = value[:-decimal_points]
    # now we should have parts = ['345', '12']
    parts.reverse()
    # and the return value should be u'12.345'
    return seperator.join(parts)

Criar um filtro de modelo personalizado a partir desta função é trivial.

muhuk
fonte
10

A solução humanizar é boa se o seu site estiver em inglês. Para outros idiomas, você precisa de outra solução: eu recomendo usar o Babel . Uma solução é criar uma tag de modelo personalizada para exibir os números corretamente. Veja como: basta criar o seguinte arquivo em your_project/your_app/templatetags/sexify.py:

# -*- coding: utf-8 -*-
from django import template
from django.utils.translation import to_locale, get_language
from babel.numbers import format_number

register = template.Library()

def sexy_number(context, number, locale = None):
    if locale is None:
        locale = to_locale(get_language())
    return format_number(number, locale = locale)

register.simple_tag(takes_context=True)(sexy_number)

Em seguida, você pode usar essa tag de modelo nos seus modelos assim:

{% load sexy_number from sexify %}

{% sexy_number 1234.56 %}
  • Para um usuário americano (localidade en_US), isso exibe 1.234,56.
  • Para um usuário francês (código do idioma fr_FR), isso exibe 1 234,56.
  • ...

Claro que você pode usar variáveis:

{% sexy_number some_variable %}

Nota: o contextparâmetro atualmente não é usado no meu exemplo, mas eu o coloco lá para mostrar que você pode facilmente ajustar essa tag de modelo para usá-la com qualquer coisa que esteja no contexto do modelo.

MiniQuark
fonte
1
minha localidade é 'pt_BR' e "intcomma" funciona mesmo no Brasil, sendo a separação por '.' e não por ',' sendo floatvalue = 25000.35 {{floatvalue | intcomma}} resulta em 25000.35
Zokis
Você está certo, mas esse formato não é apropriado em relação ao separador de milhares (25.000,35 para en_US e 25 000,35 para fr_FR).
MiniQuark
4

O aplicativo humanizar oferece uma maneira rápida e agradável de formatar um número, mas se você precisar usar um separador diferente da vírgula, é simples reutilizar o código do aplicativo humanizado , substituir o caractere separador e criar um filtro personalizado . Por exemplo, use o espaço como um separador:

@register.filter('intspace')
def intspace(value):
    """
    Converts an integer to a string containing spaces every three digits.
    For example, 3000 becomes '3 000' and 45000 becomes '45 000'.
    See django.contrib.humanize app
    """
    orig = force_unicode(value)
    new = re.sub("^(-?\d+)(\d{3})", '\g<1> \g<2>', orig)
    if orig == new:
        return new
    else:
        return intspace(new)
Enis Afgan
fonte
Obrigado. Eu precisava de uma versão em ponto e sua sugestão me salvou! Felicidades!
Kstratis
3

Um pouco fora do tópico:

Encontrei esta pergunta enquanto procurava uma maneira de formatar um número como moeda, assim:

$100
($50)  # negative numbers without '-' and in parens

Acabei fazendo:

{% if   var >= 0 %} ${{ var|stringformat:"d" }}
{% elif var <  0 %} $({{ var|stringformat:"d"|cut:"-" }})
{% endif %}

Você também pode fazer, por exemplo, {{ var|stringformat:"1.2f"|cut:"-" }}exibir como $50.00(com 2 casas decimais, se é isso que você deseja.

Talvez um pouco do lado hacky, mas talvez alguém ache isso útil.

Felix Böhme
fonte
2

Bem, eu não consegui encontrar uma maneira do Django, mas encontrei uma maneira python de dentro do meu modelo:

def format_price(self):
    import locale
    locale.setlocale(locale.LC_ALL, '')
    return locale.format('%d', self.price, True)
Oli
fonte
Hmm - isso foi marcado com -1, sem explicações - inútil. Para aqueles que pensam que a resposta está incorreta de alguma forma, diga por que em benefício do respondente e dos leitores. Suponho que não sou fã de definir o local (que é global em alcance), nesta única função (efeito desagradável de obter a moeda) - o que poderia explicar o -1.
Danny Staple
Eu fiz o segundo -1 - como descrito no anterior. Comente, s really not correct not giving any reason. Here ité fácil: o Django possui um modelo específico (semelhante ao Jinja2 - ou é o Jinja2), que não permite funções python padrão. Portanto, esta resposta não é de todo útil. O que é funções próprias mais, Django têm obtido gerir este e escrevendo nada de novo que está funcionando realmente não é boa idéia ...
Tomis
2
Não concordo com os que rejeitam o voto - garantir que o valor seja formatado corretamente na função view antes de atingir o modelo parece uma abordagem perfeitamente válida.
Paul Tomblin
1

Esteja ciente de que a alteração da localidade é de todo o processo e não é segura para threads (ou seja, pode ter efeitos colaterais ou afetar outro código executado no mesmo processo).

Minha proposta: confira o pacote Babel . Alguns meios de integração com modelos do Django estão disponíveis.

zgoda
fonte
1

Com base na resposta muhuk, fiz esse string.formatmétodo simples de encapsular tag python .

  • Crie um templatetagsna pasta do aplicativo.
  • Crie um format.pyarquivo nele.
  • Adicione isto a ele:

    from django import template
    
    register = template.Library()
    
    @register.filter(name='format')
    def format(value, fmt):
        return fmt.format(value)
  • Carregue-o no seu modelo {% load format %}
  • Use-o. {{ some_value|format:"{:0.2f}" }}
lagartixas
fonte