我正在开发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.')
但是,验证无法按预期进行:我仍然可以在同一时间(即开始日期和结束日期)将“雇员”分配给同一“项目”。
非常感谢您的帮助。
答案 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,
)