在Django中对ModelForm的自定义验证

时间:2019-03-13 21:44:24

标签: django django-models django-forms

我正在开发Django应用来注册销售。我创建了三个模型:项目,员工和销售。

项目和员工模型如下:

class Project(models.Model):
    project_id = models.IntegerField(primary_key = True)
    name = models.CharField(max_length = 100, unique = True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

class Employee(models.Model):
    employee_id = models.IntegerField(primary_key = True)
    name = models.CharField(max_length = 50)
    email = models.CharField(max_length = 40)

class Meta:
    ordering = ['name']

def __str__(self):
    return self.name

然后是销售模式:

class Sale(models.Model):
    sale_name = models.CharField(max_length = 30)
    project = models.ForeignKey('Project', on_delete = models.CASCADE)

    proactive_seller = models.ManyToManyField(Employee, related_name = 'proactive')

    participants = models.ManyToManyField(Employee, related_name = 'participant')

    doers = models.ManyToManyField(Employee, related_name = 'doer')

    start_date = models.DateField()
    end_date = models.DateField()

    def __str__(self):
        return self.sale_name

因此,每个销售对象都包含与销售相关的项目,哪个员工是主动/潜在卖家,哪些员工参与了销售以及哪些员工将进行实际项目的信息。

在我的forms.py中,如果用户试图输入已经具有相同项目,相同日期和相同执行者的销售,那么我想确保销售是唯一的,即我想提出一个错误,即一次最多只能将一个行动者分配给该项目一次。

我的forms.py当前看起来像这样:

class SaleForm(ModelForm):
    class Meta:
        model = Sale
        widgets = {
        'start_date': DatePickerInput(), 
        'end_date': DatePickerInput(), 
    }

我尝试了以下操作:

    def clean(self):
        cleaned_data = super.clean()
        start = cleaned_data.get('start_date')
        end = cleaned_data.get('end_date')
        doers = cleaned_data.get('doers')
        project = cleaned_data.get('project')
        if start and end and doers and project:
            queryset = Sale.objects.all()
            # Filter based on project
            q = queryset.filter(project__name=project, start_date = start, end_date = end)
            for employee in doers:
                q = q.filter(doers__name=employee)
            if q.count() > 1:
                raise forms.ValidationError('Sale has already been registered.')

但是,验证无法按预期进行:我仍然可以在同一时间(即开始日期和结束日期)将“雇员”分配给同一“项目”。

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

您要尝试的是验证给定实例的M2M关系的每个实例。这可能很难做到。足够的是过滤具有相同行为者数量的销售,并过滤掉包含不同行为者的销售。

from django.db.models import F, OuterRef, Exists, Q

q = queryset.filter(project__name=project, start_date=start, end_date = end)
other_doers = Employee.objects.filter(
     # Exclude any employee with the name of the doers on this project.
     # We only want other doers.
     ~Q(name__in=[e.name for e in doers]),
     # This links the subquery to the main query (Sale)
     doer=OuterRef('id'),
)
q = q.annotate(
    # Get the count of doers per sale
    doer_count=Count('doers__id', distinct=True),
    # Check if other doers are in the project
    has_other_doer=Exists(other_doers)
).filter(
    # Only look for sales with the same number of doers
    doer_count=len(doers),
    # Filter out sales that contain other doers
    has_other_doer=False,
)