mod: убрать bTypograf и обновить TbContent
This commit is contained in:
@@ -101,7 +101,7 @@ class AdminContentForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TbContent
|
model = TbContent
|
||||||
exclude = ('bTypograf',)
|
fields = '__all__'
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
@@ -110,7 +110,6 @@ class AdminContentForm(forms.ModelForm):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['typograph_enabled'].initial = self.instance.bTypograf
|
|
||||||
# AJAX-виджет подгружает список тегов лениво, а здесь мы оставляем
|
# AJAX-виджет подгружает список тегов лениво, а здесь мы оставляем
|
||||||
# только уже выбранные значения, чтобы не тащить все теги из базы при
|
# только уже выбранные значения, чтобы не тащить все теги из базы при
|
||||||
# открытии формы и не провоцировать лишние запросы к SQLite.
|
# открытии формы и не провоцировать лишние запросы к SQLite.
|
||||||
@@ -187,7 +186,7 @@ class AdminContent(admin.ModelAdmin):
|
|||||||
actions_on_bottom = False
|
actions_on_bottom = False
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
obj.bTypograf = form.cleaned_data.get('typograph_enabled', False)
|
obj._typograph_enabled = form.cleaned_data.get('typograph_enabled', False)
|
||||||
obj._typograph_strip_soft_hyphens = form.cleaned_data.get('typograph_strip_soft_hyphens', True)
|
obj._typograph_strip_soft_hyphens = form.cleaned_data.get('typograph_strip_soft_hyphens', True)
|
||||||
obj._typograph_mode = form.cleaned_data.get('typograph_mode', MODE_MIXED)
|
obj._typograph_mode = form.cleaned_data.get('typograph_mode', MODE_MIXED)
|
||||||
obj._typograph_hyphenation = form.cleaned_data.get('typograph_hyphenation', True)
|
obj._typograph_hyphenation = form.cleaned_data.get('typograph_hyphenation', True)
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 5.2.13 on 2026-04-11 13:18
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('filer', '0018_alter_file_options'),
|
||||||
|
('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'),
|
||||||
|
('web', '0003_alter_tbcontent_tags'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='tbcontent',
|
||||||
|
name='bTypograf',
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='tbcontent',
|
||||||
|
index=models.Index(fields=['bContentPublish', 'tdContentPublishUp'], name='web_tbconte_bConten_b53754_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='tbcontent',
|
||||||
|
index=models.Index(fields=['bContentPublish', 'tdContentPublishDown'], name='web_tbconte_bConten_dd200b_idx'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -119,25 +119,28 @@ class RuTaggedItem(TaggedItem):
|
|||||||
return RuTag
|
return RuTag
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
|
||||||
class TbContent(models.Model):
|
class TbContent(models.Model):
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# ТАБЛИЦА TbContent (контент для всего-всего-всего)
|
# ТАБЛИЦА TbContent (контент для всего-всего-всего)
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# | id -- id | primarykey bigint NOT NULL AUTO_INCREMENT |
|
# | id -- id | primarykey bigint NOT NULL AUTO_INCREMENT |
|
||||||
# | kCategory_id -- категория (ссылка на таблицу TbCategory) | bigint DEFAULT NULL,
|
# | bContentPublish -- признак публикации | TINYINT(1) NOT NULL ADD INDEX |
|
||||||
# | bContentPublish -- имя файла | TINYINT(1) NOT NULL ADD INDEX |
|
# | tdContentPublishUp -- начало публикации | datetime(6) NOT NULL ADD INDEX |
|
||||||
# | tdContentPublishStart -- начало публикации | date NOT NULL ADD INDEX |
|
# | tdContentPublishDown -- окончание публикации | datetime(6) NULL ADD INDEX |
|
||||||
|
# | tags -- теги (taggit, M2M) |
|
||||||
# | szContentHead -- заголовок | varchar(512) NOT NULL |
|
# | szContentHead -- заголовок | varchar(512) NOT NULL |
|
||||||
# | imgContentPreview_id -- картинка превью (ссылка на таблицу filer_image) | bigint DEFAULT NULL ADD INDEX
|
# | imgContentPreview_id -- картинка-превью (ссылка на `filer_image`) | bigint DEFAULT NULL |
|
||||||
# | szContentAnno -- анонс | longtext NOT NULL,
|
# | szContentIntro -- анонс | longtext NOT NULL |
|
||||||
# | szContentBody -- содержание | longtext NOT NULL,
|
# | szContentBody -- содержание | longtext NOT NULL |
|
||||||
# | bTypografS -- включить типограф Typograf 2.0 | tinyint(1) NOT NULL,
|
# | szContentSlug -- slug | varchar(128) |
|
||||||
# | szContentTitle -- title для SEO | longtext NOT NULL,
|
# | iContentHits -- число просмотров | bigint/unsigned int NOT NULL ADD INDEX |
|
||||||
# | szContentKeywords -- keywords для SEO | longtext NOT NULL,
|
# | szContentKeywords -- keywords для SEO | varchar(256) |
|
||||||
# | szContentDescription -- Description для SEO | longtext NOT NULL,
|
# | szContentDescription -- description для SEO | varchar(256) |
|
||||||
# | dtContentCreate -- дата и время создания | datetime(6) NOT NULL,
|
# | dtContentCreate -- дата и время создания | datetime(6) NOT NULL |
|
||||||
# | dtContentTimeStamp -- штамп времени (время последнего обновления в базе) | datetime(6) NOT NULL
|
# | dtContentTimeStamp -- штамп времени (время последнего обновления) | datetime(6) NOT NULL |
|
||||||
|
#
|
||||||
|
# Типограф и его настройки теперь живут в админке как виртуальные поля,
|
||||||
|
# и в базе отдельно не хранятся.
|
||||||
# ============================================================
|
# ============================================================
|
||||||
bContentPublish = models.BooleanField(
|
bContentPublish = models.BooleanField(
|
||||||
default=True, db_index=True,
|
default=True, db_index=True,
|
||||||
@@ -199,20 +202,6 @@ class TbContent(models.Model):
|
|||||||
verbose_name="◉",
|
verbose_name="◉",
|
||||||
help_text="Число просмотров"
|
help_text="Число просмотров"
|
||||||
)
|
)
|
||||||
# Поле для удаления. Все будет делаться с помощью виртуальных полей админки
|
|
||||||
bTypograf = models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
verbose_name="Типограф etpgrf",
|
|
||||||
help_text="Обработать через <a href=\"https://typograph.cube2.ru/\""
|
|
||||||
" target=\"_blank\">Типограф ETPRGF</a><br />"
|
|
||||||
"<small><b>СТАБИЛЬНЫЙ И СОВРЕМЕННЫЙ ТИПОГРАФ, РЕКОМЕНДУЕМ</b> "
|
|
||||||
"«приклеивает» союзы и предлоги, поддерживает неразрывные конструкции, "
|
|
||||||
"замена тире, кавычек и дефисов, расстановка «мягких переносов» "
|
|
||||||
"в словах длиннее 14 символов, убирает «вдовы» «сироты» (кроме "
|
|
||||||
"заголовков), расставляет абзацы (кроме заголовков), расшифровывает "
|
|
||||||
"аббревиатуры (те, что знает и кроме заголовков), висячая "
|
|
||||||
"пунктуация (только в заголовках) и т.п.</small>"
|
|
||||||
)
|
|
||||||
szContentKeywords = models.CharField(
|
szContentKeywords = models.CharField(
|
||||||
default="", max_length=256, blank=True, null=True,
|
default="", max_length=256, blank=True, null=True,
|
||||||
verbose_name="Keywords (SEO)",
|
verbose_name="Keywords (SEO)",
|
||||||
@@ -248,6 +237,7 @@ class TbContent(models.Model):
|
|||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
# Переопределяем save(), чтобы автоматически типографировать контент перед сохранением.
|
# Переопределяем save(), чтобы автоматически типографировать контент перед сохранением.
|
||||||
|
typograph_enabled = getattr(self, '_typograph_enabled', False)
|
||||||
typograph_mode = getattr(self, '_typograph_mode', _TYPOGRAPHER_DEFAULT_MODE)
|
typograph_mode = getattr(self, '_typograph_mode', _TYPOGRAPHER_DEFAULT_MODE)
|
||||||
typograph_hyphenation = getattr(self, '_typograph_hyphenation', _TYPOGRAPHER_DEFAULT_HYPHENATION)
|
typograph_hyphenation = getattr(self, '_typograph_hyphenation', _TYPOGRAPHER_DEFAULT_HYPHENATION)
|
||||||
typograph_sanitizer = getattr(self, '_typograph_sanitizer', _TYPOGRAPHER_DEFAULT_SANITIZER)
|
typograph_sanitizer = getattr(self, '_typograph_sanitizer', _TYPOGRAPHER_DEFAULT_SANITIZER)
|
||||||
@@ -265,7 +255,7 @@ class TbContent(models.Model):
|
|||||||
result_slug = f"{base_slug}-{suffix}"
|
result_slug = f"{base_slug}-{suffix}"
|
||||||
suffix += 1
|
suffix += 1
|
||||||
self.szContentSlug = result_slug
|
self.szContentSlug = result_slug
|
||||||
if self.bTypograf:
|
if typograph_enabled:
|
||||||
# `etpgrf` уже умеет HTML-режим и висячую пунктуацию, поэтому здесь
|
# `etpgrf` уже умеет HTML-режим и висячую пунктуацию, поэтому здесь
|
||||||
# не нужен старый локальный fallback.
|
# не нужен старый локальный fallback.
|
||||||
# Мягкие переносы убираем заранее: `etpgrf` не очищает их сам, а они
|
# Мягкие переносы убираем заранее: `etpgrf` не очищает их сам, а они
|
||||||
@@ -291,7 +281,6 @@ class TbContent(models.Model):
|
|||||||
self.szContentHead = _typograph_text(self.szContentHead, head_typographer)
|
self.szContentHead = _typograph_text(self.szContentHead, head_typographer)
|
||||||
self.szContentIntro = _typograph_text(self.szContentIntro, text_typographer)
|
self.szContentIntro = _typograph_text(self.szContentIntro, text_typographer)
|
||||||
self.szContentBody = _typograph_text(self.szContentBody, text_typographer)
|
self.szContentBody = _typograph_text(self.szContentBody, text_typographer)
|
||||||
self.bTypograf = False
|
|
||||||
if self.dtContentCreate is None:
|
if self.dtContentCreate is None:
|
||||||
self.dtContentCreate = datetime.datetime.now()
|
self.dtContentCreate = datetime.datetime.now()
|
||||||
super(TbContent, self).save(*args, **kwargs)
|
super(TbContent, self).save(*args, **kwargs)
|
||||||
@@ -299,11 +288,10 @@ class TbContent(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Контент"
|
verbose_name = "Контент"
|
||||||
verbose_name_plural = u"Контент"
|
verbose_name_plural = u"Контент"
|
||||||
# Если боковая навигация или лента начнут упираться в SQLite, сюда можно
|
# Чтобы боковая навигация или лента нне упиралась в SQLite и работала быстро,
|
||||||
# добавить составные индексы. Пока оставляем это как подсказку, чтобы не
|
# добавляем составные индексы.
|
||||||
# менять схему базы без замеров.
|
indexes = [
|
||||||
# indexes = [
|
models.Index(fields=['bContentPublish', 'tdContentPublishUp']),
|
||||||
# models.Index(fields=['bContentPublish', 'tdContentPublishUp']),
|
models.Index(fields=['bContentPublish', 'tdContentPublishDown']),
|
||||||
# models.Index(fields=['bContentPublish', 'tdContentPublishDown']),
|
]
|
||||||
# ]
|
|
||||||
ordering = ['-tdContentPublishUp', ]
|
ordering = ['-tdContentPublishUp', ]
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ class AdminTypographFormTests(SimpleTestCase):
|
|||||||
def test_admin_form_exposes_virtual_typograph_fields(self):
|
def test_admin_form_exposes_virtual_typograph_fields(self):
|
||||||
form = AdminContentForm()
|
form = AdminContentForm()
|
||||||
|
|
||||||
self.assertNotIn('bTypograf', form.fields)
|
|
||||||
self.assertIn('typograph_enabled', form.fields)
|
self.assertIn('typograph_enabled', form.fields)
|
||||||
self.assertIn('typograph_strip_soft_hyphens', form.fields)
|
self.assertIn('typograph_strip_soft_hyphens', form.fields)
|
||||||
self.assertIn('typograph_mode', form.fields)
|
self.assertIn('typograph_mode', form.fields)
|
||||||
@@ -68,6 +67,9 @@ class AdminTypographFormTests(SimpleTestCase):
|
|||||||
self.assertTrue(form.fields['typograph_hyphenation'].initial)
|
self.assertTrue(form.fields['typograph_hyphenation'].initial)
|
||||||
self.assertEqual(form.fields['typograph_sanitizer'].initial, 'None')
|
self.assertEqual(form.fields['typograph_sanitizer'].initial, 'None')
|
||||||
|
|
||||||
|
def test_tbcontent_model_has_no_btypograf_field(self):
|
||||||
|
self.assertNotIn('bTypograf', [field.name for field in TbContent._meta.fields])
|
||||||
|
|
||||||
def test_tbcontent_str_uses_clean_text(self):
|
def test_tbcontent_str_uses_clean_text(self):
|
||||||
item = TbContent(id=7, szContentHead='<b>«Привет мир»</b>')
|
item = TbContent(id=7, szContentHead='<b>«Привет мир»</b>')
|
||||||
|
|
||||||
@@ -144,8 +146,8 @@ class TypographTests(TestCase):
|
|||||||
szContentHead='«Привет»',
|
szContentHead='«Привет»',
|
||||||
szContentIntro='<p>Абзац</p>',
|
szContentIntro='<p>Абзац</p>',
|
||||||
szContentBody='<p>Тело</p>',
|
szContentBody='<p>Тело</p>',
|
||||||
bTypograf=True,
|
|
||||||
)
|
)
|
||||||
|
item._typograph_enabled = True
|
||||||
|
|
||||||
with patch('web.models._build_typographer') as build_mock:
|
with patch('web.models._build_typographer') as build_mock:
|
||||||
build_mock.return_value.process.side_effect = lambda text: f'[{text}]'
|
build_mock.return_value.process.side_effect = lambda text: f'[{text}]'
|
||||||
@@ -155,15 +157,14 @@ class TypographTests(TestCase):
|
|||||||
self.assertEqual(item.szContentHead, '[«Привет»]')
|
self.assertEqual(item.szContentHead, '[«Привет»]')
|
||||||
self.assertEqual(item.szContentIntro, '[<p>Абзац</p>]')
|
self.assertEqual(item.szContentIntro, '[<p>Абзац</p>]')
|
||||||
self.assertEqual(item.szContentBody, '[<p>Тело</p>]')
|
self.assertEqual(item.szContentBody, '[<p>Тело</p>]')
|
||||||
self.assertFalse(item.bTypograf)
|
|
||||||
|
|
||||||
def test_save_uses_virtual_typograph_options(self):
|
def test_save_uses_virtual_typograph_options(self):
|
||||||
item = TbContent(
|
item = TbContent(
|
||||||
szContentHead='Привет',
|
szContentHead='Привет',
|
||||||
szContentIntro='Текст',
|
szContentIntro='Текст',
|
||||||
szContentBody='Тело',
|
szContentBody='Тело',
|
||||||
bTypograf=True,
|
|
||||||
)
|
)
|
||||||
|
item._typograph_enabled = True
|
||||||
item._typograph_mode = MODE_UNICODE
|
item._typograph_mode = MODE_UNICODE
|
||||||
item._typograph_hyphenation = False
|
item._typograph_hyphenation = False
|
||||||
item._typograph_sanitizer = SANITIZE_ETPGRF
|
item._typograph_sanitizer = SANITIZE_ETPGRF
|
||||||
@@ -198,8 +199,8 @@ class TypographTests(TestCase):
|
|||||||
szContentHead='При­вет\u00ad',
|
szContentHead='При­вет\u00ad',
|
||||||
szContentIntro='А­нонс',
|
szContentIntro='А­нонс',
|
||||||
szContentBody='Те­ло\u00ad',
|
szContentBody='Те­ло\u00ad',
|
||||||
bTypograf=True,
|
|
||||||
)
|
)
|
||||||
|
item._typograph_enabled = True
|
||||||
|
|
||||||
with patch('web.models._build_typographer') as build_mock:
|
with patch('web.models._build_typographer') as build_mock:
|
||||||
build_mock.return_value.process.side_effect = lambda text: f'[{text}]'
|
build_mock.return_value.process.side_effect = lambda text: f'[{text}]'
|
||||||
@@ -209,7 +210,12 @@ class TypographTests(TestCase):
|
|||||||
self.assertEqual(item.szContentHead, '[Привет]')
|
self.assertEqual(item.szContentHead, '[Привет]')
|
||||||
self.assertEqual(item.szContentIntro, '[Анонс]')
|
self.assertEqual(item.szContentIntro, '[Анонс]')
|
||||||
self.assertEqual(item.szContentBody, '[Тело]')
|
self.assertEqual(item.szContentBody, '[Тело]')
|
||||||
self.assertFalse(item.bTypograf)
|
|
||||||
|
def test_tbcontent_has_composite_indexes_for_navigation(self):
|
||||||
|
index_fields = [tuple(index.fields) for index in TbContent._meta.indexes]
|
||||||
|
|
||||||
|
self.assertIn(('bContentPublish', 'tdContentPublishUp'), index_fields)
|
||||||
|
self.assertIn(('bContentPublish', 'tdContentPublishDown'), index_fields)
|
||||||
|
|
||||||
def test_show_item_increments_hits_without_touching_timestamp(self):
|
def test_show_item_increments_hits_without_touching_timestamp(self):
|
||||||
item = TbContent.objects.create(
|
item = TbContent.objects.create(
|
||||||
|
|||||||
Reference in New Issue
Block a user