我正在使用Django开发一个物业管理系统,现在我正在开发一个以“属性检查”命名的应用程序,基本上它的目的是为表单提供一个包含“ Diswasher:clean”之类的任务列表&空吗?”,这些任务需要由工作人员在物业检查。
主要思想是允许管理员在管理员端创建任务及其类别。 示例:任务-洗碗水:干净且空的属于类别-厨房。
每个属性检查都属于一个属性,它具有任务列表,并且这些任务具有不同的状态,例如“已检查”或“需要注意”。
到目前为止,这是我创建的:
models.py
class Task(models.Model):
name = models.CharField(db_column='SafetyTaskName', max_length=100, blank=False, null=False)
category = models.ForeignKey(Categories, db_column='category')
task_check = models.ForeignKey(TaskCheck)
class Categories(models.Model):
name = models.CharField(db_column='Categories', max_length=40, null=False, blank=False)
class TaskCheck(models.Model):
status = models.CharField(db_column='Status', choices=STATUS_CHOICES, default='nd')
image = models.ImageField(upload_to='property_check',null=True)
notes = models.CharField(db_column='Notes', max_length=500, blank=True, null=True) # Field name made lowercase.
class Propertycheck(models.Model):
property = models.ForeignKey(Property, models.DO_NOTHING, db_column='ID_Property') # Field name made lowercase.
task = models.CharField(TaskCheck)
name = models.CharField(db_column='Name', max_length=150)
date = models.DateField(db_column='Date', default=timezone.now) # Field name made lowercase.
next_visit = models.DateField(db_column='Next Visit')
staff = models.ForeignKey(User, db_column='Staff', max_length=25)
notes = models.CharField(db_column='Notes', max_length=500, blank=True, null=True) # Field name made lowercase.
我假装的功能示例:
一名工作人员去了需要检查的财产,他填补了 包含所有任务的表单。如果需要更多任务, 管理员转到管理面板并添加一个新面板。相同状态 适用于所有任务。
要求:
问题: 我对在哪里使用外键有些困惑。我需要进行属性检查以显示任务列表,以及每个任务的状态。
由于我的经验,我现在停留在这个位置,因此我需要一些帮助。 您能否看一下已完成的工作,并让我知道更好的解决方案?
* **更新 ***
感谢Bruno Desthuilliers的回答,我可以按照他的建议来重组我的模型。我认为此解决方案更接近我的需求,但是我的问题是,根据Bruno回答的要求,我的更改是否100%正确?
class Task(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Categories)
property = models.ManyToManyField(Property)
class Categories(models.Model):
name = models.CharField(max_length=40)
class TaskCheck(models.Model):
status = models.CharField(choices=STATUS_CHOICES, default='nd')
image = models.ImageField(upload_to='task_check', null=True)
notes = models.TextField(max_length=500)
task = models.ForeignKey(Task)
property_check = models.ForeignKey(Propertycheck)
class Propertycheck(models.Model):
property = models.ForeignKey(Property, models.DO_NOTHING)
name = models.CharField(max_length=150)
date = models.DateField(default=timezone.now)
next_visit = models.DateField()
staff = models.ForeignKey(User, max_length=25)
notes = models.TextField(max_length=500, default='')
我的英语不是最好的,我不确定我的问题的最佳标题。
答案 0 :(得分:0)
一个属性有许多属性检查;
这仅描述关系的基数的一半-您还需要指定属性检查可以属于多少个属性。在这种情况下,答案似乎很明显(我看不到相同的属性检查将属于多个属性的情况),但是除非您对域有真正的了解,否则您应该仍然询问您的客户-有时“明显”的东西实际上是错误的;-)
但是,如果我们认为“一个属性具有多个属性检查”和“一个属性检查属于一个属性”,则我们具有一对多的关系。在数据库模式级别,这是通过“许多”侧“一个”侧的外键实现的,即PropertyCheck在Property上必须有fk。
当您记得在关系模型中字段是原子值(每个字段中只有一个值)时,这是合乎逻辑的。您无法在Property中存储相关的PropertyCheck ID列表,但可以在每个PropertyCheck中存储一个Property ID。
当您考虑约束时,这也是合乎逻辑的-属性实际上可以进行“零对多”相关的属性检查(到目前为止,您可以拥有从未被“检查”过的属性),但是PropertyCheck必须具有相关属性(没有属性进行属性检查是没有意义的,对吗?)。如果属性检查ID以列表形式存储在“属性”中,则您仍然可以创建不带属性的属性检查(如果删除属性检查并且属性的属性检查列表未更新,也会产生一致性问题)。
因此,长话短说:对于一对多关系,fk驻留在“许多”一侧,并指向“一个”一侧。
属性检查包含任务列表;
您确定这是对的吗?在我看来,您正在将应用程序的用户视图与数据库模式混淆。
当然,用户在“属性检查”页面上查看的内容是要执行的任务列表(以及每个任务的复选框等),但这并不意味着这些任务属于属性检查。如果是这种情况,管理员将必须为每个属性检查创建一个新的任务列表...据我所知,对于域来说,流行的一点是每个任务都有一个 task 列表。 property ,并且系统为每个 property check 构建(尚未检查)任务检查的列表。您已经开始设计哪个FWIW。
因此(假设我没问题),您的规则实际上是“每个属性都有一个任务列表”。现在,我们可以对另一种基数进行梳理:一个任务属于一个属性,还是同一任务可以被多个属性共享?
我们已经讨论了第一种情况(请参见上文)。在第二种情况下(实际上可能性更高,因为肯定有很多任务对于大多数属性而言都是相同的),您之间存在多对多关系。这些由关系表实现,该关系表在关系的每一侧都有一个fk,并且在这对fks上具有唯一性约束(您不希望同一属性的相同任务列出两次)。请注意,使用Django的ORM,您无需为此显式声明一个模型(当然,除非您需要向关系中添加其他字段,但到目前为止,我在这里看不到此需要)-只需声明关系的任何一方都有一个many2many字段(并不重要),ORM会为您创建中介表。
然后,您将在属性检查和任务检查之间建立关系。在这里,这是一个简单的一对多关系-一个属性检查有很多任务检查,一个任务检查属于一个单独的属性检查。唯一的限制是这些任务检查的任务必须与属性检查的属性属于同一属性(是的,以xD编写时有点困惑)。简而言之:将属性的任务列表用作创建属性检查任务列表的蓝图。
您拥有的IOW:
管理员必须具有添加任务和类别的能力;
这确实是一个要求,但是它与我们感兴趣的内容无关,因为这是在代码级别(权限)而不是在数据库架构级别处理的。
任务属于一类;
和一个类别可以有许多任务-一对多关系,请参见上文。
由工作人员进行财产检查;
一名工作人员可以进行许多财产检查-一对多关系,请参见上文。
每个属性的任务列表都相同;
啊,这个很有趣。如果是这样,则意味着您实际上不需要Task和Property之间的任何关系。
但这仍然是我要与客户反复检查的事情-从经验来看,客户在解释域时往往只想到一般情况,然后在开始测试软件时就会出现很多极端情况变成蓝色,您突然意识到您将不得不重写一半或更多的架构和代码。实际上,我是在参与的第一个应用程序中就遇到过这种情况-实际上不是作为开发人员,我只是该应用程序的用户之一,而与该应用程序相关的第一件事却暴露出了这些缺点,导致整个月的额外开发费用(雇用我的公司必须为此付费,因为他们签署了-错误-要求)。毋庸置疑,应对这一代价高昂的错误负责的人要么被责备,要么甚至被开除。
每个任务都必须具有状态(例如:完成状态);
这也是错误的。状态属于任务检查,而不是任务。
好的,所以您发布的模型距离不太远。正如我在评论中已经提到的那样,您的一对多关系是错误的(fk在关系的错误方面),但是通过上面的解释,您应该可以解决这个问题。您可能还需要与客户仔细检查一些规则,并相应地调整模型。
现在还有其他几件事:
首先,除非您使用的是旧版数据库(显然不是这种情况),否则最好不使用模型字段db_column
属性-ORM将使用模型字段的名称作为db列名,这通常是最好的默认值-至少,当您要执行原始SQL查询时,您不必在models.py文件中检查列名。请注意,对于外键,模型的属性将产生相关的模型实例,但会创建一个“ fieldname_id”列。
第二点:如果不需要文本字段或charfield,请不要使用“ null = True”-否则,您将有两个可能的值指示“无数据”,即SQL“ NULL”或空字符串。最好只包含其中之一,在这种情况下为空字符串,因此删除“ null = True”并使用“ default =””代替。另外,对于自由文本(例如“ notes”字段),您可能希望使用文本字段而不是charfield。这样可以避免在最大长度上放置无用的约束(您可以打赌用户会要求您扩展),并且还会被Django的ModelForms
转换为适当的html“文本”小部件,而不是(单行) html“输入”。
第三点:“ blank = False”和“ null = False”已经是默认值-除非另外指定,否则必填字段是必需的-因此为必需字段显式传递它们只会增加“代码噪声”。可读性最高的代码根本没有代码;-)
希望为您解决所有问题,如果您不愿意在评论中要求提供详细信息/解释,则为