Phase 2: T-201 至 T-206 前台 MVP 全部实现(11 templates + CSS + 31 tests)

This commit is contained in:
ila
2026-07-06 22:57:08 +08:00
parent 5604e2cde5
commit cff3c64c19
20 changed files with 996 additions and 98 deletions
+70
View File
@@ -1,4 +1,6 @@
from django.core.paginator import Paginator, EmptyPage
from django.db import models
from django.db.models import Q
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator, MaxValueValidator
from modelcluster.fields import ParentalManyToManyField
@@ -25,6 +27,18 @@ class ScenarioPage(Page):
content_panels = Page.content_panels
template = "core/scenario_page.html"
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
context["projects"] = (
SkeletonProjectPage.objects.live()
.filter(scenarios=self)
.prefetch_related("languages", "scenarios")
.order_by("-first_published_at")
)
return context
class Meta:
verbose_name = "Scenario"
@@ -112,11 +126,65 @@ class ProjectIndexPage(Page):
content_panels = Page.content_panels
template = "core/project_index_page.html"
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
projects = (
SkeletonProjectPage.objects.live()
.prefetch_related("languages", "scenarios")
.order_by("-first_published_at")
)
language = request.GET.get("language", "")
framework = request.GET.get("framework", "")
database = request.GET.get("database", "")
min_score = request.GET.get("min_score", "")
q = request.GET.get("q", "")
if language:
projects = projects.filter(languages__slug=language)
if framework:
projects = projects.filter(frameworks__slug=framework)
if database:
projects = projects.filter(databases__slug=database)
try:
score_val = int(min_score)
if 0 <= score_val <= 30:
projects = [
p for p in projects if p.total_score >= score_val
]
except (ValueError, TypeError):
pass
if q:
projects = projects.filter(
Q(title__icontains=q) | Q(summary__icontains=q)
)
paginator = Paginator(projects, 20)
page = request.GET.get("page", "1")
try:
context["projects"] = paginator.page(int(page))
except (ValueError, EmptyPage):
context["projects"] = paginator.page(1)
context["current_language"] = language
context["current_framework"] = framework
context["current_database"] = database
context["current_min_score"] = min_score
context["current_q"] = q
context["languages"] = Language.objects.all()
context["frameworks"] = Framework.objects.all()
context["databases"] = DatabaseOption.objects.all()
return context
class Meta:
verbose_name = "Project Index"
class SkeletonProjectPage(Page):
template = "core/skeleton_project_page.html"
MATURITY_CHOICES = [
("experimental", "Experimental"),
("stable", "Stable"),
@@ -255,6 +323,7 @@ class SkeletonProjectPage(Page):
class ArticleIndexPage(Page):
template = "core/article_index_page.html"
parent_page_types = ["home.HomePage"]
subpage_types = ["core.ArticlePage"]
max_count = 1
@@ -266,6 +335,7 @@ class ArticleIndexPage(Page):
class ArticlePage(Page):
template = "core/article_page.html"
body = RichTextField()
related_projects = ParentalManyToManyField(
"core.SkeletonProjectPage", blank=True