feat: add people sections and opt-in read more
This commit is contained in:
+14
-10
@@ -9,14 +9,14 @@ from .models import Article, ArticleCitation, ArticleSection, MainProduct, Produ
|
||||
class ArticleCitationInline(admin.TabularInline):
|
||||
model = ArticleCitation
|
||||
extra = 1
|
||||
fields = ("text", "url", "order")
|
||||
fields = ("text", "read_more_text", "url", "order")
|
||||
ordering = ("order",)
|
||||
|
||||
|
||||
class ArticleSectionInline(admin.TabularInline):
|
||||
model = ArticleSection
|
||||
extra = 1
|
||||
fields = ("title", "value_format", "value", "order")
|
||||
fields = ("title", "value_format", "value", "read_more_value", "order")
|
||||
ordering = ("order",)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class SubProductArticleInline(admin.StackedInline):
|
||||
model = Article
|
||||
fk_name = "sub_product"
|
||||
extra = 0
|
||||
fields = ("badge", "title", "description", "order")
|
||||
fields = ("badge", "title", "description", "read_more_description", "order")
|
||||
ordering = ("order",)
|
||||
show_change_link = True
|
||||
|
||||
@@ -33,7 +33,7 @@ class MainProductArticleInline(admin.StackedInline):
|
||||
model = Article
|
||||
fk_name = "main_product"
|
||||
extra = 0
|
||||
fields = ("badge", "title", "description", "order")
|
||||
fields = ("badge", "title", "description", "read_more_description", "order")
|
||||
ordering = ("order",)
|
||||
show_change_link = True
|
||||
|
||||
@@ -47,6 +47,7 @@ class ProductVideoInline(admin.StackedInline):
|
||||
"title",
|
||||
"description_format",
|
||||
"description",
|
||||
"read_more_description",
|
||||
"video_source",
|
||||
"video_url",
|
||||
"video_file",
|
||||
@@ -63,7 +64,7 @@ class ProductVideoInline(admin.StackedInline):
|
||||
class SubProductInline(admin.StackedInline):
|
||||
model = SubProduct
|
||||
extra = 0
|
||||
fields = ("name", "slug", "short_description", "image", "logo", "show_on_homepage", "homepage_order", "order", "is_active")
|
||||
fields = ("name", "slug", "short_description", "read_more_short_description", "image", "logo", "show_on_homepage", "homepage_order", "order", "is_active")
|
||||
ordering = ("order",)
|
||||
show_change_link = True
|
||||
prepopulated_fields = {"slug": ("name",)}
|
||||
@@ -85,10 +86,11 @@ class MainProductAdmin(admin.ModelAdmin):
|
||||
"name",
|
||||
"slug",
|
||||
"short_description",
|
||||
"read_more_short_description",
|
||||
)
|
||||
},
|
||||
),
|
||||
("Description", {"fields": ("description_format", "description")}),
|
||||
("Description", {"fields": ("description_format", "description", "read_more_description")}),
|
||||
("Media", {"fields": ("image",)}),
|
||||
("Homepage", {"fields": ("show_on_homepage", "homepage_order")}),
|
||||
("Settings", {"fields": ("order", "is_active")}),
|
||||
@@ -104,6 +106,7 @@ class SubProductVideoInline(admin.StackedInline):
|
||||
"title",
|
||||
"description_format",
|
||||
"description",
|
||||
"read_more_description",
|
||||
"video_source",
|
||||
"video_url",
|
||||
"video_file",
|
||||
@@ -143,10 +146,11 @@ class SubProductAdmin(admin.ModelAdmin):
|
||||
"name",
|
||||
"slug",
|
||||
"short_description",
|
||||
"read_more_short_description",
|
||||
)
|
||||
},
|
||||
),
|
||||
("Description", {"fields": ("description_format", "description")}),
|
||||
("Description", {"fields": ("description_format", "description", "read_more_description")}),
|
||||
("Media", {"fields": ("image", "logo")}),
|
||||
("Homepage", {"fields": ("show_on_homepage", "homepage_order")}),
|
||||
("Settings", {"fields": ("order", "is_active")}),
|
||||
@@ -163,7 +167,7 @@ class ArticleAdmin(admin.ModelAdmin):
|
||||
inlines = [ArticleSectionInline, ArticleCitationInline]
|
||||
fieldsets = (
|
||||
(None, {"fields": ("main_product", "sub_product", "badge", "title")}),
|
||||
("Description", {"fields": ("description_format", "description")}),
|
||||
("Description", {"fields": ("description_format", "description", "read_more_description")}),
|
||||
("Citation Badge", {"fields": ("citation_count_display", "citation_count_label"), "description": "Number and optional label for the dashed citation circle badge. Both are optional — a label without a number shows an empty circle with the label; neither hides the badge entirely."}),
|
||||
("Settings", {"fields": ("order",)}),
|
||||
)
|
||||
@@ -186,7 +190,7 @@ class ProductVideoAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ("main_product", "sub_product")
|
||||
readonly_fields = ("video_preview",)
|
||||
fieldsets = (
|
||||
(None, {"fields": ("main_product", "sub_product", "badge", "title", "description_format", "description")}),
|
||||
(None, {"fields": ("main_product", "sub_product", "badge", "title", "description_format", "description", "read_more_description")}),
|
||||
VIDEO_ADMIN_FIELDSET,
|
||||
("Settings", {"fields": ("order", "is_active")}),
|
||||
)
|
||||
@@ -212,6 +216,6 @@ class ArticleSectionAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ("article",)
|
||||
fieldsets = (
|
||||
(None, {"fields": ("article", "title")}),
|
||||
("Content", {"fields": ("value_format", "value")}),
|
||||
("Content", {"fields": ("value_format", "value", "read_more_value")}),
|
||||
("Settings", {"fields": ("order",)}),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 5.2.16 on 2026-10-12 04:52
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('products', '0022_alter_mainproduct_options_alter_productvideo_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='read_more_description',
|
||||
field=models.BooleanField(default=False, help_text='Collapse this article text with Read more.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='articlesection',
|
||||
name='read_more_value',
|
||||
field=models.BooleanField(default=False, help_text='Collapse this section text with Read more.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mainproduct',
|
||||
name='read_more_description',
|
||||
field=models.BooleanField(default=False, help_text='Collapse the full description with Read more.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mainproduct',
|
||||
name='read_more_short_description',
|
||||
field=models.BooleanField(default=False, help_text='Collapse the short description with Read more.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subproduct',
|
||||
name='read_more_description',
|
||||
field=models.BooleanField(default=False, help_text='Collapse the full description with Read more.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subproduct',
|
||||
name='read_more_short_description',
|
||||
field=models.BooleanField(default=False, help_text='Collapse the short description with Read more.'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.2.16 on 2026-10-12 11:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('products', '0023_article_read_more_description_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='articlecitation',
|
||||
name='read_more_text',
|
||||
field=models.BooleanField(default=False, help_text='Collapse a long citation with Read more.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='productvideo',
|
||||
name='read_more_description',
|
||||
field=models.BooleanField(default=False, help_text='Collapse this video description with Read more.'),
|
||||
),
|
||||
]
|
||||
@@ -63,7 +63,9 @@ class MainProduct(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
slug = models.SlugField(unique=True, blank=True)
|
||||
short_description = models.CharField(max_length=300)
|
||||
read_more_short_description = models.BooleanField(default=False, help_text="Collapse the short description with Read more.")
|
||||
description = models.TextField()
|
||||
read_more_description = models.BooleanField(default=False, help_text="Collapse the full description with Read more.")
|
||||
description_format = models.CharField(
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
@@ -104,7 +106,9 @@ class SubProduct(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
slug = models.SlugField(blank=True)
|
||||
short_description = models.CharField(max_length=300)
|
||||
read_more_short_description = models.BooleanField(default=False, help_text="Collapse the short description with Read more.")
|
||||
description = models.TextField()
|
||||
read_more_description = models.BooleanField(default=False, help_text="Collapse the full description with Read more.")
|
||||
description_format = models.CharField(
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
@@ -166,6 +170,7 @@ class Article(models.Model):
|
||||
)
|
||||
title = models.CharField(max_length=300)
|
||||
description = models.TextField()
|
||||
read_more_description = models.BooleanField(default=False, help_text="Collapse this article text with Read more.")
|
||||
description_format = models.CharField(
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
@@ -433,6 +438,7 @@ class ArticleSection(models.Model):
|
||||
)
|
||||
title = models.CharField(max_length=200)
|
||||
value = models.TextField()
|
||||
read_more_value = models.BooleanField(default=False, help_text="Collapse this section text with Read more.")
|
||||
value_format = models.CharField(
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
@@ -458,6 +464,7 @@ class ArticleCitation(models.Model):
|
||||
related_name="citations",
|
||||
)
|
||||
text = models.TextField(help_text="Full citation text.")
|
||||
read_more_text = models.BooleanField(default=False, help_text="Collapse a long citation with Read more.")
|
||||
url = models.URLField(blank=True, help_text="Optional link to the cited source.")
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
|
||||
@@ -488,6 +495,7 @@ class ProductVideo(VideoBlockMixin, models.Model):
|
||||
badge = models.CharField(max_length=100, blank=True)
|
||||
title = models.CharField(max_length=300, blank=True)
|
||||
description = models.TextField(blank=True)
|
||||
read_more_description = models.BooleanField(default=False, help_text="Collapse this video description with Read more.")
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
|
||||
@@ -56,6 +56,21 @@ class ProductOverviewViewTest(ProductViewsSetup):
|
||||
|
||||
|
||||
class MainProductDetailViewTest(ProductViewsSetup):
|
||||
def test_read_more_can_be_set_per_article_and_section(self):
|
||||
url = self.main_product.get_absolute_url()
|
||||
article = Article.objects.create(main_product=self.main_product, title="Notes", description="Long notes")
|
||||
section = ArticleSection.objects.create(article=article, title="Detail", value="Long detail")
|
||||
response = self.client.get(url)
|
||||
self.assertNotContains(response, 'data-readmore="260"')
|
||||
self.assertNotContains(response, 'data-readmore="160"')
|
||||
article.read_more_description = True
|
||||
article.save(update_fields=["read_more_description"])
|
||||
section.read_more_value = True
|
||||
section.save(update_fields=["read_more_value"])
|
||||
response = self.client.get(url)
|
||||
self.assertContains(response, 'data-readmore="260"')
|
||||
self.assertContains(response, 'data-readmore="160"')
|
||||
|
||||
def test_detail_returns_200(self):
|
||||
url = reverse("products:main_product_detail", kwargs={"main_slug": "com2care"})
|
||||
response = self.client.get(url)
|
||||
|
||||
Reference in New Issue
Block a user