feat: paper list 返 abstract + 加 retrieve 端点 + filterset 扩 year range / 多字段

为 zcbot research skill 让出 LLM 友好接口:list 端点带 abstract 省 LLM 逐条 get 的 round-trip;PaperViewSet 加 CustomRetrieveModelMixin 修 GET /api/resm/paper/<id>/ 原本 404 的 bug;filterset_class 扩 publication_year_gte/lte + has_fulltext_pdf / is_oa / publication_name / first_author / openalex_id;queryset 加 select_related("abstract") 防 N+1。search_fields 不动(仍 title/first_author/first_author_institution)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-05-21 13:17:46 +08:00
parent 326f6b35d5
commit e8320bce05
3 changed files with 43 additions and 5 deletions

26
apps/resm/filters.py Normal file
View File

@ -0,0 +1,26 @@
from django_filters import rest_framework as filters
from .models import Paper
class PaperFilterSet(filters.FilterSet):
# 年份范围(LLM 做"近 N 年文献"很常见)
publication_year_gte = filters.NumberFilter(field_name="publication_year", lookup_expr="gte")
publication_year_lte = filters.NumberFilter(field_name="publication_year", lookup_expr="lte")
class Meta:
model = Paper
fields = [
# 原有
"publication_year",
"type",
"fetch_status",
"has_abstract",
"has_fulltext",
"doi",
# 新增
"has_fulltext_pdf", # 比 has_fulltext 更准:zcbot research skill 的 has_pdf 参数走这个
"is_oa", # 是否开放获取
"publication_name", # 期刊精确名
"first_author", # 作者精确名
"openalex_id", # OpenAlex 唯一标识(等价 doi)
]

View File

@ -1,7 +1,18 @@
from apps.utils.serializers import CustomModelSerializer
from rest_framework import serializers
from .models import Paper
class PaperListSerializer(CustomModelSerializer):
abstract = serializers.SerializerMethodField()
class Meta:
model = Paper
fields = '__all__'
fields = '__all__'
def get_abstract(self, obj) -> str:
# PaperAbstract 是 O2O,related_name="abstract";无行 / has_abstract=False 时返空串
abs_obj = getattr(obj, "abstract", None)
if abs_obj is None:
return ""
return abs_obj.abstract or ""

View File

@ -5,7 +5,9 @@ from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from .models import Paper, PaperAbstract
from .serializers import PaperListSerializer
from .filters import PaperFilterSet
from apps.utils.viewsets import CustomGenericViewSet, CustomListModelMixin
from apps.utils.mixins import CustomRetrieveModelMixin
import os
@ -31,11 +33,10 @@ paper_pdf_view.cls.swagger_schema = None
# Create your views here.
class PaperViewSet(CustomGenericViewSet, CustomListModelMixin):
queryset = Paper.objects.all()
class PaperViewSet(CustomGenericViewSet, CustomListModelMixin, CustomRetrieveModelMixin):
queryset = Paper.objects.select_related("abstract").all()
serializer_class = PaperListSerializer
filterset_fields = ["publication_year", "type", "fetch_status",
"has_abstract", "has_fulltext", "doi"]
filterset_class = PaperFilterSet
search_fields = ['title', 'first_author', 'first_author_institution']
ordering = ["-publication_date", "-create_time"]