Code

Opened 2 years ago

Closed 16 months ago

#17662 closed New feature (wontfix)

Adds percentage filter to humanize module

Reported by: eire1130 Owned by: nobody
Component: contrib.humanize Version: master
Severity: Normal Keywords: humanize
Cc: anssi.kaariainen@… Triage Stage: Design decision needed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: yes UI/UX: no

Description

I often find myself using a custom filter for this, and in searching around the internet, there seems to be others who also would like this built in. I searched developers and the ticket system and didn't see any pre-existing conversations or patches.

Attachments (3)

django_percentage.diff (3.5 KB) - added by eire1130 2 years ago.
percentage filter
django_percentage_take_two (4.0 KB) - added by eire1130 2 years ago.
humanize percentage using number_format
django_percentage_take_two.diff (4.0 KB) - added by eire1130 2 years ago.
percentage with diff

Download all attachments as: .zip

Change History (13)

Changed 2 years ago by eire1130

percentage filter

comment:1 Changed 2 years ago by akaariai

  • Cc anssi.kaariainen@… added
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement set
  • Triage Stage changed from Unreviewed to Accepted

I am in general in support of this, as I can see the need for this common enough to include in Django.

I wonder if you should use decimal.Decimal instead to get proper rounding. If I am not mistaken the string formatting just truncates the value. I will mark the patch as needs improvement for that reason.

comment:2 Changed 2 years ago by eire1130

I believe str.format("{':.places%'}") rounds "correctly", example:

myfloat= .56784

print "{:.2%}".format(myfloat)

56.78%

myfloat = .56785

print "{:.2%}".format(myfloat)

56.78%

myfloat = .56786

print "{:.2%}".format(myfloat)

56.79%

(note the .5 case rounds down to 0 and .51 rounds up to 1)

Although, this has me thinking. I'm not certain if the .format method is available in Python 2.5X branch (maybe it is in the future module, but I don't have a working branch of 2.5 on this machine)

Either way, would you like me to proceed utilizing decimal, or go with the string format.

comment:3 Changed 2 years ago by akaariai

From Python's documentation it seems format is only available 2.6 onwards. I don't know what is going to be the minimum required Python version for Django 1.5, either 2.5 or 2.6. So, that could be a problem.

But looking more closely at this, it seems you should use the localization framework of Django in some way or another. I guess the best way would be to format according to what django.utils.formats.numberformat gives you. Then you would not need to worry about decimal places, numberformat would do that automatically for you. You should also check the use_l10n variable from the context and pass it on to the numberformat. This should make the tag more consistent with rest of Django, and also easier to write.

Changed 2 years ago by eire1130

humanize percentage using number_format

comment:4 Changed 2 years ago by eire1130

Here's a patch using django.utils.formats.number_format. I'm not checking for for USE_L10N or USE_THOUSAND_SEPARATOR explicitly, because django.utils.formats.number_format and django.utils.numberformat already check for it.

I still needed to use round(value) because numberformat doesn't give predictable rounding (the unit-tests were failing).

Changed 2 years ago by eire1130

percentage with diff

comment:5 Changed 2 years ago by akaariai

  • Triage Stage changed from Accepted to Design decision needed

I was thinking that context.use_l10n would be available, but I see it is not available for filters. This is a bit sad, as now the percentage filter doesn't respect {% localize on/off %} controls. I don't know what there is to do about this. Not localizing the value seems inconsistent, and not honoring the localize on/off controls seems even worse.

Any opinions? Maybe you should go with your first version. But then .format isn't available before Python 2.6.

comment:6 Changed 2 years ago by akaariai

Hmmh, I wonder if the best approach would be to just have a filter |multiply "100".

Then you could do {{ val|multiply 100}}% and let number formatting deal with all the ugly details of what to do for rounding etc.

comment:7 Changed 2 years ago by eire1130

In thinking about this last night, I agree with your concerns, and I also agree with your original point. I don't think using .format(%) is a good way to go, even if it were well documented this is only a 2.6+ feature.

So, I think a couple of things:

  1. It's fairly uncommon to have percents in the thousand+ range. Although possible, of course. I work in financial services, and usually when percentages get that big they lose their meaning, so instead I would say something is 10X rather than 1,000%; nonetheless, people still do represent very large numbers in percentage terms and so any system should be able to accommodate using very large percentages; however, considering the rarity and the special case, I would say someone can just write a custom filter if the built-in doesn't meet their needs exactly. So, if USE_L10N and USE_THOUSAND_SEPARATOR are both set to True in settings, and for this discrete case the developer didn't want to use the USE_THOUSAND_SEPARATOR they could simply write a custom filter, which is where we are at today, except applying to everyone (and I think using percentages is a fairly common use case)
  1. However, I don't like things being sloppy (but then there are only so many edge cases one can catch). The other alternative, and this is a bit more forward looking, I've thought for a while it would be nice to have filters as a class based system, where the filters themselves are methods of a class which would be loaded such as {% load load_module|NameofClass %}. Putting this together with the above concerns, instead loading filters would like this: {% load load_module|NameofClass:Context %}, where the class itself takes Context in it's init method.

This would give filters themselves direct access to context and all of the other goodies therein (not just use_l10n). The syntax of course doesn't have to be exactly like the above, but you can see my general idea.

That said, I know very little about that side of Django and how the filters are initiated, so I have no idea what level of work this would entail.

  1. More on topic, since filters are limited to only one argument, the precision argument could be dropped and always default to some arbitrary length (I like this idea even less, as I need to adjust the precision based on what I'm doing fairly often)
  1. Or, of course, the number could be rounded before being passed to the filter and simply not do any rounding at all.
  1. I think {{ val|multiply 100}}% is a good enough workaround, but ideally I would like percentage included (simply because I can see the typos that would arise)

comment:8 Changed 2 years ago by akaariai

Any larger refactoring should be discussed on the django-developers mailing list. There has been some talk about refactoring the template system mainly for speed purposes. The idea is to restrict the access to context to make the template system a lot (like 2-5x faster).

I think the ability to access use_l10n and use_tz variables of the context in filters is needed. Maybe there is already some way, I am no expert of the template system. Anyways, asking directions of what to do (especially if you consider no.2 idea above) from the mailing list is the best guidance I can give.

comment:9 Changed 17 months ago by fakotb

This was left alone for a long time, but it may be a nice feature to have. So I'm updating this ticket to let everybody know what the status is.

According to core developer jezdez, the context will not be available for template filters for some time. However it's already possible to use context in a template tag. So if we create a percentage template tag instead of a filter it's possible to take into account localization.

Here is a page that specifies how a percentage should be written in different languages: http://www.unicode.org/cldr/charts/by_type/patterns.numbers.html#17acd127f9139476
The good news is that the % sign never changes. The placement of this sign however differs per language, which makes things quite complicated. As said before it is not good to use Pythons format. Not because Django still wants to support 2.5 (it doesn't), but because the way format works depends on the locale of the installation and not the locale set in Django.

It would be nice to also support permille if we're putting effort in supporting percentage.

comment:10 Changed 16 months ago by aaugustin

  • Resolution set to wontfix
  • Status changed from new to closed

|multiply is a no go (look up "math filter" in this tracker if you want to see an ugly troll).

It's unfortunate that the design of the Django template language prevents us from writing a locale-aware filter :/ and a {% percent %} tag isn't a nice API.

For lack of a better idea, I'm going to admit defeat and leave it up to users to precompute percentages :(

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.