Django Дизайн БД - Спецификация книги c уникальные URL-адреса с unique_toght - PullRequest
1 голос
/ 21 июня 2020

У меня две модели с отношением «один ко многим».

  • A Book имеет много Chapter s. Обе модели имеют поле slug.

  • Для Book столбец slug равен UNIQUE.

  • Для Chapter book_id и slug составляют UNIQUE вместе.

  • Модель Chapter также имеет поле order. И book_id и order составляют UNIQUE вместе.

Таким образом, я могу автоматически генерировать уникальные URL-адреса для книг и разрешать дублирование ярлыков для разных книг.

Текущее 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 просмотров Я должен сначала запросить книгу и получить главу slug с совпадающим book_id. Раньше с главой slug UNIQUE я просто запрашивал только таблицу глав.

Моя цель - иметь такие URL-адреса

  1. book/rndmstrng-alice-in-the-wonderland/chapter/down-the-rabbit-hole

  2. book/rndmstrng-some-other-book/chapter/down-the-rabbit-hole

Есть ли проблема в этом дизайне? Слишком много ограничений UNIQUE - плохо? Есть ли лучший способ реализовать это?

1 Ответ

1 голос
/ 21 июня 2020

Вы можете переопределить get_object для запроса с использованием обоих слагов и использовать 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'))

Дизайн вашей таблицы выглядит нормально, есть несколько небольших улучшений, которые можно сделать

A SlugField по умолчанию db_index=True, поскольку обычно запрашивается поле slug, UniqueConstraint также создаст индекс для полей, которые ему передаются. Это означает, что у вас есть два индекса, которые включают поле заголовка, вы можете уменьшить его до одного, удалив индекс в поле и изменив порядок UniqueConstraint так, чтобы поле заголовка было первым (порядок полей в индекс имеет значение, обычно вы хотите, чтобы поля были «более уникальными» или сначала запрашивались без других полей)

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')
        ]
...