繁体   English   中英

将多个值存储到mysql数据库的单个字段中,以保留Django中的顺序

[英]Storing multiple values into a single field in mysql database that preserve order in Django

我一直在尝试构建通常在网站上看到的Tutorial系统。 就像我们单击next -> next -> previous等阅读的内容一样。

所有帖子都存储在名为Post的表(模型)中。 基本上就像一个发布对象池。

Post.objects.all()将返回所有帖子。

现在还有一个名为Tutorial Table(模型),它将存储以下内容,

class Tutorial(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    tutorial_heading = models.CharField(max_length=100)
    tutorial_summary = models.CharField(max_length=300)

    series = models.CharField(max_length=40) # <---- Here [10,11,12] 
    ...

在这里,此series字段中的条目是作为列表的字符串表示形式存储的post_id。

例如:series将具有[10,11,12] ,其中10、11和12是post_id,分别对应于Post表中的相应条目。

因此,我针对Tutorial模型的表条目如下所示。

id       heading                   summary                      series

"5"     "Series 3 Tutorial"    "lorem on ullt consequat."    "[12, 13, 14]"

因此,我只是阅读了series字段,并获得了此列表中所有具有ids的帖子,然后在Django中使用pagination显示它们。

现在 ,我从几个stackoverflow帖子中了解到,在一个字段中包含多个条目是一个坏主意。 使这种关系跨越多个表作为映射是一个更好的选择。

我想要拥有的功能是可以在我想要的任何地方向该系列插入新帖子。 也许在前面或中间。 通过将此系列作为列表并按我的意愿插入,可以轻松实现。 更改"[14,12,13]"将对正在显示的帖子进行重新排序。

我的问题是,这种在我的用例中在字段中存储多个值的方法可以吗? 还是会影响性能,或者通常是一个坏主意。 如果没有,那么我可以通过使用另一个表扩展关系来保留或更改顺序,或者在Django或MYSQL中有一种更好的方法来完成此操作。

在这里,此系列字段中的条目是作为列表的字符串表示形式存储的post_id。 (...)因此,我只是阅读了series字段,并获得了此列表中所有具有ID的帖子,然后在Django中使用分页显示它们。

不要这样做!

您正在使用关系数据库。 在关系数据库中的实体之间建立关系的一种正确方法是使用外键。 在您的情况下,根据帖子是只属于一个教程(“一对多”关系)还是同时属于多个教程(“多对多”关系),您要么希望要么发布一个教程上的外键,或者在帖子和教程上使用带有外键的中间“ post_tutorials”表。

您的解决方案不允许数据库正常工作。 它不能强制执行完整性约束(如果要删除教程引用的帖子怎么办?),不能优化读取访问权限(使用正确的模式,数据库可以在单个查询中检索教程及其所有帖子),不能遵循反向关系(给出一个帖子,访问它所属的教程)等。并且它需要一个外部程序(python代码)来与您的数据进行交互,而使用适当的建模则只需要标准SQL。

最后-但这是特定于Django的-使用适当的架构与admin功能以及django rest框架(如果您打算构建rest API)一起使用时效果更好。

wrt /排序问题,这是一个长期存在(已解决)的问题,您只需要添加一个“ order”字段(小int就足够了)。 有几个第3部分django应用程序为您的模型和管理员添加了对此功能的支持,因此几乎可以即插即用。

IOW,绝对没有充分的理由以这种方式对您的模式进行规范化,只有充分的理由才能使用适当的关系建模。 FWIW,我曾经不得不基于一个晦涩(希望已死的很久)的PHP cms进行一个项目,这个cms具有使用“序列化列表”反模式的绝妙想法,我可以告诉你,这既是灾难,也是性能和一个完整的噩梦要维持。 因此,对自己和整个世界都有利:不要尝试发挥创造力,而是遵循众所周知的既定最佳做法,您的生活会更加幸福。 我的2分钱

我可以想到两种方法:

方法一:链表

一种方法是使用像这样的链表:

class Tutorial(models.Model):
    ...
    previous = models.OneToOneField('self', null=True, blank=True, related_name="next")

通过这种方法,您可以像这样访问该系列的上一篇文章:

for tutorial in Tutorial.objects.filter(previous__isnull=True):
   print(tutorial)
   while(tutorial.next_post):
      print(tutorial.next)
      tutorial = tutorial.next

这是一种复杂的方法,例如,每当您要在链表中间添加新教程时,都需要在两个位置进行更改。 喜欢:

post = Tutorial.object.first()
next_post = post.next
new = Tutorial.objects.create(...)
post.next=new
post.save()
new.next = next_post
new.save()

但是这种方法有很大的好处,您不必为创建系列而创建新表。 另外,教程中的顺序可能不会经常修改,这意味着您不必太麻烦。

方法二:创建新模型

您可以简单地创建一个新模型并将FK转换为Tutorial,如下所示:

class Series(models.Model):
    name = models.CharField(max_length=255)


class Tutorial(models.Model):
   ..
   series = models.ForeignKey(Series, null=True, blank=True, related_name='tutorials')
   order = models.IntegerField(default=0)


   class Meta:
      unique_together=('series', 'order')  # it will make sure that duplicate order for same series does not happen

然后,您可以通过以下方式访问系列教程:

series = Series.object.first()
series.tutorials.all().order_by('tutorials__order')

这种方法的优点是它可以更灵活地访问系列教程,但是为此会创建一个额外的表,以及一个额外的字段来维护顺序。

暂无
暂无

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

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