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