Практикум — доска объявлений на Django
Зачем этот практикум
Первая программа даёт один приложение и одну модель. Справочник разбирает инструменты по отдельности. Здесь — цельный сайт: каталог объявлений с рубриками, комментариями и разграничением доступа — типичный учебный проект уровня "доска объявлений".
Перед стартом: завершите Первая программа на Django, просмотрите в Справочник по Django разделы про CBV, formsets, auth и (по желанию) PostgreSQL.
Что получится
| Функция | Реализация |
|---|---|
| Главная, навигация | Приложение main, базовый шаблон |
| Рубрики | Модель Rubric, список и фильтр объявлений |
| Объявления | Модель Listing, CRUD через CBV + формы |
| Комментарии | Связь FK, inline в админке, форма на странице объявления |
| Пользователи | django.contrib.auth, только автор правит своё |
| API (опционально) | DRF или JsonResponse из Справочник по Django |
Структура проекта
Рекомендуемое разбиение на приложения (каждое — одна зона ответственности):
board_project/
manage.py
board_project/ # пакет конфигурации
settings.py
urls.py
main/ # главная, layout, статика
catalog/ # Rubric, Listing
comments/ # Comment
accounts/ # профиль, регистрация (опционально)
templates/layout/
static/
В INSTALLED_APPS подключают main, catalog, comments, accounts и стандартные contrib.*.
Модели предметной области
Код ITЗагрузка примера кода…
# comments/models.py
class Comment(models.Model):
listing = models.ForeignKey(
'catalog.Listing',
on_delete=models.CASCADE,
related_name='comments',
)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
text = models.TextField(max_length=2000)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_at']
PROTECT у рубрики не даёт удалить рубрику, пока в ней есть объявления. get_absolute_url нужен для редиректов после сохранения и ссылок в админке.
Представления и маршруты
Именованное пространство app_name = 'catalog' в catalog/urls.py:
| URL | View | Назначение |
|---|---|---|
/ | TemplateView / главная main | вход |
/catalog/ | ListingListView | все опубликованные |
/catalog/rubric/<slug>/ | список с фильтром rubric__slug | объявления рубрики |
/catalog/<pk>/ | ListingDetailView | карточка + комментарии |
/catalog/add/ | LoginRequiredMixin + CreateView | новое объявление |
/catalog/<pk>/edit/ | UpdateView, проверка авторства | правка |
Фильтр только опубликованных записей:
class ListingListView(ListView):
model = Listing
template_name = 'catalog/listing_list.html'
context_object_name = 'listings'
paginate_by = 10
def get_queryset(self):
qs = Listing.objects.filter(published=True).select_related('rubric', 'author')
slug = self.kwargs.get('slug')
if slug:
qs = qs.filter(rubric__slug=slug)
return qs
Правка чужого объявления закрывают переопределением get_queryset в UpdateView или mixin, сравнивающим obj.author_id с request.user.id.
Комментарии на странице объявления
В DetailView в get_context_data добавляют форму комментария. POST обрабатывают в post() того же класса или отдельным FBV:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['text']
После form.save(commit=False) выставляют author=request.user и listing_id из URL. Редирект — на get_absolute_url() объявления.
Административный сайт
@admin.register(Listing)
class ListingAdmin(admin.ModelAdmin):
list_display = ('title', 'rubric', 'price', 'published', 'created_at')
list_filter = ('published', 'rubric')
list_editable = ('published',)
search_fields = ('title', 'content')
date_hierarchy = 'created_at'
autocomplete_fields = ('author',)
Для комментариев — TabularInline на странице объявления. Подробнее — справочник, §8.
Пользователи и права
createsuperuser— модерация через/admin/.- Регистрация —
django.contrib.authviews или формаUserCreationFormвaccounts. - Публикация объявления: флаг
published=Falseпо умолчанию; модератор включает в админке или автор с правомcatalog.can_publish_listing(кастомноеPermissionвMetaмодели).
Цепочка входа и сброса пароля — готовые CBV в 301, §14.
Шаблоны и статика
Базовый шаблон templates/layout/base.html с блоками title, content, навигацией по рубрикам (Rubric.objects.all() в context processor или в каждом view). Статика — {% load static %}, в продакшене — collectstatic.
Оформление через Bootstrap (django-bootstrap5 и аналоги) опционально: подключают в INSTALLED_APPS и наследуют их теги в layout.
База данных
Для разработки достаточно SQLite. Для полнотекстового поиска и сложных ограничений — PostgreSQL (см. 301, PostgreSQL). В settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('PGDATABASE', 'board'),
'USER': os.environ.get('PGUSER', 'board'),
'PASSWORD': os.environ.get('PGPASSWORD', ''),
'HOST': os.environ.get('PGHOST', 'localhost'),
'PORT': os.environ.get('PGPORT', '5432'),
}
}
REST для того же проекта
После HTML-версии те же модели подключают к DRF — ListingSerializer, ListingViewSet, router на /api/listings/. Отдельный фронтенд (React, Angular) ходит на API; сессионная auth или JWT — в Первая программа на Django REST Framework.
Минимальный вариант без DRF описан в Первая программа на Django REST Framework.
Порядок внедрения
- Проект и приложения,
migrate, суперпользователь. - Модели
Rubric,Listing, миграции, фикстуры рубрик (fixturesилиloaddata). - Админка, несколько тестовых объявлений.
ListView/DetailView, шаблоны, пагинация.CreateView/UpdateViewсLoginRequiredMixin.- Комментарии и форма на детальной странице.
- Фильтр по рубрике, главная с навигацией.
- Тесты: список 200, создание только для авторизованных.
- (Опционально) DRF или деплой — Django, 301 §16.