Day 5 の作付け計画API実装が完了しました。
実装内容 バグ修正 - fields/views.py: OfficialChusakanField → OfficialChusankanField init_crops コマンド ✅ python manage.py init_crops 水稲: 5 varieties 大豆: 3 varieties 小麦: 2 varieties そば: 2 varieties とうきび: 1 varieties serializers.py - CropSerializer - 作物マスタ - VarietySerializer - 品種マスタ - PlanSerializer - 作付け計画(crop_name, variety_name, field_name 付き) views.py - CropViewSet, VarietyViewSet, PlanViewSet - アクション: summary, copy_from_previous_year, get_crops_with_varieties API エンドポイント - /api/plans/crops/ - 作物一覧 - /api/plans/varieties/ - 品種一覧 - /api/plans/ - 作付け計画CRUD - /api/plans/summary/?year=2025 - 集計 テスト結果 GET /api/plans/crops/ → ✅ GET /api/plans/ → ✅ (空配列)
This commit is contained in:
@@ -1,3 +1,85 @@
|
||||
from django.shortcuts import render
|
||||
from rest_framework import viewsets, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from django.db.models import Sum
|
||||
from .models import Crop, Variety, Plan
|
||||
from .serializers import CropSerializer, VarietySerializer, PlanSerializer
|
||||
|
||||
# Create your views here.
|
||||
|
||||
class CropViewSet(viewsets.ModelViewSet):
|
||||
queryset = Crop.objects.all()
|
||||
serializer_class = CropSerializer
|
||||
|
||||
|
||||
class VarietyViewSet(viewsets.ModelViewSet):
|
||||
queryset = Variety.objects.all()
|
||||
serializer_class = VarietySerializer
|
||||
|
||||
|
||||
class PlanViewSet(viewsets.ModelViewSet):
|
||||
queryset = Plan.objects.all()
|
||||
serializer_class = PlanSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Plan.objects.all()
|
||||
year = self.request.query_params.get('year')
|
||||
if year:
|
||||
queryset = queryset.filter(year=year)
|
||||
return queryset
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def summary(self, request):
|
||||
year = request.query_params.get('year')
|
||||
if not year:
|
||||
return Response({'error': 'year parameter is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
plans = Plan.objects.filter(year=year)
|
||||
total_area = plans.aggregate(total=Sum('field__area_tan'))['total'] or 0
|
||||
|
||||
by_crop = {}
|
||||
for plan in plans:
|
||||
crop_name = plan.crop.name
|
||||
if crop_name not in by_crop:
|
||||
by_crop[crop_name] = {
|
||||
'crop': crop_name,
|
||||
'count': 0,
|
||||
'area': 0
|
||||
}
|
||||
by_crop[crop_name]['count'] += 1
|
||||
by_crop[crop_name]['area'] += float(plan.field.area_tan)
|
||||
|
||||
return Response({
|
||||
'year': int(year),
|
||||
'total_plans': plans.count(),
|
||||
'total_area': float(total_area),
|
||||
'by_crop': list(by_crop.values())
|
||||
})
|
||||
|
||||
@action(detail=False, methods=['post'])
|
||||
def copy_from_previous_year(self, request):
|
||||
from_year = request.data.get('from_year')
|
||||
to_year = request.data.get('to_year')
|
||||
|
||||
if not from_year or not to_year:
|
||||
return Response({'error': 'from_year and to_year are required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
previous_plans = Plan.objects.filter(year=from_year)
|
||||
new_plans = []
|
||||
|
||||
for plan in previous_plans:
|
||||
new_plans.append(Plan(
|
||||
field=plan.field,
|
||||
year=to_year,
|
||||
crop=plan.crop,
|
||||
variety=plan.variety,
|
||||
notes=plan.notes
|
||||
))
|
||||
|
||||
Plan.objects.bulk_create(new_plans, ignore_conflicts=True)
|
||||
|
||||
return Response({'message': f'Copied {len(new_plans)} plans from {from_year} to {to_year}'})
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def get_crops_with_varieties(self, request):
|
||||
crops = Crop.objects.prefetch_related('varieties').all()
|
||||
return Response(CropSerializer(crops, many=True).data)
|
||||
|
||||
Reference in New Issue
Block a user