- FertilizationPlanにcalc_settings JSONFieldを追加(migration 0004) - 編集画面を開くと前回の計算方式・パラメータが復元される - 「未入力圃場のみ」チェックで既存値を保持したまま新規圃場だけ計算可能 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
219 lines
8.5 KiB
Python
219 lines
8.5 KiB
Python
from rest_framework import serializers
|
||
from .models import Fertilizer, FertilizationPlan, FertilizationEntry, DistributionPlan, DistributionGroup, DistributionGroupField
|
||
|
||
|
||
class FertilizerSerializer(serializers.ModelSerializer):
|
||
class Meta:
|
||
model = Fertilizer
|
||
fields = '__all__'
|
||
|
||
|
||
class FertilizationEntrySerializer(serializers.ModelSerializer):
|
||
field_name = serializers.CharField(source='field.name', read_only=True)
|
||
field_area_tan = serializers.DecimalField(
|
||
source='field.area_tan', max_digits=6, decimal_places=4, read_only=True
|
||
)
|
||
fertilizer_name = serializers.CharField(source='fertilizer.name', read_only=True)
|
||
|
||
class Meta:
|
||
model = FertilizationEntry
|
||
fields = ['id', 'field', 'field_name', 'field_area_tan', 'fertilizer', 'fertilizer_name', 'bags']
|
||
|
||
|
||
class FertilizationPlanSerializer(serializers.ModelSerializer):
|
||
variety_name = serializers.SerializerMethodField()
|
||
crop_name = serializers.SerializerMethodField()
|
||
entries = FertilizationEntrySerializer(many=True, read_only=True)
|
||
field_count = serializers.SerializerMethodField()
|
||
fertilizer_count = serializers.SerializerMethodField()
|
||
|
||
class Meta:
|
||
model = FertilizationPlan
|
||
fields = [
|
||
'id', 'name', 'year', 'variety', 'variety_name', 'crop_name',
|
||
'calc_settings', 'entries', 'field_count', 'fertilizer_count', 'created_at', 'updated_at'
|
||
]
|
||
|
||
def get_variety_name(self, obj):
|
||
return obj.variety.name
|
||
|
||
def get_crop_name(self, obj):
|
||
return obj.variety.crop.name
|
||
|
||
def get_field_count(self, obj):
|
||
return obj.entries.values('field').distinct().count()
|
||
|
||
def get_fertilizer_count(self, obj):
|
||
return obj.entries.values('fertilizer').distinct().count()
|
||
|
||
|
||
class FertilizationPlanWriteSerializer(serializers.ModelSerializer):
|
||
"""保存用(entries を一括で受け取る)"""
|
||
entries = serializers.ListField(child=serializers.DictField(), write_only=True, required=False)
|
||
|
||
class Meta:
|
||
model = FertilizationPlan
|
||
fields = ['id', 'name', 'year', 'variety', 'calc_settings', 'entries']
|
||
|
||
def create(self, validated_data):
|
||
entries_data = validated_data.pop('entries', [])
|
||
plan = FertilizationPlan.objects.create(**validated_data)
|
||
self._save_entries(plan, entries_data)
|
||
return plan
|
||
|
||
def update(self, instance, validated_data):
|
||
entries_data = validated_data.pop('entries', None)
|
||
for attr, value in validated_data.items():
|
||
setattr(instance, attr, value)
|
||
instance.save()
|
||
if entries_data is not None:
|
||
instance.entries.all().delete()
|
||
self._save_entries(instance, entries_data)
|
||
return instance
|
||
|
||
def _save_entries(self, plan, entries_data):
|
||
for entry in entries_data:
|
||
FertilizationEntry.objects.create(
|
||
plan=plan,
|
||
field_id=entry['field_id'],
|
||
fertilizer_id=entry['fertilizer_id'],
|
||
bags=entry['bags'],
|
||
)
|
||
|
||
|
||
# ─── 分配計画 ────────────────────────────────────────────────────────────
|
||
|
||
class DistributionGroupFieldSerializer(serializers.ModelSerializer):
|
||
id = serializers.IntegerField(source='field.id', read_only=True)
|
||
name = serializers.CharField(source='field.name', read_only=True)
|
||
area_tan = serializers.DecimalField(
|
||
source='field.area_tan', max_digits=6, decimal_places=4, read_only=True
|
||
)
|
||
|
||
class Meta:
|
||
model = DistributionGroupField
|
||
fields = ['id', 'name', 'area_tan']
|
||
|
||
|
||
class DistributionGroupReadSerializer(serializers.ModelSerializer):
|
||
fields = DistributionGroupFieldSerializer(source='field_assignments', many=True, read_only=True)
|
||
|
||
class Meta:
|
||
model = DistributionGroup
|
||
fields = ['id', 'name', 'order', 'fields']
|
||
|
||
|
||
class FertilizationPlanForDistributionSerializer(serializers.ModelSerializer):
|
||
"""分配計画詳細に埋め込む施肥計画情報(肥料一覧・entries 含む)"""
|
||
variety_name = serializers.SerializerMethodField()
|
||
crop_name = serializers.SerializerMethodField()
|
||
fertilizers = serializers.SerializerMethodField()
|
||
entries = serializers.SerializerMethodField()
|
||
|
||
class Meta:
|
||
model = FertilizationPlan
|
||
fields = ['id', 'name', 'year', 'variety_name', 'crop_name', 'fertilizers', 'entries']
|
||
|
||
def get_variety_name(self, obj):
|
||
return obj.variety.name
|
||
|
||
def get_crop_name(self, obj):
|
||
return obj.variety.crop.name
|
||
|
||
def get_fertilizers(self, obj):
|
||
fert_ids = obj.entries.values_list('fertilizer_id', flat=True).distinct()
|
||
from .models import Fertilizer as F
|
||
fertilizers = F.objects.filter(id__in=fert_ids).order_by('name')
|
||
return [{'id': f.id, 'name': f.name} for f in fertilizers]
|
||
|
||
def get_entries(self, obj):
|
||
return [
|
||
{'field': e.field_id, 'fertilizer': e.fertilizer_id, 'bags': str(e.bags)}
|
||
for e in obj.entries.all()
|
||
]
|
||
|
||
|
||
class DistributionPlanListSerializer(serializers.ModelSerializer):
|
||
fertilization_plan_id = serializers.IntegerField(source='fertilization_plan.id', read_only=True)
|
||
fertilization_plan_name = serializers.CharField(source='fertilization_plan.name', read_only=True)
|
||
year = serializers.IntegerField(source='fertilization_plan.year', read_only=True)
|
||
variety_name = serializers.SerializerMethodField()
|
||
crop_name = serializers.SerializerMethodField()
|
||
group_count = serializers.SerializerMethodField()
|
||
field_count = serializers.SerializerMethodField()
|
||
|
||
class Meta:
|
||
model = DistributionPlan
|
||
fields = [
|
||
'id', 'name', 'fertilization_plan_id', 'fertilization_plan_name',
|
||
'year', 'variety_name', 'crop_name', 'group_count', 'field_count',
|
||
'created_at', 'updated_at',
|
||
]
|
||
|
||
def get_variety_name(self, obj):
|
||
return obj.fertilization_plan.variety.name
|
||
|
||
def get_crop_name(self, obj):
|
||
return obj.fertilization_plan.variety.crop.name
|
||
|
||
def get_group_count(self, obj):
|
||
return obj.groups.count()
|
||
|
||
def get_field_count(self, obj):
|
||
return obj.distributiongroupfield_set.count()
|
||
|
||
|
||
class DistributionPlanReadSerializer(serializers.ModelSerializer):
|
||
fertilization_plan = FertilizationPlanForDistributionSerializer(read_only=True)
|
||
groups = DistributionGroupReadSerializer(many=True, read_only=True)
|
||
unassigned_fields = serializers.SerializerMethodField()
|
||
|
||
class Meta:
|
||
model = DistributionPlan
|
||
fields = ['id', 'name', 'fertilization_plan', 'groups', 'unassigned_fields', 'created_at', 'updated_at']
|
||
|
||
def get_unassigned_fields(self, obj):
|
||
assigned_ids = obj.distributiongroupfield_set.values_list('field_id', flat=True)
|
||
plan_field_ids = obj.fertilization_plan.entries.values_list('field_id', flat=True).distinct()
|
||
from apps.fields.models import Field as F
|
||
unassigned = F.objects.filter(id__in=plan_field_ids).exclude(id__in=assigned_ids).order_by('display_order', 'id')
|
||
return [{'id': f.id, 'name': f.name, 'area_tan': str(f.area_tan)} for f in unassigned]
|
||
|
||
|
||
class DistributionPlanWriteSerializer(serializers.ModelSerializer):
|
||
fertilization_plan_id = serializers.IntegerField(write_only=True)
|
||
groups = serializers.ListField(child=serializers.DictField(), write_only=True, required=False)
|
||
|
||
class Meta:
|
||
model = DistributionPlan
|
||
fields = ['id', 'name', 'fertilization_plan_id', 'groups']
|
||
|
||
def create(self, validated_data):
|
||
groups_data = validated_data.pop('groups', [])
|
||
plan = DistributionPlan.objects.create(**validated_data)
|
||
self._save_groups(plan, groups_data)
|
||
return plan
|
||
|
||
def update(self, instance, validated_data):
|
||
groups_data = validated_data.pop('groups', None)
|
||
instance.name = validated_data.get('name', instance.name)
|
||
instance.save()
|
||
if groups_data is not None:
|
||
instance.groups.all().delete()
|
||
self._save_groups(instance, groups_data)
|
||
return instance
|
||
|
||
def _save_groups(self, plan, groups_data):
|
||
for g_data in groups_data:
|
||
group = DistributionGroup.objects.create(
|
||
distribution_plan=plan,
|
||
name=g_data['name'],
|
||
order=g_data.get('order', 0),
|
||
)
|
||
for field_id in g_data.get('field_ids', []):
|
||
DistributionGroupField.objects.create(
|
||
distribution_plan=plan,
|
||
group=group,
|
||
field_id=field_id,
|
||
)
|