[英]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.