feat(material): 列表序列化器与筛选扩展

- MaterialListSerializer 补全 A 组字段(规格/应用/优势/成本/星级/施工)
- 新增供应商扩展字段 factory_cooperation_mode/display/province/city
- MaterialSerializer 同步供应商扩展字段供详情使用
- 视图 queryset 增加 select_related('factory','brand')
- 新增筛选: stage / importance_level / factory / cooperation_mode /
  landing_project / cost_compare 区间 / score_level>=N / contact_person / handler
- material_category 改为 icontains 模糊匹配
- choices 接口追加 cooperation_mode

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-04-24 14:11:35 +08:00
parent 2b76bbc62a
commit 99e6df0a57
2 changed files with 83 additions and 7 deletions

View File

@ -20,6 +20,10 @@ class MaterialSerializer(serializers.ModelSerializer):
"""
factory_name = serializers.CharField(source='factory.factory_name', read_only=True)
factory_short_name = serializers.CharField(source='factory.short_name', read_only=True)
factory_cooperation_mode = serializers.CharField(source='factory.cooperation_mode', read_only=True, default=None)
factory_cooperation_mode_display = serializers.SerializerMethodField()
factory_province = serializers.CharField(source='factory.province', read_only=True, default=None)
factory_city = serializers.CharField(source='factory.city', read_only=True, default=None)
brand = serializers.PrimaryKeyRelatedField(
queryset=Brand.objects.all(), allow_null=True, required=False
)
@ -57,10 +61,15 @@ class MaterialSerializer(serializers.ModelSerializer):
'brochure_url', 'quality_level', 'durability_level', 'eco_level',
'carbon_level', 'score_level', 'connection_method', 'construction_method',
'limit_condition', 'factory', 'factory_name', 'factory_short_name',
'factory_cooperation_mode', 'factory_cooperation_mode_display',
'factory_province', 'factory_city',
'brand', 'brand_name',
'status', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
def get_factory_cooperation_mode_display(self, obj):
return obj.factory.get_cooperation_mode_display() if obj.factory and obj.factory.cooperation_mode else None
def get_brochure_url(self, obj):
if obj.brochure:
request = self.context.get('request')
@ -85,25 +94,44 @@ class MaterialSerializer(serializers.ModelSerializer):
class MaterialListSerializer(serializers.ModelSerializer):
"""
材料列表序列化器简化版
材料列表序列化器完整字段版供列表页按需展示列
"""
factory_name = serializers.CharField(source='factory.factory_name', read_only=True)
factory_short_name = serializers.CharField(source='factory.short_name', read_only=True)
factory_cooperation_mode = serializers.CharField(source='factory.cooperation_mode', read_only=True, default=None)
factory_cooperation_mode_display = serializers.SerializerMethodField()
factory_province = serializers.CharField(source='factory.province', read_only=True, default=None)
factory_city = serializers.CharField(source='factory.city', read_only=True, default=None)
brand_name = serializers.CharField(source='brand.name', read_only=True)
major_category_display = serializers.CharField(source='get_major_category_display', read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
stage_display = serializers.CharField(source='get_stage_display', read_only=True)
importance_level_display = serializers.CharField(source='get_importance_level_display', read_only=True)
replace_type_display = serializers.CharField(source='get_replace_type_display', read_only=True)
class Meta:
model = Material
fields = ['id', 'name', 'major_category', 'major_category_display',
fields = ['id', 'name', 'major_category', 'major_category_display',
'material_category', 'material_subcategory', 'stage', 'stage_display',
'importance_level', 'importance_level_display', 'landing_project',
'contact_person', 'contact_phone', 'handler', 'remark', 'factory',
'factory_name', 'factory_short_name', 'brand', 'brand_name',
'contact_person', 'contact_phone', 'handler', 'remark',
'spec', 'standard',
'application_scene', 'application_desc',
'replace_type', 'replace_type_display',
'advantage', 'advantage_desc',
'connection_method', 'construction_method', 'limit_condition',
'cost_compare', 'cost_desc', 'cases',
'quality_level', 'durability_level', 'eco_level',
'carbon_level', 'score_level',
'factory', 'factory_name', 'factory_short_name',
'factory_cooperation_mode', 'factory_cooperation_mode_display',
'factory_province', 'factory_city',
'brand', 'brand_name',
'status', 'status_display']
def get_factory_cooperation_mode_display(self, obj):
return obj.factory.get_cooperation_mode_display() if obj.factory and obj.factory.cooperation_mode else None
class MaterialCategorySerializer(serializers.ModelSerializer):
"""

View File

@ -17,6 +17,7 @@ from rest_framework.parsers import MultiPartParser
from .models import Material, MaterialCategory, MaterialSubcategory
from .serializers import MaterialSerializer, MaterialListSerializer, MaterialCategorySerializer, MaterialSubcategorySerializer
from .importers import import_materials_plan_excel
from apps.factory.models import COOPERATION_MODE_CHOICES
def _join_choice_values(values, choices):
@ -149,7 +150,7 @@ class MaterialViewSet(ModelViewSet):
"""
根据用户角色过滤材料
"""
queryset = Material.objects.all().order_by('-created_at', '-id')
queryset = Material.objects.select_related('factory', 'brand').order_by('-created_at', '-id')
# 普通用户只能看到自己工厂的材料
if self.request.user.role != 'admin':
@ -180,10 +181,10 @@ class MaterialViewSet(ModelViewSet):
if major_category:
queryset = queryset.filter(major_category=major_category)
# 支持按材料分类过滤
# 支持按材料分类模糊过滤
material_category = self.request.query_params.get('material_category')
if material_category:
queryset = queryset.filter(material_category=material_category)
queryset = queryset.filter(material_category__icontains=material_category)
# 支持按材料子类过滤
material_subcategory = self.request.query_params.get('material_subcategory')
@ -200,6 +201,52 @@ class MaterialViewSet(ModelViewSet):
if advantage:
queryset = queryset.filter(advantage__contains=[advantage])
# 阶段
stage = self.request.query_params.get('stage')
if stage:
queryset = queryset.filter(stage=stage)
# 重要等级
importance_level = self.request.query_params.get('importance_level')
if importance_level:
queryset = queryset.filter(importance_level=importance_level)
# 供应商(显式 id 过滤)
factory = self.request.query_params.get('factory')
if factory:
queryset = queryset.filter(factory_id=factory)
# 合作模式
cooperation_mode = self.request.query_params.get('factory__cooperation_mode')
if cooperation_mode:
queryset = queryset.filter(factory__cooperation_mode=cooperation_mode)
# 落地项目(模糊)
landing_project = self.request.query_params.get('landing_project')
if landing_project:
queryset = queryset.filter(landing_project__icontains=landing_project)
# 成本比较区间
cost_gte = self.request.query_params.get('cost_compare__gte')
if cost_gte not in (None, ''):
queryset = queryset.filter(cost_compare__gte=cost_gte)
cost_lte = self.request.query_params.get('cost_compare__lte')
if cost_lte not in (None, ''):
queryset = queryset.filter(cost_compare__lte=cost_lte)
# 综合评分下限
score_gte = self.request.query_params.get('score_level__gte')
if score_gte not in (None, ''):
queryset = queryset.filter(score_level__gte=score_gte)
# 对接人 / 经办人
contact_person = self.request.query_params.get('contact_person')
if contact_person:
queryset = queryset.filter(contact_person__icontains=contact_person)
handler = self.request.query_params.get('handler')
if handler:
queryset = queryset.filter(handler__icontains=handler)
return queryset
def get_serializer_class(self):
@ -333,6 +380,7 @@ class MaterialViewSet(ModelViewSet):
'application_scene': Material.APPLICATION_SCENE_CHOICES,
'star_level': Material.STAR_LEVEL_CHOICES,
'status': Material.STATUS_CHOICES,
'cooperation_mode': COOPERATION_MODE_CHOICES,
})
@action(detail=False, methods=['get'], url_path='template')