在 Django 中使用 UniqueConstraint 填充模型时键入错误

问题描述 投票:0回答:1

我正在尝试在 Django 中构建我的第一个项目,在创建模型 Prevision(包括销售预测)的条目时遇到一些问题,该模型有一个 uniqueConstraint 以确保供应商之间只有一个预测(模型“Representada”)和给定年份的客户(模型“Cliente”)(字段“Ano”)。

我的models.py文件如下:

from django.db import models
from django.db.models import CheckConstraint, Q, F, UniqueConstraint
from django.db.models.signals import pre_save
from django.dispatch import receiver
from datetime import datetime

# Create your models here.
class Empresa (models.Model):
    CIF = models.CharField(max_length=9, unique=True, blank=True)
    Nombre = models.CharField(max_length=64)
    Telefono = models.CharField(max_length=16, blank=True)
    Email = models.EmailField(blank=True)
    Direccion = models.CharField(max_length=64)
    Ciudad = models.CharField(max_length=32)
    CP = models.PositiveIntegerField(default=1)
    Provincia = models.IntegerField(default=1)
    Contacto = models.CharField(max_length=64, blank=True)
    Observaciones = models.TextField(blank=True)
    
    def __str__(self):
        return f"{self.Nombre}"
    
    class Meta:
        abstract = True
        verbose_name_plural = "Empresas"
        constraints = [
        ]
    
class Representada (Empresa):
    Fecha_Alta = models.DateField()
    Fecha_Baja = models.DateField(blank=True, null=True)
    Porcentaje_Comision = models.DecimalField(max_digits=5, decimal_places=2)
    
    class Meta:
        verbose_name_plural = "Representadas"
        constraints = [
            CheckConstraint(
                check = Q(Fecha_Baja__isnull=True)|Q(Fecha_Alta__lte=F('Fecha_Baja')),
                name = 'Comprobar fecha de alta y baja de representada',
            ),
        ]
    
class Cliente (Empresa):
    Hay_Toro = models.BooleanField(blank=True)
    Inicio_Horario_Descarga = models.TimeField(blank=True, null=True)
    Fin_Horario_Descarga = models.TimeField(blank=True, null=True)
    
    def __str__(self):
        return f"{self.Nombre} ({self.Ciudad})"
    
    class Meta:
        verbose_name_plural = "Clientes"
        constraints = [
            CheckConstraint(
                check = Q(Inicio_Horario_Descarga__isnull=True, Fin_Horario_Descarga__isnull=True)|Q(Inicio_Horario_Descarga__isnull=False, Fin_Horario_Descarga__isnull=False, Inicio_Horario_Descarga__lte=F('Fin_Horario_Descarga')),
                name = 'Comprobar horario de descarga',
            ),
        ]
    
class Prevision (models.Model):
    Ano = models.PositiveSmallIntegerField(default=datetime.now().year)
    Cliente = models.ForeignKey("Cliente", on_delete=models.CASCADE)
    Representada = models.ForeignKey("Representada", on_delete=models.CASCADE)
    Importe = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    
    def __str__(self):
        return f"Año: {self.Ano} Representada: {self.Representada} Cliente: {self.Cliente}"
    
    class Meta:
        verbose_name_plural = "Previsiones"
        constraints = [
            UniqueConstraint(
                fields=['Ano', 'Cliente', 'Representada'], name='Primary_Key_Prevision'
            )
        ]
    
@receiver(pre_save, sender=Prevision)
def comprobar_ano_prevision(sender, instance, ** kwargs) :
    representada = Representada.objects.get(id = instance.Representada)
    if representada.Fecha_Baja is not NONE and representada.Fecha_Baja.year < instance.Año:
        raise Exception("No se puede generar una previsión para una representada dada de baja")
        

模型正确迁移后,我进入管理模式并注册供应商(即“Representada”)和客户(即“Cliente”),没有任何问题。一旦我在数据库中注册了其中一个,我就会尝试对预测(即“预测”)执行相同的操作,但是,当尝试通过网络浏览器中的管理模式执行此操作时,我收到了服务器错误(500)返回。

为了更好地理解发生了什么,我运行了一个 shell 来仔细查看错误:

(Desarrollo) (...)\Desarrollo\Company>py manage.py shell
Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun  6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from BaseDatos.models import Representada, Prevision, Cliente
>>> Cliente.objects.all().values()
<QuerySet [{'id': 4, 'CIF': '2', 'Nombre': 'Cli1', 'Telefono': '', 'Email': '', 'Direccion': '2', 'Ciudad': '2', 'CP': 13300, 'Provincia': 13, 'Contacto': '', 'Observaciones': '', 'Hay_Toro': False, 'Inicio_Horario_Descarga': None, 'Fin_Horario_Descarga': None}]>
>>> Representada.objects.all().values()
<QuerySet [{'id': 3, 'CIF': '1', 'Nombre': 'Rep1', 'Telefono': '', 'Email': '', 'Direccion': '1', 'Ciudad': '1', 'CP': 13300, 'Provincia': 13, 'Contacto': '', 'Observaciones': '', 'Fecha_Alta': datetime.date(2024, 6, 26), 'Fecha_Baja': None, 'Porcentaje_Comision': Decimal('1.00')}]>
>>> Prevision.objects.all().values()
<QuerySet []>
>>> prev = Prevision(Ano='2023', Cliente=Cliente.objects.get(id='4'), Representada=Representada.objects.get(id='3'), Importe='100.00')
>>> prev.save()
Traceback (most recent call last):
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\fields\__init__.py", line 2117, in get_prep_value
    return int(value)
           ^^^^^^^^^^
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'Representada'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\base.py", line 822, in save
    self.save_base(
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\base.py", line 889, in save_base
    pre_save.send(
  File "(...)\Desarrollo\Lib\site-packages\django\dispatch\dispatcher.py", line 189, in send
    response = receiver(signal=self, sender=sender, **named)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Company\BaseDatos\models.py", line 290, in comprobar_ano_prevision
    representada = Representada.objects.get(id = instance.Representada)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\query.py", line 635, in get
    clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
                                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\query.py", line 1476, in filter
    return self._filter_or_exclude(False, args, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\query.py", line 1494, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\query.py", line 1501, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\sql\query.py", line 1613, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\sql\query.py", line 1645, in _add_q
    child_clause, needed_inner = self.build_filter(
                                 ^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\sql\query.py", line 1559, in build_filter
    condition = self.build_lookup(lookups, col, value)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\sql\query.py", line 1389, in build_lookup
    lookup = lookup_class(lhs, rhs)
             ^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\lookups.py", line 30, in __init__
    self.rhs = self.get_prep_lookup()
               ^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\lookups.py", line 364, in get_prep_lookup
    return super().get_prep_lookup()
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\lookups.py", line 88, in get_prep_lookup
    return self.lhs.output_field.get_prep_value(self.rhs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "(...)\Desarrollo\Lib\site-packages\django\db\models\fields\__init__.py", line 2119, in get_prep_value
    raise e.__class__(
TypeError: Field 'id' expected a number but got <Representada: Rep1>.
>>>

如果我尝试使用数字而不是实例,正如 TypeError 所建议的那样,事情并不会变得更好:

>>> prev = Prevision(Ano='2023', Cliente=Cliente.objects.get(id='4'), Representada='3', Importe='100.00')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "D:\Documents\Trabajo\00_Ofrevit\Desarrollo\Lib\site-packages\django\db\models\base.py", line 543, in __init__
    _setattr(self, field.name, rel_obj)
  File "D:\Documents\Trabajo\00_Ofrevit\Desarrollo\Lib\site-packages\django\db\models\fields\related_descriptors.py", line 284, in __set__
    raise ValueError(
ValueError: Cannot assign "'3'": "Prevision.Representada" must be a "Representada" instance.

我想这个问题对于有一点经验的人来说一定是非常明显的,它应该与 Prevision 有一个 uniqueConstraint 来保持伪多字段 PK 与 Django 自动插入的 id 键分开这一事实有一定的关系,但是我只是不明白在哪里或为什么......任何建议将不胜感激!

django foreign-keys unique-constraint composite-primary-key
1个回答
0
投票

看看你的

pre_save
信号功能:

@receiver(pre_save, sender=Prevision)
def comprobar_ano_prevision(sender, instance, **kwargs):
    representada = Representada.objects.get(id=instance.Representada)
    ...

首先尝试将实例分配为 ID,然后尝试从 int 访问实例字段(错误消息),所以:

Representada.objects.get(id=instance.Representada.id)
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.