我正在开发一个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.')
但是,验证不能按预期工作:我仍然可以同时将“员工”分配到同一个“项目”(即开始日期和结束日期)。
非常感谢帮助。
您要做的是验证给定实例的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,
)