Petru Tanas
Petru Tanas

Reputation: 1237

Django model FloatField error 'float' object has no attribute 'as_tuple'

I have a Django model with a FloatField, which I later base a form on. For some reason I get "'float' object has no attribute 'as_tuple'", and unfortunately I have no idea why do I get this error or how to fix it.

models.py:

class Course(models.Model):
    title = models.CharField(max_length = 200)
    author = models.ForeignKey(User,default=None, on_delete=models.SET_DEFAULT)
    description = models.TextField(max_length=1000, blank=True)
    tags = models.TextField(blank = True)
    duration = models.FloatField(validators=(MinValueValidator(0.1),MaxValueValidator(12), DecimalValidator(max_digits=3,decimal_places=2)))


    def __str__(self):
            return self.title

forms.py:

class CourseForm(ModelForm):
    class Meta:
        model = Course
        fields = ('title', 'description', 'price', 'duration', 'tags')

views.py:

@login_required
def create_course(request):
    if request.method == "POST":
        form = CourseForm(request.POST)

        if form.is_valid():

            form.save()
            messages.info(request, f"Course created succesfully!")

        else:
            messages.error(request, "Something went wrong, please resubmit!")


    form = CourseForm()
    return render(request, "main/createcourse.html", {"form": form})

html:

{% extends 'main/header.html' %}
<body>

   {% block content%}
<div class="container">

    <form method="POST">
        {% csrf_token %}

        {{form.as_p}}

        <br>
        <button class="btn" type="submit">Create</button>
    </form>

    If you to modify an existing course, click <a href="/modify"><strong>here</strong></a> instead.
</div>
<br><br>
    {% endblock %}



</body>

Upvotes: 1

Views: 1787

Answers (2)

G&#246;khan Eklund
G&#246;khan Eklund

Reputation: 21

If you really need to use a FloatField, then you need to write your own validator:

def validate_decimals(value):
    s = str(value)
    d = decimal.Decimal(s)
    if abs(d.as_tuple().exponent) > 2:
        raise ValidationError(
            _('%(value)s has more than 2 decimals. Please enter 2 decimals only.'),
            params={'value': value},
        )

Then, you add validators='validate_decimals' when you declare the FloatField.

Note that a float value cannot directly be converted to decimal. It should first be converted to string and then to decimal. See also:

Python float to Decimal conversion

Upvotes: 2

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476544

There is a difference between a float and a Decimal. A Decimal encodes the data by storing the digits of a decimal number. You can however not perform DecimalValidation on a float, since due to rounding errors, it will add extra digits.

You thus can use a DecimalField [Django-doc] instead. Note that you thus need to pass Decimal objects in that case, not floats.

class Course(models.Model):
    title = models.CharField(max_length = 200)
    author = models.ForeignKey(User,default=None, on_delete=models.SET_DEFAULT)
    description = models.TextField(max_length=1000, blank=True)
    tags = models.TextField(blank = True)
    duration = models.DecimalField(max_digits=3,decimal_places=2, validators=(MinValueValidator(0.1),MaxValueValidator(12), DecimalValidator(max_digits=3,decimal_places=2)))


    def __str__(self):
            return self.title

You might want to take a look at the DurationField [Django-doc] to store duration however, this will automatically use a timedelta, and store it as an integer for databases that do not support such types.

Upvotes: 1

Related Questions