class GuidelinePage(Page): parent_page_types = ['guidelines.GuidelinesIndexPage'] subpage_types = [] subtitle = RichTextField('Подзаголовок', blank=True, null=True) # поле для ввода текста raw_content = RichTextField('Содержание', blank=True) tags = ClusterTaggableManager(through='guidelines.GuidelinePageTag', blank=True) publish_date = models.DateField('Дата', blank=True, null=True, default=get_default_date) @property # тут получаем текст с валидными тегами def text(self): return richtext(self.raw_content) @property def section_title(self): return self.get_parent().title @property def section_url(self): return self.get_parent().url @property def breadcrumbs(self): breadcrumbs = [] for page in self.get_ancestors()[2:]: breadcrumbs.append({'title': page.title, 'url': page.url}) return breadcrumbs @property def tags_slugs(self): return '\n'.join(self.tags.all().values_list('slug', flat=True)) @property def clean_preview(self): h = html2text.HTML2Text() h.ignore_links = True h.ignore_emphasis = True h.ignore_images = True if self.subtitle: raw_text = h.handle(self.subtitle) else: raw_text = h.handle(self.raw_content) if len(raw_text) > 310: raw_text = raw_text[:310] space_index = raw_text.rfind(' ') raw_text = raw_text[:space_index] + '...' return raw_text.replace('\n', ' ').strip() content_panels = Page.content_panels + [ MultiFieldPanel([FieldPanel("tags")], heading="Теги"), FieldPanel('subtitle'), FieldPanel('raw_content'), FieldPanel('publish_date'), MultiFieldPanel([ InlinePanel( "guideline_authors", label="Author", min_num=0, max_num=4) ], heading="Автор(ы)") ] search_fields = Page.search_fields + [ index.RelatedFields('guideline_authors', [ index.SearchField('full_name', partial_match=True), index.SearchField('alias') ]), index.SearchField('publish_date'), index.SearchField('text'), index.RelatedFields('tags', [ index.SearchField('slug'), index.FilterField('slug'), ]), index.SearchField('tags_slugs', partial_match=True), ] api_fields = [ APIField('breadcrumbs'), APIField('section_title'), APIField('section_url'), APIField('subtitle'), APIField('text'), APIField('tags', serializer=serializers.TagSerializer()), APIField('publish_date'), APIField('tags_slugs'), APIField('authors', serializer=serializers.AuthorSerializer( source='guideline_authors')), APIField('clean_preview'), ] def clean(self): super().clean() # автоматически создаем слаг if self.slug == 'default-blank-slug': if len(self.title) > 20: self.slug = slugify(self.title[:20]) else: self.slug = slugify(self.title) if '--' in self.slug: self.slug = self.slug.replace('--', '-') if self.slug.endswith('-'): self.slug = self.slug[:-1] def get_sitemap_urls(self, request): return [{ 'location': self.full_url[:-1], 'lastmod': self.last_published_at, }] class Meta: verbose_name = 'Памятка' verbose_name_plural = 'Памятки'
class BlogArticlePage(Page): """ Запись в блоге """ parent_page_types = ['blog.BlogIndexPage'] subpage_types = [] raw_subtitle = RichTextField(null=True, blank=True) content = StreamField([('full_richtext', base_blocks.RichTextBlock()), ('table', TableBlock(label='Таблица')), ('raw_html', RawHTMLBlock(label='HTML'))], null=True, blank=True, help_text='Содержание') tags = ClusterTaggableManager(through='blog.BlogPageTag', blank=True) publish_date = models.DateField(blank=True, null=True) @property def subtitle(self): return richtext(self.raw_subtitle) @property def breadcrumbs(self): breadcrumbs = [] for page in self.get_ancestors()[2:]: breadcrumbs.append({'title': page.title, 'url': page.url}) return breadcrumbs @property def section_title(self): title = self.get_parent().title if 'Дискуссионные вопросы' in title: return 'Дискуссионные вопросы' else: return title @property def section_url(self): return self.get_parent().url @property def tags_slugs(self): return '\n'.join(self.tags.all().values_list('slug', flat=True)) @property def clean_preview(self): h = html2text.HTML2Text() h.ignore_links = True h.ignore_emphasis = True h.ignore_images = True if self.subtitle: raw_text = h.handle(self.subtitle) else: raw_text = h.handle(self.content[0].value.source) if len(raw_text) > 310: raw_text = raw_text[:310] space_index = raw_text.rfind(' ') raw_text = raw_text[:space_index] + '...' return raw_text.replace('\n', ' ').strip() content_panels = Page.content_panels + [ FieldPanel('raw_subtitle', heading='Подзаголовок'), StreamFieldPanel('content', heading='Содержание'), MultiFieldPanel([ InlinePanel("blog_authors", label="Author", min_num=1, max_num=4) ], heading="Автор(ы)"), MultiFieldPanel([FieldPanel("tags")], heading="Теги"), FieldPanel('publish_date') ] search_fields = Page.search_fields + [ index.RelatedFields('blog_authors', [ index.SearchField('full_name', partial_match=True), index.SearchField('alias') ]), index.SearchField('tags_slugs', partial_match=True), index.SearchField('subtitle'), index.SearchField('content'), index.SearchField('publish_date'), index.FilterField('publish_date') ] api_fields = [ APIField('breadcrumbs'), APIField('section_title'), APIField('section_url'), APIField('subtitle'), APIField('content'), APIField('tags', serializer=serializers.TagSerializer()), APIField( 'authors', serializer=serializers.AuthorSerializer(source='blog_authors')), APIField('publish_date', serializer=serializers.DateSerializer()), APIField('tags_slugs'), APIField('clean_preview') ] def clean(self): super().clean() # автоматически создаем слаг if self.slug == 'default-blank-slug': if len(self.title) > 30: self.slug = slugify(self.title[:30]) else: self.slug = slugify(self.title) if '--' in self.slug: self.slug = self.slug.replace('--', '-') if self.slug.endswith('-'): self.slug = self.slug[:-1] def get_sitemap_urls(self, request): return [{ 'location': self.full_url[:-1], 'lastmod': self.last_published_at, }] class Meta: verbose_name = 'Запись в блоге' verbose_name_plural = 'Записи в блогах'
class LibraryItemPage(Page): parent_page_types = ['library.LibraryIndexPage'] subpage_types = [] raw_subtitle = RichTextField(verbose_name='Подзаголовок', blank=True, null=True) content = StreamField([('full_richtext', base_blocks.RichTextBlock()), ('table', TableBlock(label='Таблица')), ('raw_html', RawHTMLBlock(label='HTML'))], null=True, blank=True, verbose_name='Содержание') doc = models.ForeignKey('base.CustomDocument', blank=True, null=True, on_delete=models.SET_NULL, related_name='+', verbose_name="Документ") publish_date = models.DateField("Дата публикации", blank=True, null=True) tags = ClusterTaggableManager(through='library.LibraryPageTag', blank=True) @property def subtitle(self): return richtext(self.raw_subtitle) @property def breadcrumbs(self): breadcrumbs = [] for page in self.get_ancestors()[2:]: breadcrumbs.append({'title': page.title, 'url': page.url}) return breadcrumbs @property def section_title(self): return self.get_parent().title @property def section_url(self): return self.get_parent().url @property def tags_slugs(self): return '\n'.join(self.tags.all().values_list('slug', flat=True)) content_panels = Page.content_panels + [ FieldPanel('publish_date'), FieldPanel('raw_subtitle'), StreamFieldPanel('content'), MultiFieldPanel([ InlinePanel("library_authors", label="Автор", min_num=0, max_num=5) ], heading="Автор(ы)"), FieldPanel('tags'), DocumentChooserPanel('doc'), ] search_fields = Page.search_fields + [ index.SearchField('subtitle'), index.SearchField('content'), index.SearchField('tags'), index.RelatedFields('tags', [ index.SearchField('slug'), index.FilterField('slug'), index.FilterField('name'), index.SearchField('name') ]), index.SearchField('tags_slugs', partial_match=True), index.SearchField('publish_date'), index.FilterField('publish_date'), ] api_fields = [ APIField('breadcrumbs'), APIField('section_title'), APIField('section_url'), APIField('publish_date', serializer=serializers.DateSerializer()), APIField('tags', serializer=serializers.TagSerializer()), APIField('subtitle'), APIField('content'), APIField( 'authors', serializer=serializers.AuthorSerializer(source='library_authors')), APIField('doc', serializer=serializers.DocSerializer()), APIField('tags_slugs'), ] def get_sitemap_urls(self, request): return [{ 'location': self.full_url[:-1], 'lastmod': self.last_published_at, }] def clean(self): super().clean() # автоматически создаем слаг if self.slug == 'default-blank-slug': if len(self.title) > 40: self.slug = slugify(self.title[:40]) else: self.slug = slugify(self.title) if '--' in self.slug: self.slug = self.slug.replace('--', '-') if self.slug.endswith('-'): self.slug = self.slug[:-1] class Meta: verbose_name = 'Материал библиотеки' verbose_name_plural = 'Материалы библиотеки'
class ConsPage(Page): """Страница консультации""" subpage_types = [] parent_page_types = ['cons.ConsIndexPage'] number = models.PositiveIntegerField('Номер', unique=True, default=get_default_cons_number) tags = ClusterTaggableManager('Теги', through=ConsPageTag, blank=True) publish_date = models.DateField('Дата', null=True, default=get_default_date) too_old = models.BooleanField('Устарела', default=False, null=False, ) # если конса неактаульная, ставим галочку @property def preview_client(self): ''' предпросмотр имен клиентов, задающих вопрос ''' for block in self.content: if block.block_type == 'question': clients = list() for sub_block in block.value: if sub_block.block_type == 'client': clients.append(sub_block.value) clients_str = str() for client in clients: clients_str += client clients_str += ', ' clients_str = clients_str[:-2] return clients_str @property def preview_question(self): for block in self.content: if block.block_type == 'question': for sub_block in block.value: if sub_block.block_type == 'question_text': html = sub_block.value.source soup = BeautifulSoup(html, 'html.parser') links = soup.find_all('a', href=True) if links: for link in links: link.replace_with_children() html = str(soup) if len(html) > 1000: text = html[:1000] last_dot_index = text.rfind('. ') html = text[:last_dot_index] + '...</p>' return html else: return html @property def section_title(self): return self.get_parent().title @property def section_url(self): return self.get_parent().url @property def breadcrumbs(self): breadcrumbs = [] for page in self.get_ancestors()[2:]: breadcrumbs.append({'title': page.title, 'url': page.url}) return breadcrumbs @property def clean_preview(self): h = html2text.HTML2Text() h.ignore_links = True h.ignore_emphasis = True h.ignore_images = True raw_text = h.handle(self.preview_question) return raw_text.replace('\n', ' ').strip() authors = ParentalManyToManyField(Author, blank=True) content = StreamField( [ ('question', QuestionBlock()), ('answer', AnswerBlock()), ], blank=True, verbose_name='Содержание') content_panels = [ FieldPanel("number"), FieldPanel("tags"), StreamFieldPanel("content"), FieldPanel('publish_date'), FieldPanel('too_old'), MultiFieldPanel( [InlinePanel("previous_cons", label='Консультация') ], heading='Предыдущие консультации') ] @property def tags_slugs(self): return '\n'.join(self.tags.all().values_list('slug', flat=True)) search_fields = Page.search_fields + [ index.SearchField('number'), index.FilterField('number'), index.SearchField('publish_date'), index.FilterField('publish_date'), index.SearchField('preview_client'), index.SearchField('preview_question'), index.FilterField('authors'), index.SearchField('content'), index.SearchField('tags_slugs', partial_match=True), index.RelatedFields('tags', [ index.SearchField('slug'), index.FilterField('slug'), ]), ] api_fields = [ APIField('breadcrumbs'), APIField('section_title'), APIField('section_url'), APIField('number'), APIField('tags', serializer=serializers.TagSerializer()), APIField('publish_date', serializer=serializers.DateSerializer()), APIField('authors'), APIField("preview_client"), # APIField("preview_question"), APIField("content"), APIField('previous_cons'), APIField('too_old'), APIField('tags_slugs'), APIField('authors', serializer=serializers.AuthorSerializer()), APIField('clean_preview'), ] def clean(self): super().clean() # автоматически создаем заголовок и слаг, # добавляя к номеру консультации и ее теги title = 'Консультация №' + str(self.number) + ': ' for tag in self.tags.names(): title += tag title += ', ' title = title[:-2] self.title = title slug = f'c-{str(self.number)}' self.slug = slug def save(self, *args, **kwargs): super().save() for block in self.content: if block.block_type == 'answer': # TODO удалить всех авторов на всякий случай for sub_block in block.value: if sub_block.block_type == 'author': a = Author.objects.get(id=sub_block.value.id) self.authors.add(a) return super().save(*args, **kwargs) def get_sitemap_urls(self, request): return [{ 'location': self.full_url[:-1], 'lastmod': self.last_published_at, }] class Meta: verbose_name = 'Консультация' verbose_name_plural = 'Консультации'
class SubjectArticlePage(Page): parent_page_types = ['SubjectIndexPage', 'SubjectArticlePage'] subpage_types = ['SubjectArticlePage'] preview_image = models.ForeignKey( 'wagtailimages.Image', on_delete=models.SET_NULL, related_name='+', null=True, blank=True ) preview_desc = models.TextField(verbose_name='Описание сюжета', max_length=800, blank=True) raw_subtitle = RichTextField(null=True, blank=True) content = StreamField( [ ('full_richtext', base_blocks.RichTextBlock()), ('table', TableBlock(label='Таблица')), ('raw_html', RawHTMLBlock(label='HTML')), ('incut', CustomPageChooserBlock(label='Врезка')) ], null=True, blank=True, help_text='Содержание') tags = ClusterTaggableManager(through='subjects.SubjectArticleTag', blank=True) publish_date = models.DateField(blank=True, null=True) news = StreamField( [ ('full_richtext', base_blocks.RichTextBlock()), ], null=True, blank=True, help_text='Новости/материалы по теме' ) @property def subtitle(self): return richtext(self.raw_subtitle) @property def tags_slugs(self): return '\n'.join(self.tags.all().values_list('slug', flat=True)) @property def clean_preview(self): h = html2text.HTML2Text() h.ignore_links = True h.ignore_emphasis = True h.ignore_images = True if self.subtitle: raw_text = h.handle(self.subtitle) else: raw_text = h.handle(self.content[0].value.source) if len(raw_text) > 310: raw_text = raw_text[:310] space_index = raw_text.rfind(' ') raw_text = raw_text[:space_index] + '...' return raw_text.replace('\n', ' ').strip() @property def breadcrumbs(self): breadcrumbs = [] for page in self.get_ancestors()[2:]: breadcrumbs.append({'title': page.title, 'url': page.url}) return breadcrumbs @property def section_title(self): title = self.get_parent().title return title @property def section_url(self): return self.get_parent().url content_panels = Page.content_panels + [ FieldPanel('raw_subtitle', heading='Подзаголовок'), StreamFieldPanel('content', heading='Содержание'), StreamFieldPanel('news', heading='Новости по теме'), MultiFieldPanel( [ InlinePanel("subject_authors", label="Автор", min_num=1, max_num=4) ], heading="Автор(ы)" ), MultiFieldPanel( [ FieldPanel("tags") ], heading="Теги" ), FieldPanel('publish_date', heading='Дата публикации'), ImageChooserPanel('preview_image', heading='Картинка для предпросмотра'), FieldPanel('preview_desc') ] api_fields = [ APIField('subtitle'), APIField('content'), APIField('authors', serializer=serializers.AuthorSerializer(source='subject_authors')), APIField('tags', serializer=serializers.TagSerializer()), APIField('publish_date', serializer=serializers.DateSerializer()), APIField('news'), APIField('clean_preview'), APIField('preview_image'), APIField('preview_desc'), APIField('breadcrumbs'), APIField('section_title'), APIField('section_url') ] search_fields = Page.search_fields + [ index.RelatedFields('subject_authors',[ index.SearchField('full_name', partial_match=True), index.SearchField('alias')]), index.SearchField('tags_slugs', partial_match=True), index.SearchField('subtitle'), index.SearchField('content'), index.SearchField('publish_date'), index.FilterField('publish_date'), index.SearchField('news') ] def clean(self): super().clean() # автоматически создаем слаг if self.slug == 'default-blank-slug': if len(self.title) > 30: self.slug = slugify(self.title[:30]) else: self.slug = slugify(self.title) if '--' in self.slug: self.slug = self.slug.replace('--','-') if self.slug.endswith('-'): self.slug = self.slug[:-1] def get_sitemap_urls(self, request): return [{ 'location': self.full_url[:-1], 'lastmod': self.last_published_at, }] class Meta: verbose_name = 'Страница сюжета' verbose_name_plural = "Страницы сюжетов"