简体   繁体   English

这种数据库模型结构的最佳方法是什么?

[英]What's the best approach for this database models structure?

I'm developing a Property Management System with Django, right now I'm working on an app named by "Property Check", basically the purpose of it is to provide a form with a list of tasks like "Diswasher: clean & empty?", those tasks need to be checked at a property by a staff member.我正在用 Django 开发一个物业管理系统,现在我正在开发一个名为“Property Check”的应用程序,基本上它的目的是提供一个包含任务列表的表单,例如“Diswasher:clean & empty? ",这些任务需要由工作人员在酒店进行检查。

The main idea is to allow admin to create Tasks and their Categories on the admin side.主要思想是允许管理员在管理员端创建任务及其类别。 Example: Task - Dishwater: clean & empty belongs to Category - Kitchen.示例:任务 - 洗碗水:清洁和清空属于类别 - 厨房。

Each Property Check belongs to a property, it has the list of tasks and those tasks have different status, like "Checked" or "Needs attention".每个属性检查都属于一个属性,它具有任务列表,这些任务具有不同的状态,例如“已检查”或“需要注意”。

So far this is what I've created:到目前为止,这是我创建的:

models.py模型.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.

Functional example of what I pretend:我假装的功能示例:

A staff member goes to a property that needs to be checked, he fills the form that contains all the tasks.一名工作人员前往需要检查的物业,他填写了包含所有任务的表格。 In case of needing more tasks, the admin goes to the admin panel and adds a new one.如果需要更多任务,管理员会转到管理面板并添加一个新任务。 The same status applies to every task.相同的状态适用于每个任务。

Requirements:要求:

  • A property has many property checks;一个属性有很多属性检查;
  • A property check has a list of tasks;属性检查有一个任务列表;
  • Admin must be capable to add tasks and categories;管理员必须能够添加任务和类别;
  • Tasks belong to one category;任务属于一类;
  • Property checks are made by a staff member;财产检查由一名工作人员进行;
  • The task list is the same to every property;任务列表对每个属性都相同;
  • Every task must have a status (Ex.: completed state);每个任务都必须有一个状态(例如:已完成状态);

Problem: I'm a bit confused about where to use the foreignkeys.问题:我对在哪里使用外键有点困惑。 I need property check to show the list of tasks, and for each one, their status.我需要属性检查来显示任务列表,以及每个任务的状态。

Due to my experience I'm stuck at this right now, so I need some help with this.由于我的经验,我现在陷入困境,所以我需要一些帮助。 Could you please take a look at what've done and let me know a better solution?你能看看我做了什么,让我知道更好的解决方案吗?

* **Update *** * **更新***

Thanks to Bruno Desthuilliers answer, I could restructure my models by following his advices.感谢 Bruno Desthuilliers 的回答,我可以按照他的建议重组我的模型。 I think this solution is closer to what I need, but my question is, are my changes 100% correct according to the requirements on Bruno's answer?我认为这个解决方案更接近我的需要,但我的问题是,根据布鲁诺的答案的要求,我的更改是否 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='')

My english ain't the best and I wasn't sure about the best title for my question.我的英语不是最好的,我不确定我的问题的最佳标题。

A property has many property checks;一个属性有很多属性检查;

This describes only half of the relationship's cardinality - you also need to specify how many properties a property check can belong to.这仅描述了关系基数的一半 - 您还需要指定属性检查可以属于多少个属性。 In this case the answer seems rather obvious (I can't see a case where a same property check would belong to more than one property), BUT unless you have a real and deep working knowledge of the domain, you should STILL ask your customer - sometimes "obvious" things are actually wrong ;-)在这种情况下,答案似乎相当明显(我看不到同一个财产检查属于多个财产的情况),但是除非您对该领域有真实而深入的工作知识,否则您仍然应该询问您的客户- 有时“明显”的事情实际上是错误的;-)

But if we consider that "a property has many property checks" AND "a property check belongs to one single property", we have a one to many relationship.但是如果我们考虑“一个属性有多个属性检查”和“一个属性检查属于一个属性”,我们就有一对多的关系。 At the db schema level, this is materialized by a foreign key on the "one" side in the "many" side, ie PropertyCheck must have a fk on Property.在 db 模式级别,这是由“多”方中“一”方的外键具体化的,即 PropertyCheck 必须在 Property 上有一个 fk。

This is logical when you remember that in the relational model, fields are atomic values (one single value in each field).当您记得在关系模型中,字段是原子值(每个字段中有一个值)时,这是合乎逻辑的。 You couldn't store a list of related PropertyCheck ids in Property, but you can store a single Property id in each PropertyCheck.您无法在 Property 中存储相关 PropertyCheck id 的列表,但您可以在每个 PropertyCheck 中存储一个 Property id。

This is also logical when you think of the constraints - a Property can actually have "zero to many" related property checks (you can have a property that has never been "checked" so far), but a PropertyCheck MUST have a related property (it wouldn't make sense to have a property check without property, would it ?).当您考虑约束时,这也是合乎逻辑的 - 属性实际上可以具有“零到多个”相关属性检查(您可以具有迄今为止从未被“检查过”的属性),但 PropertyCheck 必须具有相关属性(在没有财产的情况下进行财产检查是没有意义的,是吗?)。 If property checks ids where stored as a list in Property, you could still create property checks without property (and you would have consistency issues too if a property check was deleted and the property's list of property checks not updated).如果属性检查以列表形式存储在属性中的 id,您仍然可以创建没有属性的属性检查(如果删除了属性检查并且未更新属性的属性检查列表,您也会遇到一致性问题)。

So, to make a long story short: for a one to many relationship, the fk resides on the "many" side and points to the "one" side.因此,长话短说:对于一对多关系,fk 位于“多”侧并指向“一”侧。

A property check has a list of tasks;属性检查有一个任务列表;

Are you sure this one is right ?你确定这个是对的吗? It seems to me that you're confusing the user's view of the application with the database schema.在我看来,您将应用程序的用户视图与数据库架构混淆了。

Sure, what the user views when he's on the "property check" page is a list of tasks to perform (and a checkbox etc for each task) - but this doesn't mean the tasks belong to the property check.当然,用户在“属性检查”页面上查看的是要执行的任务列表(以及每个任务的复选框等) - 但这并不意味着任务属于属性检查。 If that was the case, the admin would have to create a new list of tasks for each property check... As fad as I understand the domain, the point is that there's a list of tasks for each property , and that the system builds a list of (not yet checked) task checks for each property check .如果是这种情况,管理员将不得不为每个属性检查创建一个新的任务列表......正如我对域的理解一样,重点是每个属性都有一个任务列表,并且系统构建每个属性检查的(尚未检查的)任务检查列表。 Which FWIW is already what you started to design.哪个 FWIW 已经是您开始设计的。

So (assuming I got the problem right), your rule is actually "each property has a list of tasks".因此(假设我的问题是正确的),您的规则实际上是“每个属性都有一个任务列表”。 Now we have the other cardinality to sort out: does a task belong to one single property, or can the same task be shared by many properties ?现在我们有另一个基数需要解决:一个任务是否属于一个单一的属性,或者同一个任务可以被多个属性共享?

We already covered the first case (cf above).我们已经介绍了第一种情况(参见上文)。 In the second case - which is actually more likely since there are certainly quite a few tasks that will be the same for most properties -, you have a many to many relationship.在第二种情况下——实际上更有可能,因为对于大多数属性肯定有很多任务是相同的——你有一个多对多的关系。 Those are materialized by a relationship table which has a fk on each side of the relationship, with a unicity constraint on the pair of fks (you don't want to have the same task listed twice for a same property).这些由关系表具体化,该表在关系的每一侧都有一个 fk,对 fk 对具有唯一性约束(您不希望为同一属性列出两次相同的任务)。 Note that with Django's ORM, you don't need to explicitely declare a model for this (unless of course you need to add some other field to the relationship, but so far I don't see a need for this here) - just declare a many2many field on any side of the relationship (doesn't really matter) and the ORM will create the intermediary table for you.请注意,使用 Django 的 ORM,您不需要为此明确声明模型(除非您当然需要向关系添加一些其他字段,但到目前为止我认为这里不需要) - 只需声明关系的任何一侧都有一个 many2many 字段(并不重要),ORM 将为您创建中间表。

Then you have a relationship between property check and task check.那么你就有了属性检查和任务检查之间的关系。 Here it's a simple one to many relationship - a property check has many task checks, a task check belongs to one single property check.这是一个简单的一对多关系——一个属性检查有许多任务检查,一个任务检查属于一个单一的属性检查。 The only constraint here is that those task checks's task must belong to the same property as the property check's property (yes, it's a bit confused when written that way xD).这里唯一的限制是那些任务检查的任务必须与属性检查的属性属于相同的属性(是的,这样写时有点混乱 xD)。 To say it more simply: the task list of a property is used as a blueprint to create the tasks check list for a property check.更简单地说:属性的任务列表用作创建属性检查的任务检查列表的蓝图。

IOW you have: IOW你有:

  • a task belongs to one or many property一项任务属于一个或多个属性
  • a property has many tasks一个属性有很多任务
  • a property has many property checks一个属性有很多属性检查
  • a property check belongs to one single property一个财产检查属于一个单一的财产
  • a task check references one single task一个任务检查引用一个单一的任务
  • a task has many task checks一个任务有很多任务检查
  • a task check's task must be one of the tasks of the task check's property check's property (duh!)任务检查的任务必须是任务检查的属性检查的属性的任务之一(废话!)

Admin must be capable to add tasks and categories;管理员必须能够添加任务和类别;

This is a requirement indeed, but it's not related to what interest us here since this is handled at code level (permissions), not at the db schema level.这确实是一个要求,但它与我们在这里感兴趣的内容无关,因为这是在代码级别(权限)而不是数据库架构级别处理的。

Tasks belong to one category;任务属于一类;

and a category can have many tasks - one to many relationship, cf above.一个类别可以有很多任务——一对多的关系,参见上面。

Property checks are made by a staff member;财产检查由一名工作人员进行;

and a staff member can do many property checks - one to many relationship, cf above.一个工作人员可以做很多财产检查——一对多的关系,参见上面。

The task list is the same to every property;任务列表对每个属性都相同;

Ah, this one is interesting.啊,这个很有意思。 If this is true, it means that you actually don't need any relationship between Task and Property.如果这是真的,则意味着您实际上不需要 Task 和 Property 之间的任何关系。

But that's still something I'd double-check with the customer - from experience, customers tend to only think of the general case when they explain the domain, then when they start testing the software a whole lot of corner cases appear out of the blue, and you suddenly realize you will have to rewrite half or more of your schema and code.但这仍然是我要与客户仔细检查的事情 - 根据经验,客户在解释域时往往只考虑一般情况,然后当他们开始测试软件时,会突然出现很多极端情况,您突然意识到您将不得不重写一半或更多的架构和代码。 I actually had the case on one of the very first application I was involved in - not as a developper actually, I was just one of the app's users, and the first thing I had to do with the app revealed such shortcomings, leading to a full month of additional development (which the company that employed me had to pay for since they had signed on the - wrong - requirements).我实际上在我参与的第一个应用程序中遇到了这种情况 - 实际上不是作为开发人员,我只是该应用程序的用户之一,而我必须对应用程序做的第一件事揭示了这些缺点,导致整整一个月的额外开发(雇用我的公司必须支付费用,因为他们签署了 - 错误的 - 要求)。 Needless to say the persons responsible for this costly mistake were either blamed or, for one, just plain fired.毋庸置疑,对这一代价高昂的错误负责的人要么受到指责,要么被直接解雇。

Every task must have a status (Ex.: completed state);每个任务都必须有一个状态(例如:已完成状态);

This one is wrong too.这个也是错的。 The status belongs to the task check, not to the task.状态属于任务检查,不属于任务。

Ok, so the models you posted are not too far off.好的,所以您发布的模型并不太远。 As I already mentionned in a comment, you have some one to many relationships wrong (fk on the wrong side of the relationship) but with the explanations above you should be able to sort this out.正如我在评论中已经提到的,你有一些一对多的关系是错误的(fk 在关系的错误方面)但是通过上面的解释你应该能够解决这个问题。 You may also want to double check some of the rules with the customer and adjust your models accordingly.您可能还想与客户仔细检查一些规则并相应地调整您的模型。

A couple other things now:现在还有一些其他事情:

First, unless you're working with a legacy database (which is obviously not the case here), you'd be better leaving the model fields db_column attribute alone - the ORM will use the model field's name as db column name, and that's most often the best default - at least you don't have to check your models.py file for column names when you want to do raw SQL queries.首先,除非您使用的是遗留数据库(这里显然不是这种情况),否则最好单独保留模型字段db_column属性 - ORM 将使用模型字段的名称作为 db 列名称,这是最重要的通常是最好的默认设置 - 至少当您想要执行原始 SQL 查询时,您不必检查 models.py 文件中的列名。 Note that for foreign keys, the model's attribute will yield the related model instance, but will create a "fieldname_id" column.请注意,对于外键,模型的属性将产生相关的模型实例,但会创建一个“fieldname_id”列。

Second point: if a textfield or charfield is not required, do NOT use "null=True" - else you'd have two possible values indicating "no data", either SQL "NULL" or an empty string.第二点:如果不需要文本字段或字符字段,请勿使用“null=True”——否则您将有两个可能的值表示“无数据”,SQL“NULL”或空字符串。 Better to only have one of them, in this case the empty string, so remove the "null=True" and use "default=''" instead.最好只有其中之一,在这种情况下是空字符串,因此删除“null=True”并使用“default=''”代替。 Also, for free text (the "notes" field for example), you may want to use a textfield instead of a charfield.此外,对于自由文本(例如“注释”字段),您可能希望使用文本字段而不是字符字段。 This avoids placing useless contraints on the maximum length (that you can bet the users WILL ask you to extend), and will also be translated by Django's ModelForms to a proper html "text" widget instead of a (single line) html "input".这避免了在最大长度上放置无用的ModelForms (您可以打赌用户会要求您扩展),并且还将被 Django 的ModelForms转换为适当的 html“文本”小部件而不是(单行)html“输入” .

Third point: "blank=False" and "null=False" are already the defaults - a field is required unless specified otherwise - so explicitely passing them for required fields only adds "code noise".第三点:“blank=False”和“null=False”已经是默认值——除非另有说明,否则需要一个字段——所以明确地将它们传递给必填字段只会增加“代码噪音”。 The most readable code is no code at all ;-)最易读的代码是根本没有代码;-)

Hope that clears up things for you, if not feel free to ask for details / explanations in a comment.希望这可以为您解决问题,如果不能随意在评论中询问详细信息/解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM