我一直在尝试构建一个我们通常在网站上看到的Tutorial系统。就像我们单击next -> next -> previous
一样阅读。
所有帖子都存储在名为Post
的表(模型)中。基本上就像一个发布对象池。
Post.objects.all()
将返回所有帖子。
现在还有另一个表(模型)
称为Tutorial
,它将存储以下内容,
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。
示例:系列将具有[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中有一种更好的方法来实现此目的。
答案 0 :(得分:2)
此系列字段中的条目是存储为列表的字符串表示形式的post_id。 (...) 因此,我只是阅读了series字段,并获得了此列表中所有具有ID的帖子,然后在Django中使用分页显示它们。
不要这样做!
您正在使用关系数据库。在关系数据库中的实体之间建立关系的一种正确方法是使用外键。在您的情况下,根据帖子是只属于一个教程(“一对多”关系)还是同时属于多个教程(“多对多”关系),您要么希望要么发布一个教程上的外键,或者将中间的“ post_tutorials”表与帖子和教程上的外键一起使用。
您的解决方案不允许数据库正常运行。它不能强制执行完整性约束(如果要删除教程引用的帖子怎么办?),不能优化读取访问权限(使用正确的模式,数据库可以在单个查询中检索教程及其所有帖子),不能遵循反向关系(给出一个帖子,访问它所属的教程)等。并且它需要一个外部程序(python代码)来与您的数据进行交互,而使用适当的建模则只需要标准的SQL。
最后-但这是django特有的-使用适当的架构与admin功能以及django rest框架(如果您打算构建rest API)更好地配合使用。
wrt /排序问题,这是一个长期存在(已解决)的问题,您只需要添加一个“ order”字段(小int就足够了)。 django的第3部分有几个应用程序为您的模型和管理员添加了对此功能的支持,因此几乎可以即插即用。
IOW,绝对没有充分的理由以这种方式对您的模式进行规范化,只有充分的理由才能使用适当的关系建模。 FWIW,我曾经不得不基于一个晦涩(希望已死的很久)的PHP cms进行一个项目,这个cms具有使用“序列化列表”反模式的绝妙想法,我可以告诉你,这既是灾难,也是性能和一个完整的噩梦要维持。因此,对自己和整个世界都有利:不要尝试发挥创造力,而是遵循众所周知的既定最佳做法,您的生活会更加幸福。我的2美分...
答案 1 :(得分: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')
这种方法的优点是它可以更灵活地通过系列访问教程,但是为此会创建一个额外的表,以及一个额外的字段来维护顺序。