【问题标题】:Do Django models really need a single unique key fieldDjango 模型真的需要一个唯一的关键字段吗
【发布时间】:2014-01-16 22:16:06
【问题描述】:

我的一些models 仅在keys 的组合中是唯一的。我不想使用auto-numberingid作为标识符,因为数据的子集将被导出到其他系统(例如spreadsheets),修改然后用于更新master database

这是一个例子:

class Statement(models.Model):
    supplier = models.ForeignKey(Supplier)  
    total = models.DecimalField("statement total", max_digits=10, decimal_places=2)
    statement_date = models.DateField("statement date")
    ....


class Invoice(models.Model):
    supplier = models.ForeignKey(Supplier)
    amount = models.DecimalField("invoice total", max_digits=10, decimal_places=2)
    invoice_date = models.DateField("date of invoice")
    statement = models.ForeignKey(Statement, blank=True, null=True)
    ....

Invoice 记录仅对 supplieramountinvoice_date 的组合是唯一的

我想知道是否应该基于supplieramountinvoice_dateInvoice 创建一个slug,以便轻松识别正确的记录。

有多个related fields 来识别正确记录的问题的一个例子是django-csvimport,它假设只有一个相关字段并且在构建foreign key links 时不会区分两个。

然而,slug 似乎是一个笨拙的选项,在批量添加 records 后需要某种管理来重建 slugs

我认为这一定是一个常见问题,也许某处存在最佳实践设计模式。

我正在使用PostgreSQL,以防有人有数据库解决方案。虽然如果可能的话我宁愿避免这种情况,但我可以看到它可能是构建我的slug 的方式,如果这是要走的路,也许是trigger functions。不过,这感觉有点像隐藏功能,并且可能会导致在不同的服务器上进行设置时令人头疼。

更新 - 阅读初始回复后

我的应用程序要求数据可以导出、远程修改,并在审核和批准后合并回主数据库。隐藏的自动编号键不容易始终如一地存活下来。如果statement 表被清空并从CSV 重新加载,则关系invoices[2417] is part of statements[265] 不是持久的。

如果我使用数字自动编号 pk,那么任何更新 database 的进程都需要刷新相关的键编号或使用多个 WITH 子句。

如果我创建了一个基于我的 3 个键但易于复制的 slug,那么我可以将它用作键 - 尽管很笨拙。我在想一个蛞蝓:

u'%s %s %s' % (self.supplier, 
    self.statement_date.strftime("%Y-%m-%d"), 
    self.total)

这似乎很笨拙,而且不是很 DRY,因为我希望我可能不得不在其他地方重新创建 slug 以复制算法(可能在 Excel 公式或 Access 查询中)

我认为一定有更好的方法我错过了,但看起来 yuvi 的回复意味着应该有,并且会有,但还没有:-(

【问题讨论】:

    标签: django django-models foreign-keys


    【解决方案1】:

    您所说的是多列主键,也称为"composite" or "compound" keys。今天在 django 中对复合键的支持仍在进行中,您可以阅读它here

    目前 Django 模型仅支持该集合中的单个列, 否认许多表的自然主键是 多列[...]当前状态是问题是 已接受/分配并正在处理 [...]

    该链接还提到了一个部分实现,即django-compositekeys。这只是部分的,会导致您在关系之间导航时遇到麻烦:

    ForeignKey 中缺少对复合键的支持,并且 相关经理。因此,无法导航 来自具有复合主键的模型的关系。

    所以目前不完全支持,但将来会支持。关于您自己的项目,您可以按照自己的意愿去做,尽管我自己的建议是坚持完全支持的默认隐藏自动增量字段,您甚至不需要考虑(并使用unique_together强制所描述字段的唯一性,而不是使它们成为您的主键)。

    我希望这会有所帮助!

    【讨论】:

    • 我试图传达的是,django 不会“验证”这种关系——所有这些都发生在数据库中,而您在模型中描述的只是为了让模型“工作”而不是为了优化真实的查询。您如何描述关系以及它们的工作速度仍然仅取决于您如何创建表以及如何优化索引。 Django 层仅根据从数据库读取的数据创建对象。
    • 谢谢。结论令人失望,但至少我没有错过明显的东西。链接的文章实际上表达了同样的困境,并提出了人工hash 的选项,并带来了相应的缺点。
    • 不要失望,我要表达的观点类似于@OdifYltsaeb - 你应该始终prefer surrogate (meaningless) keys over natural (meaningful) ones
    【解决方案2】:

    没有。

    模型需要有一个字段primary_key = True。默认情况下,这是存储对象 ID 的(隐藏)自动字段。但是您可以在任何其他字段中将 primary_key 设置为 True。 我已经这样做了,我在以前手动或通过其他一些框架/系统创建的表上创建 django 项目。

    实际上 - 您可以使用任何您能想到的方式在查询中将对象连接在一起。只要查询返回一堆可以与您拥有的模型相关联的数据 - 您使用哪个字段进行连接并不重要。请记住,您使用的解决方案应该尽可能有效。

    艾伦

    【讨论】:

    • 但在这种情况下,我无法在Statement 上设置单个主键,这意味着识别正确的记录涉及具有多个条件的查询WHERE supplier=test.supplier AND total=test.total AND statement_date = test.statement_date。这没关系,我可以在其中放入 SQL,但是许多 Django 包假定外键将指向具有可用主键的对象,所以很多可用的自动化都丢失了。例如,我正在使用django-restframework,但我看不到如何使用幕后魔法更新Invoice,并附上指向正确Statement 的链接。
    • 我认为我需要在 Statement 上使用一个唯一键,但它必须是一个持久且仍然有效的键,如果应将记录替换为具有相同可见字段的新副本,但autonumber id 的不同值。因此,我感到被驱使到结合了我的三个识别字段值的slug
    • 嗯,指向其他对象/表的主键是理解对象/表之间连接的最简单方法。我很难创建考虑到模型之间所有可能关系的第 3 方项目。我知道问题出在 csvimport 项目上,这使您很难使用关系,这对您来说最有意义。在这种情况下,放弃项目并使用 python 自己的 csv 模块。
    • 我也在使用django-restframework,我认为任何 REST 接口也需要一个键来识别资源中的项目
    猜你喜欢
    • 2012-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-29
    • 2021-09-30
    • 2012-04-15
    • 2021-02-20
    • 2012-03-12
    相关资源
    最近更新 更多