我需要建立这种关系:
└───SCOPE01
├───PROJECT01
│ └───WORKER01
└───WORKER02
工作人员可以在某些项目上忙,也可以在某些范围内空闲。 我选择了正确的方法,并且否则我该怎么做?
#models.py
from django.db import models
class Scope(models.Model):
title = models.CharField(max_length=30)
class Project(models.Model):
title = models.CharField(max_length=30)
scope = models.ForeignKey(
Scope,
related_name='projects',
on_delete=models.CASCADE
)
class Worker(models.Model):
title = models.CharField(max_length=30)
project = models.ForeignKey(
Project,
related_name='workers',
on_delete=models.CASCADE
)
scope = models.ForeignKey(
Scope,
related_name='workers',
on_delete=models.CASCADE
)
如果这是正确的,如何将同时添加的工作人员限制在范围和项目中?
答案 0 :(得分:1)
工作人员可以在某些项目上忙,也可以在某些范围内空闲。我选择了正确的方法吗?
没有足够的规格说明。这是一个排他的“或”吗? “一定范围内免费”是什么意思?究竟什么是“范围”?工人可以从范围A的“自由”更改为范围B的“在项目上忙”吗?工人既不能“忙于某个项目”,又不能“在某些范围内没有空”?
这些只是我想到的头几个问题... IOW:对于知道上下文的人来说,您的“规格”当然很清楚,但我们不知道!-)< / p>
如果正确的话
实际上,不是完全正确-至少Worker.project
应该可以为空,这样工作人员可以“在一定范围内自由”:
class Worker(models.Model):
title = models.CharField(max_length=30)
project = models.ForeignKey(
Project,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
scope = models.ForeignKey(
Scope,
related_name='workers',
on_delete=models.CASCADE
)
如何将同时添加的工人限制在范围和项目中?
如果这意味着一个工作人员应该与一个范围XOR(异或)或一个项目相关联,那么您必须使两个字段都可为空:
class Worker(models.Model):
title = models.CharField(max_length=30)
project = models.ForeignKey(
Project,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
scope = models.ForeignKey(
Scope,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
然后,您可以将1 /添加几个方法到Worker类中,以将其分配给项目或范围(并同时使其他关系为空):
class Worker(models.Model):
title = models.CharField(max_length=30)
project = models.ForeignKey(
Project,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
scope = models.ForeignKey(
Scope,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
def assign_to_project(self, project, commit=True):
self.project = project
self.scope = None
if commit:
self.save()
def assign_to_scope(self, scope, commit=True):
self.scope = scope
self.project = None
if commit:
self.save()
,然后通过save()
方法添加一些验证(简单但不一定是最佳解决方案):
def save(self, *args, **kw):
if self.project and self.scope:
raise SomeCustomException("cannot assign worker to both a scope and project at the same time")
super(Worker, self).save(*args, **kw)
或using model validation-但您必须确保调用了full_clean
方法(调用clean
方法)(模型形式会自动进行,但这仅是全部)。但是您当然可以使用full_clean
方法自己调用save
:
class Worker(models.Model):
title = models.CharField(max_length=30)
project = models.ForeignKey(
Project,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
scope = models.ForeignKey(
Scope,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
def assign_to_project(self, project, commit=True):
self.project = project
self.scope = None
if commit:
self.save()
def assign_to_scope(self, scope, commit=True):
self.scope = scope
self.project = None
if commit:
self.save()
def clean(self):
if self.project and self.scope:
raise ValidationError("cannot assign worker to both a scope and project at the same time")
def save(self, *args, **kw):
self.full_clean()
super(Worker, self).save(*args, **kw)
编辑:(关于assign_to_scope()
和assign_to_project()
方法)
这是否意味着它们将被自动调用,或者我可以在需要时显式调用它们吗?我只是不明白他们将被称为什么? -您的代码中没有调用它们
对不起,这对我来说是显而易见的,但是不,这里没有魔术,您必须在适当的地方自己调用这些方法-第一个显而易见的地方是您的模型形式和/或视图和/或drf序列化器(如果您使用drf)等。我没有提供任何示例,因为我不知道您将如何实际使用这些模型。
在处理此问题时,您可能希望将这些字段标记为“受保护”(如果您是python的新手,请在它们的名称前添加一个下划线,这是惯例,该属性应表示“可以直接访问)并提供只读的公共访问器,即(nb:问题与解答示例代码,可能会出现错别字或其他错误):
class Worker(models.Model):
title = models.CharField(max_length=30)
_project = models.ForeignKey(
Project,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
@property
def project(self):
return self._project
_scope = models.ForeignKey(
Scope,
null=True,
blank=True,
related_name='workers',
on_delete=models.CASCADE
)
@property
def scope(self):
return self._scope
def assign_to_project(self, project, commit=True):
self._project = project
self._scope = None
if commit:
self.save()
def assign_to_scope(self, scope, commit=True):
self._scope = scope
self._project = None
if commit:
self.save()
def clean(self):
if self._project and self._scope:
raise ValidationError("cannot assign worker to both a scope and project at the same time")
# etc