我是Python3.8.2/Django 3.1.1的新手,我正在尝试找出向模型添加规则的正确方法,以便从表单和web服务调用这些规则。我遇到的问题是,一些错误没有被正确捕获,它们会生成500个错误
这是我的初始模型:
class TotalPoints(TimeStampedModel):
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='TotalPoints_Course', null=False, blank=False, verbose_name='Curso')
kit = models.ForeignKey(Kit, on_delete=models.CASCADE, related_name='TotalPoints_Kit', null=False, blank=False, verbose_name='Kit')
bought = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Comprados')
assigned = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Asignados')
available = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Disponibles')
class Meta:
db_table = 'app_totalpoints'
constraints = [
models.UniqueConstraint(fields=['course','kit'], name='app_totalpoints.course-kit')
]
字段声明为正值IntegerField以避免负值
这是我的表单和CreateView
class TotalPointsForm(forms.ModelForm):
class Meta:
model = TotalPoints
fields = ['id', 'course', 'kit', 'available', 'bought', 'assigned']
class TotalPointsCreateView(CreateView):
model = TotalPoints
form_class = TotalPointsForm
template_name = 'app/form_base.html'
success_url='../list/'
这是我的模板:
{# app/templates/app/form_base.html #}
{% load crispy_forms_tags %}
<!doctype html>
<html lang="en">
<head lang="es">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
<title>{% block page_title %}{% endblock %}</title>
</head>
<body>
<div class="container-fluid">
<div class="jumbotron">
<h1 class="display-4">{% block form_title %}{% endblock %}</h1>
<p class="lead">{% block form_subtitle %}{% endblock %}
{% if user.is_authenticated %}
( Usuario: {{ user.get_username }} )
{% else %}
( Usuario no registrado. )
{% endif %}
</p>
<hr class="my-4">
<div id='form-errors'>{{ form_errors }}</div>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-success">Grabar</button>
<!-- <a class="btn btn-primary" href="../list" role="button">Cancelar</a> -->
<a class="btn btn-primary" href="#" onclick="location.href = document.referrer; return false;" role="button">Cancelar</a>
</form>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
</body>
</html>
要实施的规则是: 可用=购买-分配 (可争议文件=Compados-Asignados)
这是我尝试的第一个版本,修改后的型号:
class TotalPoints(TimeStampedModel):
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='TotalPoints_Course', null=False, blank=False, verbose_name='Curso')
kit = models.ForeignKey(Kit, on_delete=models.CASCADE, related_name='TotalPoints_Kit', null=False, blank=False, verbose_name='Kit')
bought = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Comprados')
assigned = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Asignados')
available = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Disponibles')
class Meta:
db_table = 'app_totalpoints'
constraints = [
models.UniqueConstraint(fields=['course','kit'], name='app_totalpoints.course-kit')
]
def save(self, *args, **kwargs):
#AVAILABLE = BOUGHT - ASSIGNED
self.available = self.bought - self.assigned
super(TotalPoints, self).save(*args, **kwargs)
如果所有值均为正值,则该规则有效并正确计算可用值 如果Buyd为负值,则表单上会显示正确的错误,但如果可用,则会变为负值(bougth<;assigned),错误不会被捕获 跟踪:
Traceback (most recent call last):
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
psycopg2.errors.CheckViolation: el nuevo registro para la relación «app_totalpoints» viola la restricción «check» «app_totalpoints_available_check»
DETAIL: La fila que falla contiene (36, 2020-10-19 21:16:05.356574+00, 2020-10-20 15:57:39.007548+00, 10, 12, 23, 1, -2).
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/core/handlers/base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
return view_func(request, *args, **kwargs)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/views/generic/base.py", line 98, in dispatch
return handler(request, *args, **kwargs)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/views/generic/edit.py", line 194, in post
return super().post(request, *args, **kwargs)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/views/generic/edit.py", line 142, in post
return self.form_valid(form)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/views/generic/edit.py", line 125, in form_valid
self.object = form.save()
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/forms/models.py", line 460, in save
self.instance.save()
File "/home/egarza/projects/python/DjangoRESTABC/abc_project/app/models.py", line 138, in save
super(TotalPoints, self).save(*args, **kwargs)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/models/base.py", line 753, in save
self.save_base(using=using, force_insert=force_insert,
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/models/base.py", line 790, in save_base
updated = self._save_table(
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/models/base.py", line 872, in _save_table
updated = self._do_update(base_qs, using, pk_val, values, update_fields,
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/models/base.py", line 926, in _do_update
return filtered._update(values) > 0
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/models/query.py", line 803, in _update
return query.get_compiler(self.db).execute_sql(CURSOR)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1522, in execute_sql
cursor = super().execute_sql(result_type)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1156, in execute_sql
cursor.execute(sql, params)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/backends/utils.py", line 98, in execute
return super().execute(sql, params)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/backends/utils.py", line 66, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/home/egarza/projects/python/DjangoRESTABC/django-rest-abc-env/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: el nuevo registro para la relación «app_totalpoints» viola la restricción «check» «app_totalpoints_available_check»
DETAIL: La fila que falla contiene (36, 2020-10-19 21:16:05.356574+00, 2020-10-20 15:57:39.007548+00, 10, 12, 23, 1, -2).
我切换到pre_save using signals中的一个规则,但结果是一样的: 使用信号的版本02:
class TotalPoints(TimeStampedModel):
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='TotalPoints_Course', null=False, blank=False, verbose_name='Curso')
kit = models.ForeignKey(Kit, on_delete=models.CASCADE, related_name='TotalPoints_Kit', null=False, blank=False, verbose_name='Kit')
bought = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Comprados')
assigned = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Asignados')
available = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Disponibles')
class Meta:
db_table = 'app_totalpoints'
constraints = [
models.UniqueConstraint(fields=['course','kit'], name='app_totalpoints.course-kit')
]
@receiver(pre_save, sender=TotalPoints, dispatch_uid="totalpoints_presave")
def totalpoints_presave(sender, instance, **kwargs):
instance.available = instance.bought - instance.assigned
那么,这些规则应该在哪里实现,以便使表单和Django能够正确地处理错误呢
添加:
我知道我正在提前计算表单以捕获错误,这是否意味着我必须在表单上复制规则并始终进行重复验证
模型上是否有一个标准事件来执行此计算?(我也通过web服务调用model,所以我需要规则是通用的)
我知道我可以使用@property,但我需要数据库上的字段,因为它稍后将被另一个进程(非Python进程)使用
我是一个老的C#/Java程序员,我可能犯了非常明显的错误,所以请教育我,不要假设我理解全部内容
先谢谢你
部分答案
我修改了Melvyns注释并重写了clean方法:
class TotalPoints(TimeStampedModel):
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='TotalPoints_Course', null=False, blank=False, verbose_name='Curso')
kit = models.ForeignKey(Kit, on_delete=models.CASCADE, related_name='TotalPoints_Kit', null=False, blank=False, verbose_name='Kit')
bought = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Comprados')
assigned = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Asignados')
available = models.PositiveIntegerField(default=0, null=False, blank=False, verbose_name='Disponibles')
class Meta:
db_table = 'app_totalpoints'
constraints = [
models.UniqueConstraint(fields=['course','kit'], name='app_totalpoints.course-kit')
]
def clean(self):
super().clean()
if self.assigned > self.bought:
#OPTION 1 Raise on the Form
#raise ValidationError('Asignados debe ser menor o igual a Comprados')
#OPTION 2 Raise on the field
raise ValidationError({'assigned': 'Asignados no debe ser mayor a Comprados.'})
由于约束,保存数据库认为错误的内容时,会发生完整性错误。这太晚了,表单无法捕获它
ModelForms在模型上调用
full_clean()
,并将捕获任何要处理的验证错误,就像它是一个表单错误一样。通话顺序如下:因此,您可以在instance.clean()上进行验证,并确保引发ValidationErrors。相关阅读:
Form validation:
Model validation
相关问题 更多 >
编程相关推荐