问题描述:

My question is very simple, but I can't manage to solve my problem: I need to process a Field every time that this is showed in the admin, because it has to be saved differently in the DB.

For example, I need the user to input percentages in the admin, (say 50, 70 or 100), but those values will be saved in the database as 0.5, 0.7 or 1. After, when the user wants to edit or just see those values, they mus't be preprocessed to show them as percetages (integer numbers) again, even though they were saved as floats in the DB.

I thought something like:

def valid_percentage(value):

if not 0 <= value <= 1:

raise ValidationError(u'Must be a value between 0 and 100')

class PercentageField(models.IntegerField):

def __init__(self, *args, **kwargs):

kwargs['validators'] = kwargs.get('validators', []) + [valid_percentage]

super(PercentageField, self).__init__(*args, **kwargs)

def to_python(self, value):

return None if value is None else int(100 * value)

def get_prep_value(self, value):

return None if value is None else value/100.0

will do it. But this is saving and showing data all wrong.

网友答案:

I took it pretty far, got one version of your code that works for saving, another that works for display. So that's quite some fun but not really useful.

A correct to achieve what you want is:

  1. Use a simple DecimalField or FloatField in your Model class definition,

  2. Create a ModelForm for your Model

  3. Define clean_yourpercentagefield() in your ModelForm to divide input value by 100

  4. Make and use a custom widget class to multiply the value by 100 before rendering

As stated by django custom fields and modelform documentation:

you must ensure that the form field used to represent your custom field performs whatever input validation and data cleaning is necessary to convert user-provided form input into a to_python()-compatible model field value. This may require writing a custom form field, and/or implementing the formfield() method on your field to return a form field class whose to_python() returns the correct datatype.

Indeed, you can also take these steps:

  1. Create a PercentageField class overriding formfield()

  2. Create PercentageFormField which should be called by PercentageField.formfield()

  3. Create PercentageWidget which PercentageFormField will use by default

The error you will run into if you intent to do it the way you suggested, which is wrong, is that to_python() is called several times over and over again, basically with this code:

def to_python(self, value):
    print 'to python', value
    value = None if value is None else int(100 * value)consider this section of the documentation
    print 'converted to python', value
    return value

Saving the model from a modelform will output something like:

to python 32.0
converted to python 3200
clean 3200
to python 3200
converted to python 320000

You'd have to use SubFieldMetaclass if you insist in wanting to do it the wrong way.

Conclusion: KISS FTW !

Credits to SmileyChris for helping in this answer, which might not be perfect but i thought it would be too bad to not share what i've found.

网友答案:

Try get_db_prep_value and then look into DB to see on which step does it fail.

相关阅读:
Top