Django DB 设计 - 使用 unique_together 预订特定的唯一 URL
Django DB design - Book specific unique URLs with unique_together
我有两个具有一对多关系的模型。
一个Book
有很多Chapter
。两个模型都有一个 slug
字段。
对于 Book
,slug
列是 UNIQUE
。
对于 Chapter
,book_id
和 slug
是 UNIQUE
。
Chapter
模型还有一个字段order
。 book_id
和 order
在一起 UNIQUE
。
这样,我可以自动为书籍生成唯一的 URL,并允许不同书籍的重复 slug。
当前 models.py
:
class Book(models.Model):
# other fields
slug = models.SlugField(max_length=80)
def save(self, *args, **kwargs):
if not self.pk:
self.slug = unique_slug(self.title)
return super(Book, self).save(*args, **kwargs)
class Chapter(models.Model):
#other fields
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='chapters')
slug = models.SlugField(max_length=80)
order = models.IntegerField(null=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['book_id', 'order'], name='unique_order'),
models.UniqueConstraint(fields=['book_id', 'slug'], name='unique_slug')]
ordering = ['order']
图书应用的 urls.py
:
urlpatterns = [
# Book
path('', views.BookList.as_view(), name='book-list'),
path('create/', views.BookCreate.as_view(), name='book-create'),
path('<slug:book_slug>/', views.BookDetail.as_view(), name='book-detail'),
path('<slug:book_slug>/edit/', views.BookEdit.as_view(), name='book-edit'),
# Chapter
path('<slug:book_slug>/chapter/', views.BookDetail.as_view(), name='chapter-list'),
path('<slug:book_slug>/chapter/create/', views.ChapterCreate.as_view(), name='chapter-create'),
path('<slug:book_slug>/chapter/<slug:chapter_slug>/', views.ChapterDetail.as_view(), name='chapter-detail'),
path('<slug:book_slug>/chapter/<slug:chapter_slug>/edit/', views.ChapterEdit.as_view(), name='chapter-edit')
]
这里的缺点是,在我的 chapter
视图中,我必须先查询这本书并获得匹配 book_id
的章节 slug
。虽然之前只有章节 slug
UNIQUE,但我只是单独查询章节 table。
我的目标是拥有这样的 URL
book/rndmstrng-alice-in-the-wonderland/chapter/down-the-rabbit-hole
book/rndmstrng-some-other-book/chapter/down-the-rabbit-hole
这个设计有什么问题吗?太多 UNIQUE
约束不好吗?有没有更好的方法来实现这个?
您可以覆盖 get_object
以使用两个 slug 进行查询,并使用 select_related
在单个查询中获取两个对象
def get_object(self, queryset=None):
return get_object_or_404(Chapter.objects.filter(
book__slug=self.kwargs['book_slug'],
slug=self.kwargs['chapter_slug']
).select_related('book'))
您的 table 设计看起来不错,可以进行一些细微的改进
A SlugField
默认为 db_index=True
,因为通常查询 slug 字段,UniqueConstraint
也会在传递给它的字段上创建索引。这意味着您有两个包含 slug 字段的索引,您可以通过删除字段上的索引并更改 UniqueConstraint
的顺序将其减少到一个,以便 slug 字段位于第一个(索引中的字段很重要,您通常希望字段“更独特”或者将在没有其他字段的情况下首先被查询)
class Chapter(models.Model):
# Other fields
slug = models.SlugField(max_length=80, db_index=False)
class Meta:
constraints = [
# Other constraints
models.UniqueConstraint(fields=['slug', 'book'], name='unique_slug')
]
我有两个具有一对多关系的模型。
一个
Book
有很多Chapter
。两个模型都有一个slug
字段。对于
Book
,slug
列是UNIQUE
。对于
Chapter
,book_id
和slug
是UNIQUE
。Chapter
模型还有一个字段order
。book_id
和order
在一起UNIQUE
。
这样,我可以自动为书籍生成唯一的 URL,并允许不同书籍的重复 slug。
当前 models.py
:
class Book(models.Model):
# other fields
slug = models.SlugField(max_length=80)
def save(self, *args, **kwargs):
if not self.pk:
self.slug = unique_slug(self.title)
return super(Book, self).save(*args, **kwargs)
class Chapter(models.Model):
#other fields
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='chapters')
slug = models.SlugField(max_length=80)
order = models.IntegerField(null=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['book_id', 'order'], name='unique_order'),
models.UniqueConstraint(fields=['book_id', 'slug'], name='unique_slug')]
ordering = ['order']
图书应用的 urls.py
:
urlpatterns = [
# Book
path('', views.BookList.as_view(), name='book-list'),
path('create/', views.BookCreate.as_view(), name='book-create'),
path('<slug:book_slug>/', views.BookDetail.as_view(), name='book-detail'),
path('<slug:book_slug>/edit/', views.BookEdit.as_view(), name='book-edit'),
# Chapter
path('<slug:book_slug>/chapter/', views.BookDetail.as_view(), name='chapter-list'),
path('<slug:book_slug>/chapter/create/', views.ChapterCreate.as_view(), name='chapter-create'),
path('<slug:book_slug>/chapter/<slug:chapter_slug>/', views.ChapterDetail.as_view(), name='chapter-detail'),
path('<slug:book_slug>/chapter/<slug:chapter_slug>/edit/', views.ChapterEdit.as_view(), name='chapter-edit')
]
这里的缺点是,在我的 chapter
视图中,我必须先查询这本书并获得匹配 book_id
的章节 slug
。虽然之前只有章节 slug
UNIQUE,但我只是单独查询章节 table。
我的目标是拥有这样的 URL
book/rndmstrng-alice-in-the-wonderland/chapter/down-the-rabbit-hole
book/rndmstrng-some-other-book/chapter/down-the-rabbit-hole
这个设计有什么问题吗?太多 UNIQUE
约束不好吗?有没有更好的方法来实现这个?
您可以覆盖 get_object
以使用两个 slug 进行查询,并使用 select_related
在单个查询中获取两个对象
def get_object(self, queryset=None):
return get_object_or_404(Chapter.objects.filter(
book__slug=self.kwargs['book_slug'],
slug=self.kwargs['chapter_slug']
).select_related('book'))
您的 table 设计看起来不错,可以进行一些细微的改进
A SlugField
默认为 db_index=True
,因为通常查询 slug 字段,UniqueConstraint
也会在传递给它的字段上创建索引。这意味着您有两个包含 slug 字段的索引,您可以通过删除字段上的索引并更改 UniqueConstraint
的顺序将其减少到一个,以便 slug 字段位于第一个(索引中的字段很重要,您通常希望字段“更独特”或者将在没有其他字段的情况下首先被查询)
class Chapter(models.Model):
# Other fields
slug = models.SlugField(max_length=80, db_index=False)
class Meta:
constraints = [
# Other constraints
models.UniqueConstraint(fields=['slug', 'book'], name='unique_slug')
]