Reputation: 29805
I am trying to do the following:
class AmountInfoForm(forms.ModelForm):
amount = forms.IntegerField()
class Meta:
model = Customer
fields = ('amount',)
For my model field:
class Customer(models.Model):
amount = models.DecimalField(max_digits=6, decimal_places=2, null=True)
This is so that values that show in my text input are round as opposed to showing decimal places.
The problem is that decimal places still show.
It only accepts the number 60
for example as input value. However when it shows the form instance of the field, it shows 60.00
What am I doing wrong?
Upvotes: 2
Views: 6382
Reputation: 18983
Alternative 2022 answer
Usage: if you enter 60
in the creation form, form redirects you to update form and displays 60
in the field (not 60.00
). In the database, the amount column shows 0.6
. The database field is decimal
type.
urls.py
from django.urls import path
from .views import AmountCreateView, AmountUpdateView
app_name = 'customers'
urlpatterns = [
path('', AmountCreateView.as_view(), name='create'),
path('<int:pk>/', AmountUpdateView.as_view(), name='update'),
]
models.py
from django.db import models
from django.shortcuts import reverse
class Customer(models.Model):
amount = models.DecimalField(max_digits=6, decimal_places=2, null=True)
def get_absolute_url(self):
return reverse("customers:update", kwargs={'pk': self.pk})
widgets.py
from django.forms.widgets import NumberInput
import decimal
class CustomWidget(NumberInput):
def format_value(self, value):
if value is None:
return value
if isinstance(value, decimal.Decimal):
# it should return this if everything's OK
return str(int(decimal.Decimal(value) * 100))
return super().format_value(value)
forms.py
from django import forms
from .models import Customer
from .widgets import CustomWidget
import decimal
from django.utils import formats
from django.core.exceptions import ValidationError
class CustomIntegerField(forms.IntegerField):
widget = CustomWidget
def to_python(self, value):
if value in self.empty_values:
return None
if self.localize:
value = formats.sanitize_separators(value)
value = str(value).strip()
try:
value = decimal.Decimal(value) / 100
except decimal.DecimalException:
raise ValidationError(self.error_messages['invalid'], code='invalid')
return value
class AmountInfoForm(forms.ModelForm):
amount = CustomIntegerField()
class Meta:
model = Customer
fields = ('amount',)
views.py
from django.views.generic import FormView, UpdateView
from .forms import AmountInfoForm
from .models import Customer
class AmountCreateView(FormView):
template_name = 'home.html'
form_class = AmountInfoForm
def form_valid(self, form):
self.object = form.save()
return super().form_valid(form)
def get_success_url(self):
return self.object.get_absolute_url()
class AmountUpdateView(UpdateView):
template_name = 'home.html'
form_class = AmountInfoForm
model = Customer
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
</body>
</html>
Upvotes: 0
Reputation: 11808
1) Django field is not Django widget, default widget for DecimalField
and for IntegerField
is TextInput
.
And from this note your declaration overrides default behaviour of amount
field. But data stored in Decimal
type not int
so if you want represent it in int
you should do smth like this:
int(Decimal())
Or read Decimal docs
and set custom rounding.
2) At this step you need smth to specify your custom rendering, so there are number of variation how you can do it (at this moment I remember two of them):
2.1) Simple one - override initial data in ModelForm init:
class AmountInfoForm(forms.ModelForm):
class Meta:
model = Customer
fields = ('amount',)
def __init__(self, *args, **kwargs):
super(AmountInfoForm, self).__init__(*args, **kwargs)
amount_initial = self.initial.get('amount')
if amount_initial:
self.initial['amount'] = int(amount_initial)
2.2) If you need custom representation in fancy widget you can override TextInput
(look at the source) and specify it in your class Meta
docs :
class YourCustomWidget(TextInput):
def render(self, name, value, attrs=None):
if value is None:
value = ''
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
if value != '':
# Only add the 'value' attribute if a value is non-empty.
custom_value = int(value)
final_attrs['value'] = force_text(self._format_value(custom_value))
# here you can customise output html
return format_html('<input{0} />', flatatt(final_attrs))
class AmountInfoForm(forms.ModelForm):
class Meta:
model = Customer
fields = ('amount',)
widgets = {
'amount': YourCustomWidget(),
}
Upvotes: 3