将 UniqueConstraint 添加到 ManyToMany 字段

Adding a UniqueConstraint to a ManyToMany field

我想在我的数据库中添加一个限制条件,以便一份申请只能与一个职位空缺相关联。我不希望能够从 shell 或 django 管理页面进入,进入一个空缺和 select 一个已经与空缺相关联的应用程序。我想提出某种验证错误。但是我有点不确定我应该怎么做?

models.py

class Vacancy(models.Model):
    CATEGORY_CHOICES = [
        ('ADMINISTRATION', 'Administration'),
        ('CONSULTING', 'Consulting'),
        ('ENGINEERING', 'Engineering'),
        ('FINANCE', 'Finance'),
        ('RETAIL', 'Retail'),
        ('SALES', 'Sales'),
    ]
    employer = models.ForeignKey('Employer', on_delete=models.CASCADE)
    job_title = models.CharField(max_length=35, default=None)
    main_duties = models.TextField(default=None, validators=[
        MinLengthValidator(650),
        MaxLengthValidator(2000)
    ])
    person_spec = models.TextField(default=None, validators=[
        MinLengthValidator(650),
        MaxLengthValidator(2000)
    ])
    salary = models.PositiveIntegerField(default=None, validators=[
        MinValueValidator(20000), 
        MaxValueValidator(99000)
    ])
    city = models.CharField(choices=CITY_CHOICES, max_length=11, default=None)
    category = models.CharField(choices=CATEGORY_CHOICES, max_length=15, default=None)
    max_applications = models.PositiveSmallIntegerField(blank=True, null=True)
    deadline = models.DateField(default=None)
    applications = models.ManyToManyField('Application', blank=True, related_name='submissions')
    

    class Meta:
        verbose_name_plural = 'vacancies'
        constraints = [
            models.UniqueConstraint(fields=['id', 'applications'], name="unique_application")
        ]


class Application(models.Model):
    STAGES = [
        ('pre-selection', 'PRE-SELECTION'),
        ('shortlisted', 'SHORTLISTED'),
        ('rejected pre-interview', 'REJECTED PRE-INTERVIEW'),
        ('rejected post-interview', 'REJECTED POST-INTERVIEW'),
        ('successful', 'SUCCESSFUL')
    ]
    candidate = models.ForeignKey('Candidate', on_delete=models.CASCADE)
    job = models.ForeignKey('Vacancy', on_delete=models.CASCADE)
    cv = models.CharField(max_length=60, default=None)
    cover_letter = models.TextField(default=None, validators=[
        MinLengthValidator(0),
        MaxLengthValidator(2000)
    ])
    submitted = models.DateTimeField(auto_now_add=True)
    stage = models.CharField(choices=STAGES, max_length=25, default='pre-selection')

如您所见,我已将约束添加到 Vacancy 模型:

constraints = [
            models.UniqueConstraint(fields=['id', 'applications'], name="unique_application")
        ]

但是当我执行 makemigrations 然后 migrate 时,我得到这个错误:

    raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name))
django.core.exceptions.FieldDoesNotExist: NewVacancy has no field named 'applications'

我不清楚这是什么意思。

有什么想法吗?

听起来像是一对多关系。您可以使用 related_name=applications 而不是 ManyToManyField:

移动到 Application 上的 ForeignKey
class Vacancy(models.Model):
    CATEGORY_CHOICES = [
        ('ADMINISTRATION', 'Administration'),
        ('CONSULTING', 'Consulting'),
        ('ENGINEERING', 'Engineering'),
        ('FINANCE', 'Finance'),
        ('RETAIL', 'Retail'),
        ('SALES', 'Sales'),
    ]
    employer = models.ForeignKey('Employer', on_delete=models.CASCADE)
    job_title = models.CharField(max_length=35, default=None)
    main_duties = models.TextField(default=None, validators=[
        MinLengthValidator(650),
        MaxLengthValidator(2000)
    ])
    person_spec = models.TextField(default=None, validators=[
        MinLengthValidator(650),
        MaxLengthValidator(2000)
    ])
    salary = models.PositiveIntegerField(default=None, validators=[
        MinValueValidator(20000),
        MaxValueValidator(99000)
    ])
    city = models.CharField(choices=CITY_CHOICES, max_length=11, default=None)
    category = models.CharField(choices=CATEGORY_CHOICES, max_length=15, default=None)
    max_applications = models.PositiveSmallIntegerField(blank=True, null=True)
    deadline = models.DateField(default=None)


class Application(models.Model):
    STAGES = [
        ('pre-selection', 'PRE-SELECTION'),
        ('shortlisted', 'SHORTLISTED'),
        ('rejected pre-interview', 'REJECTED PRE-INTERVIEW'),
        ('rejected post-interview', 'REJECTED POST-INTERVIEW'),
        ('successful', 'SUCCESSFUL')
    ]
    candidate = models.ForeignKey('Candidate', on_delete=models.CASCADE)
    job = models.ForeignKey('Vacancy', on_delete=models.CASCADE)
    cv = models.CharField(max_length=60, default=None)
    cover_letter = models.TextField(default=None, validators=[
        MinLengthValidator(0),
        MaxLengthValidator(2000)
    ])
    submitted = models.DateTimeField(auto_now_add=True)
    stage = models.CharField(choices=STAGES, max_length=25, default='pre-selection')
    vacancy = models.ForeignKey(Vacancy, related_name='applications', on_delete=models.DO_NOTHING)