Add Django backend, Next.js frontend, and deploy templates
This commit is contained in:
1 parent
fc0c991335
commit
e9cd22d36f
93 files changed
+7840
-1
No files matched your search
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import ContactMessage, Doctor, Document, Page, Service, SiteSettings
|
||||
|
||||
|
||||
@admin.register(SiteSettings)
|
||||
class SiteSettingsAdmin(admin.ModelAdmin):
|
||||
list_display = ("site_name", "phone", "city", "updated_at")
|
||||
|
||||
|
||||
@admin.register(Page)
|
||||
class PageAdmin(admin.ModelAdmin):
|
||||
list_display = ("title", "slug", "is_published", "nav_order", "updated_at")
|
||||
list_editable = ("is_published", "nav_order")
|
||||
prepopulated_fields = {"slug": ("title",)}
|
||||
|
||||
|
||||
@admin.register(Service)
|
||||
class ServiceAdmin(admin.ModelAdmin):
|
||||
list_display = ("title", "slug", "sort_order", "updated_at")
|
||||
list_editable = ("sort_order",)
|
||||
prepopulated_fields = {"slug": ("title",)}
|
||||
|
||||
|
||||
@admin.register(Doctor)
|
||||
class DoctorAdmin(admin.ModelAdmin):
|
||||
list_display = ("full_name", "position", "sort_order", "updated_at")
|
||||
list_editable = ("sort_order",)
|
||||
|
||||
|
||||
@admin.register(Document)
|
||||
class DocumentAdmin(admin.ModelAdmin):
|
||||
list_display = ("title", "category", "sort_order", "updated_at")
|
||||
list_editable = ("sort_order",)
|
||||
|
||||
|
||||
@admin.register(ContactMessage)
|
||||
class ContactMessageAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "phone", "email", "is_processed", "created_at")
|
||||
list_editable = ("is_processed",)
|
||||
readonly_fields = ("name", "phone", "email", "message", "created_at", "updated_at")
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ContentConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "content"
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
[
|
||||
{
|
||||
"model": "content.sitesettings",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"site_name": "Консультационное клиническое патологоанатомическое бюро",
|
||||
"tagline": "Точная морфологическая диагностика и профессиональное сопровождение",
|
||||
"phone": "+7 (8652) 22-66-77",
|
||||
"email": "kkpab@bk.ru",
|
||||
"address": "улица Балакирева, 5к7",
|
||||
"city": "Ставрополь",
|
||||
"working_hours": "Пн-Пт 08:00-17:00, Сб 09:00-14:00",
|
||||
"hero_title": "Современная патоморфология с вниманием к клинической точности",
|
||||
"hero_subtitle": "Переосмысленный цифровой сайт для бюро, где ключевая информация, специалисты, документы и направления на исследования доступны быстро и понятно.",
|
||||
"hero_image": "https://images.unsplash.com/photo-1579154204601-01588f351e67?auto=format&fit=crop&w=1600&q=80",
|
||||
"about_excerpt": "Бюро объединяет специалистов по патологической анатомии и гистологии, обеспечивая качественную диагностику и профессиональную коммуникацию с медицинскими организациями."
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.page",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"title": "О бюро",
|
||||
"slug": "about",
|
||||
"intro": "Специалисты разного уровня и современное оборудование для морфологической диагностики.",
|
||||
"body": "В патологоанатомическом бюро работают врачи с большим стажем, квалификационными категориями и постоянным профессиональным развитием. Для гистологической обработки используется современное высокотехнологичное оборудование, а диагностический процесс поддерживается подготовленным средним и младшим медицинским персоналом.",
|
||||
"meta_title": "О бюро",
|
||||
"meta_description": "Информация о консультационном клиническом патологоанатомическом бюро.",
|
||||
"is_published": true,
|
||||
"nav_order": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.page",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"title": "Сведения о медицинской организации",
|
||||
"slug": "svedeniya-mo",
|
||||
"intro": "Юридическая информация, лицензии, график и данные страховых организаций.",
|
||||
"body": "Полное наименование: ООО \"Консультационное Клиническое Патологоанатомическое Бюро\". ОГРН 1182651002093. Режим работы: пн-пт с 8:00 до 17:00, суббота с 9:00 до 14:00. Генеральный директор: Балабеков Андрей Вагифович. Телефон: +7 (8652) 22-66-77. Электронная почта: kkpab@bk.ru.",
|
||||
"meta_title": "Сведения МО",
|
||||
"meta_description": "Официальные сведения о медицинской организации.",
|
||||
"is_published": true,
|
||||
"nav_order": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"title": "Гистологические исследования",
|
||||
"slug": "histology",
|
||||
"short_description": "Базовый и наиболее распространенный метод исследования тканей.",
|
||||
"description": "Гистологическое исследование используется для диагностики опухолевых и неопухолевых заболеваний. Материал проходит фиксацию, гистопроцессинг, заливку в парафин, микротомию, окрашивание и оценку врачом-патоморфологом.",
|
||||
"icon": "microscope",
|
||||
"sort_order": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"title": "Консультация морфологических препаратов",
|
||||
"slug": "consultations",
|
||||
"short_description": "Профессиональная верификация и экспертная оценка сложных случаев.",
|
||||
"description": "Консультация препаратов помогает уточнить диагноз, подтвердить сложные клинические случаи и обеспечить единый профессиональный стандарт интерпретации.",
|
||||
"icon": "stethoscope",
|
||||
"sort_order": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"title": "Документационное сопровождение",
|
||||
"slug": "documents-support",
|
||||
"short_description": "Подготовка обязательной информации и доступ к официальным документам.",
|
||||
"description": "Пациенты и партнеры получают быстрый доступ к сведениям о медицинской организации, лицензии, графику работы и контактам.",
|
||||
"icon": "file-text",
|
||||
"sort_order": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"title": "Исследование биопсийно-операционного материала",
|
||||
"slug": "biopsy-operative-material",
|
||||
"short_description": "Исследование биопсийно-операционного материала органов и тканей при воспалительных процессах, дистрофиях, патологии артерий/вен, пороках развития.",
|
||||
"description": "Исследование биопсийно-операционного материала органов и тканей при воспалительных процессах, дистрофиях, патологии артерий/вен, пороках развития.",
|
||||
"icon": "microscope",
|
||||
"sort_order": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"title": "Исследование операционного материала",
|
||||
"slug": "operative-material",
|
||||
"short_description": "Исследование операционного материала органов и тканей при инфекционных заболеваниях, опухолях ясного гистогенеза без дисплазии, гранулематозном воспалении.",
|
||||
"description": "Исследование операционного материала органов и тканей при инфекционных заболеваниях, опухолях ясного гистогенеза без дисплазии, гранулематозном воспалении.",
|
||||
"icon": "file-text",
|
||||
"sort_order": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"title": "Исследование диагностических биопсий",
|
||||
"slug": "diagnostic-biopsies",
|
||||
"short_description": "Исследование диагностических биопсий (кроме пункционных) органов и тканей при воспалении, дисфункции, дисплазии, опухолях, требующих уточнения гистогенеза, степени инвазии, стадии прогрессирования.",
|
||||
"description": "Исследование диагностических биопсий (кроме пункционных) органов и тканей при воспалении, дисфункции, дисплазии, опухолях, требующих уточнения гистогенеза, степени инвазии, стадии прогрессирования.",
|
||||
"icon": "stethoscope",
|
||||
"sort_order": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"title": "Исследование диагностических пункционных биопсий",
|
||||
"slug": "diagnostic-puncture-biopsies",
|
||||
"short_description": "Исследование диагностических пункционных биопсий различных органов и тканей, биопсий и операционный материал при иммунопатологических процессах, опухолях и опухолеподобных поражениях.",
|
||||
"description": "Исследование диагностических пункционных биопсий различных органов и тканей, биопсий и операционный материал при иммунопатологических процессах, опухолях и опухолеподобных поражениях.",
|
||||
"icon": "microscope",
|
||||
"sort_order": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"title": "Экспресс-исследование диагностических биопсий",
|
||||
"slug": "express-diagnostic-biopsies",
|
||||
"short_description": "Экспресс-исследование диагностических биопсий (кроме пункционных) органов и тканей при воспалении, дисфункции, дисплазии, опухолях, требующих уточнения гистогенеза, степени инвазии, стадии прогрессирования.",
|
||||
"description": "Экспресс-исследование диагностических биопсий (кроме пункционных) органов и тканей при воспалении, дисфункции, дисплазии, опухолях, требующих уточнения гистогенеза, степени инвазии, стадии прогрессирования.",
|
||||
"icon": "stethoscope",
|
||||
"sort_order": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"title": "Исследование костно-хрящевых тканей",
|
||||
"slug": "bone-cartilage-tissues",
|
||||
"short_description": "Исследование костно-хрящевых тканей, включая декальцинацию операционного материала.",
|
||||
"description": "Исследование костно-хрящевых тканей, включая декальцинацию операционного материала.",
|
||||
"icon": "file-text",
|
||||
"sort_order": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.service",
|
||||
"pk": 10,
|
||||
"fields": {
|
||||
"title": "Гистологическое исследование последов",
|
||||
"slug": "placenta-histology",
|
||||
"short_description": "Гистологическое исследование последов с целью диагностики внутриутробного инфицирования.",
|
||||
"description": "Гистологическое исследование последов с целью диагностики внутриутробного инфицирования.",
|
||||
"icon": "microscope",
|
||||
"sort_order": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.doctor",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"full_name": "Чуков Сергей Залимович",
|
||||
"position": "Заведующий отделением, врач-патологоанатом",
|
||||
"credentials": "Сертификат специалиста 2514 от 24.12.2018",
|
||||
"bio": "Руководит отделением и участвует в диагностике сложных клинических случаев.",
|
||||
"photo": "",
|
||||
"sort_order": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.doctor",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"full_name": "Балабеков Андрей Вагифович",
|
||||
"position": "Врач-патологоанатом, генеральный директор",
|
||||
"credentials": "Сертификат специалиста 307 от 07.03.2019",
|
||||
"bio": "Координирует работу бюро и участвует в развитии диагностической службы.",
|
||||
"photo": "",
|
||||
"sort_order": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.doctor",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"full_name": "Громова Юлия Владимировна",
|
||||
"position": "Врач-патологоанатом",
|
||||
"credentials": "Сертификат ВСБ № 0947855 от 30.09.2006",
|
||||
"bio": "Работает с морфологической диагностикой и сопровождением исследований.",
|
||||
"photo": "",
|
||||
"sort_order": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.doctor",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"full_name": "Чеккуева Зульфия Алиевна",
|
||||
"position": "Врач-патологоанатом",
|
||||
"credentials": "Сертификат специалиста 530 от 21.04.2021 г. Диплом об окончании ординатуры 1163 от 31.08.2020 г.",
|
||||
"bio": "Врач-патологоанатом.",
|
||||
"photo": "",
|
||||
"sort_order": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.document",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"title": "Лицензия на осуществление медицинской деятельности",
|
||||
"category": "Лицензии",
|
||||
"description": "Лицензия № ЛО-26-01-005362 от 20 марта 2020 года.",
|
||||
"file_url": "",
|
||||
"sort_order": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "content.document",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"title": "Территориальная программа ОМС СК",
|
||||
"category": "Документы",
|
||||
"description": "Обязательная информация для пациентов и партнеров.",
|
||||
"file_url": "",
|
||||
"sort_order": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,129 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ContactMessage",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("name", models.CharField(max_length=255)),
|
||||
("phone", models.CharField(blank=True, max_length=64)),
|
||||
("email", models.EmailField(blank=True, max_length=254)),
|
||||
("message", models.TextField()),
|
||||
("is_processed", models.BooleanField(default=False)),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Сообщение",
|
||||
"verbose_name_plural": "Сообщения",
|
||||
"ordering": ["-created_at"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Doctor",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("full_name", models.CharField(max_length=255)),
|
||||
("position", models.CharField(max_length=255)),
|
||||
("credentials", models.TextField(blank=True)),
|
||||
("bio", models.TextField(blank=True)),
|
||||
("photo", models.URLField(blank=True)),
|
||||
("sort_order", models.PositiveIntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Специалист",
|
||||
"verbose_name_plural": "Специалисты",
|
||||
"ordering": ["sort_order", "full_name"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Document",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("title", models.CharField(max_length=255)),
|
||||
("category", models.CharField(blank=True, max_length=120)),
|
||||
("description", models.TextField(blank=True)),
|
||||
("file_url", models.URLField(blank=True)),
|
||||
("sort_order", models.PositiveIntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Документ",
|
||||
"verbose_name_plural": "Документы",
|
||||
"ordering": ["sort_order", "title"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Page",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("title", models.CharField(max_length=255)),
|
||||
("slug", models.SlugField(unique=True)),
|
||||
("intro", models.TextField(blank=True)),
|
||||
("body", models.TextField()),
|
||||
("meta_title", models.CharField(blank=True, max_length=255)),
|
||||
("meta_description", models.CharField(blank=True, max_length=320)),
|
||||
("is_published", models.BooleanField(default=True)),
|
||||
("nav_order", models.PositiveIntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Страница",
|
||||
"verbose_name_plural": "Страницы",
|
||||
"ordering": ["nav_order", "title"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Service",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("title", models.CharField(max_length=255)),
|
||||
("slug", models.SlugField(unique=True)),
|
||||
("short_description", models.TextField()),
|
||||
("description", models.TextField()),
|
||||
("icon", models.CharField(blank=True, max_length=64)),
|
||||
("sort_order", models.PositiveIntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Услуга",
|
||||
"verbose_name_plural": "Услуги",
|
||||
"ordering": ["sort_order", "title"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="SiteSettings",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("site_name", models.CharField(max_length=255)),
|
||||
("tagline", models.CharField(blank=True, max_length=255)),
|
||||
("phone", models.CharField(max_length=64)),
|
||||
("email", models.EmailField(blank=True, max_length=254)),
|
||||
("address", models.CharField(max_length=255)),
|
||||
("city", models.CharField(default="Ставрополь", max_length=120)),
|
||||
("working_hours", models.CharField(blank=True, max_length=255)),
|
||||
("hero_title", models.CharField(max_length=255)),
|
||||
("hero_subtitle", models.TextField()),
|
||||
("hero_image", models.URLField(blank=True)),
|
||||
("about_excerpt", models.TextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Настройки сайта",
|
||||
"verbose_name_plural": "Настройки сайта",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class TimestampedModel(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class SiteSettings(TimestampedModel):
|
||||
site_name = models.CharField(max_length=255)
|
||||
tagline = models.CharField(max_length=255, blank=True)
|
||||
phone = models.CharField(max_length=64)
|
||||
email = models.EmailField(blank=True)
|
||||
address = models.CharField(max_length=255)
|
||||
city = models.CharField(max_length=120, default="Ставрополь")
|
||||
working_hours = models.CharField(max_length=255, blank=True)
|
||||
hero_title = models.CharField(max_length=255)
|
||||
hero_subtitle = models.TextField()
|
||||
hero_image = models.URLField(blank=True)
|
||||
about_excerpt = models.TextField(blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Настройки сайта"
|
||||
verbose_name_plural = "Настройки сайта"
|
||||
|
||||
def __str__(self):
|
||||
return self.site_name
|
||||
|
||||
|
||||
class Page(TimestampedModel):
|
||||
title = models.CharField(max_length=255)
|
||||
slug = models.SlugField(unique=True)
|
||||
intro = models.TextField(blank=True)
|
||||
body = models.TextField()
|
||||
meta_title = models.CharField(max_length=255, blank=True)
|
||||
meta_description = models.CharField(max_length=320, blank=True)
|
||||
is_published = models.BooleanField(default=True)
|
||||
nav_order = models.PositiveIntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ["nav_order", "title"]
|
||||
verbose_name = "Страница"
|
||||
verbose_name_plural = "Страницы"
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Service(TimestampedModel):
|
||||
title = models.CharField(max_length=255)
|
||||
slug = models.SlugField(unique=True)
|
||||
short_description = models.TextField()
|
||||
description = models.TextField()
|
||||
icon = models.CharField(max_length=64, blank=True)
|
||||
sort_order = models.PositiveIntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ["sort_order", "title"]
|
||||
verbose_name = "Услуга"
|
||||
verbose_name_plural = "Услуги"
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Doctor(TimestampedModel):
|
||||
full_name = models.CharField(max_length=255)
|
||||
position = models.CharField(max_length=255)
|
||||
credentials = models.TextField(blank=True)
|
||||
bio = models.TextField(blank=True)
|
||||
photo = models.URLField(blank=True)
|
||||
sort_order = models.PositiveIntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ["sort_order", "full_name"]
|
||||
verbose_name = "Специалист"
|
||||
verbose_name_plural = "Специалисты"
|
||||
|
||||
def __str__(self):
|
||||
return self.full_name
|
||||
|
||||
|
||||
class Document(TimestampedModel):
|
||||
title = models.CharField(max_length=255)
|
||||
category = models.CharField(max_length=120, blank=True)
|
||||
description = models.TextField(blank=True)
|
||||
file_url = models.URLField(blank=True)
|
||||
sort_order = models.PositiveIntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ["sort_order", "title"]
|
||||
verbose_name = "Документ"
|
||||
verbose_name_plural = "Документы"
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class ContactMessage(TimestampedModel):
|
||||
name = models.CharField(max_length=255)
|
||||
phone = models.CharField(max_length=64, blank=True)
|
||||
email = models.EmailField(blank=True)
|
||||
message = models.TextField()
|
||||
is_processed = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
verbose_name = "Сообщение"
|
||||
verbose_name_plural = "Сообщения"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.created_at:%d.%m.%Y})"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import ContactMessage, Doctor, Document, Page, Service, SiteSettings
|
||||
|
||||
|
||||
class SiteSettingsSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = SiteSettings
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class PageSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Page
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class ServiceSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Service
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class DoctorSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Doctor
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class DocumentSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Document
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class ContactMessageSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ContactMessage
|
||||
fields = ("id", "name", "phone", "email", "message", "created_at")
|
||||
read_only_fields = ("id", "created_at")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import (
|
||||
ContactMessageCreateAPIView,
|
||||
DoctorListAPIView,
|
||||
DocumentListAPIView,
|
||||
HomeAPIView,
|
||||
PageDetailAPIView,
|
||||
PageListAPIView,
|
||||
ServiceDetailAPIView,
|
||||
ServiceListAPIView,
|
||||
SiteSettingsAPIView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("home/", HomeAPIView.as_view(), name="home"),
|
||||
path("site/", SiteSettingsAPIView.as_view(), name="site-settings"),
|
||||
path("pages/", PageListAPIView.as_view(), name="page-list"),
|
||||
path("pages/<slug:slug>/", PageDetailAPIView.as_view(), name="page-detail"),
|
||||
path("services/", ServiceListAPIView.as_view(), name="service-list"),
|
||||
path("services/<slug:slug>/", ServiceDetailAPIView.as_view(), name="service-detail"),
|
||||
path("doctors/", DoctorListAPIView.as_view(), name="doctor-list"),
|
||||
path("documents/", DocumentListAPIView.as_view(), name="document-list"),
|
||||
path("contact/", ContactMessageCreateAPIView.as_view(), name="contact-create"),
|
||||
]
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
from rest_framework import generics
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from .models import ContactMessage, Doctor, Document, Page, Service, SiteSettings
|
||||
from .serializers import (
|
||||
ContactMessageSerializer,
|
||||
DoctorSerializer,
|
||||
DocumentSerializer,
|
||||
PageSerializer,
|
||||
ServiceSerializer,
|
||||
SiteSettingsSerializer,
|
||||
)
|
||||
|
||||
|
||||
class HomeAPIView(APIView):
|
||||
def get(self, request):
|
||||
settings_obj = SiteSettings.objects.order_by("id").first()
|
||||
data = {
|
||||
"site": SiteSettingsSerializer(settings_obj).data if settings_obj else None,
|
||||
"pages": PageSerializer(Page.objects.filter(is_published=True)[:6], many=True).data,
|
||||
"services": ServiceSerializer(Service.objects.all()[:6], many=True).data,
|
||||
"doctors": DoctorSerializer(Doctor.objects.all(), many=True).data,
|
||||
"documents": DocumentSerializer(Document.objects.all()[:4], many=True).data,
|
||||
}
|
||||
return Response(data)
|
||||
|
||||
|
||||
class SiteSettingsAPIView(generics.RetrieveAPIView):
|
||||
serializer_class = SiteSettingsSerializer
|
||||
|
||||
def get_object(self):
|
||||
return SiteSettings.objects.order_by("id").first()
|
||||
|
||||
|
||||
class PageListAPIView(generics.ListAPIView):
|
||||
serializer_class = PageSerializer
|
||||
queryset = Page.objects.filter(is_published=True)
|
||||
|
||||
|
||||
class PageDetailAPIView(generics.RetrieveAPIView):
|
||||
serializer_class = PageSerializer
|
||||
lookup_field = "slug"
|
||||
queryset = Page.objects.filter(is_published=True)
|
||||
|
||||
|
||||
class ServiceListAPIView(generics.ListAPIView):
|
||||
serializer_class = ServiceSerializer
|
||||
queryset = Service.objects.all()
|
||||
|
||||
|
||||
class ServiceDetailAPIView(generics.RetrieveAPIView):
|
||||
serializer_class = ServiceSerializer
|
||||
lookup_field = "slug"
|
||||
queryset = Service.objects.all()
|
||||
|
||||
|
||||
class DoctorListAPIView(generics.ListAPIView):
|
||||
serializer_class = DoctorSerializer
|
||||
queryset = Doctor.objects.all()
|
||||
|
||||
|
||||
class DocumentListAPIView(generics.ListAPIView):
|
||||
serializer_class = DocumentSerializer
|
||||
queryset = Document.objects.all()
|
||||
|
||||
|
||||
class ContactMessageCreateAPIView(generics.CreateAPIView):
|
||||
serializer_class = ContactMessageSerializer
|
||||
queryset = ContactMessage.objects.all()
|
||||
Reference in new issue
Block a user