Ограничение для запрета NaN в числовых столбцах postgres с помощью Django ORM
Postgresql допускает NaN
значения в числовых столбцах согласно документации здесь.
При определении таблиц Postgres с помощью Django ORM
, столбец DecimalField
транслируется в столбец numeric
в Postgres. Даже если вы определите столбец следующим образом:
from django.db import models
# You can insert NaN to this column without any issue
numeric_field = models.DecimalField(max_digits=32, decimal_places=8, blank=False, null=False)
Есть ли способ использовать синтаксис Python/Django, чтобы запретить значения NaN
в этом сценарии? Родное решение Postgres, вероятно, заключается в использовании какого-то ограничения. Но возможно ли это с помощью синтаксиса Django?
У меня нет базы данных PostgreSQL для тестирования, но вы можете попробовать создать ограничение базы данных, используя поиск на основе IsNull
looukup:
from decimal import Decimal
from django.db.models import (
CheckConstraint,
DecimalField,
Field,
Model,
Q,
)
from django.db.models.lookups import (
BuiltinLookup,
)
@Field.register_lookup
class IsNaN(BuiltinLookup):
lookup_name = "isnan"
prepare_rhs = False
def as_sql(self, compiler, connection):
if not isinstance(self.rhs, bool):
raise ValueError(
"The QuerySet value for an isnan lookup must be True or False."
)
sql, params = self.process_lhs(compiler, connection)
if self.rhs:
return '%s = "NaN"' % sql, params
else:
return '%s <> "NaN"' % sql, params
class Item(Model):
numeric_field = DecimalField(
max_digits=32,
decimal_places=8,
blank=False,
null=False,
)
class Meta:
constraints = [
CheckConstraint(
check=Q(numeric_field__isnan=False),
name="numeric_field_not_isnan",
),
]
@MT0 ответил на вопрос. Я просто хочу добавить, что если вы используете DemimalField
и манипулируете данными в Django, вы обычно не можете вставить NaN
или Infinity
в базу данных. Действительно, Django сам проверяет, является ли значение бесконечным (NaN
не считается конечным).
Django сначала «подготавливает» значения для представления в базе данных, и делает это с помощью метода .get_db_prep_save(…)
[GitHub]:
def get_db_prep_save(self, value, connection): if hasattr(value, "as_sql"): return value return connection.ops.adapt_decimalfield_value( self.to_python(value), self.max_digits, self.decimal_places )
Which calls the .to_python(…)
function. The .to_python(…)
method [GitHub] then checks if the Decimal
is finite:
def to_python(self, value): # … if not decimal_value.is_finite(): raise exceptions.ValidationError( self.error_messages["invalid"], code="invalid", params={"value": value}, )
Конечно, лучше добавить ограничение, так как все равно возможно, что некоторые запросы на обновление в конечном итоге приведут к NaN
, но для простых вставок/обновлений через Django этого не должно произойти.