You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DRF技术问询:提交JobPlanManager时如何创建/更新关联JobPlan

How to Handle JobPlan Creation/Update with Overlapping JobPlanManagers in DRF

Alright, let's break down how to implement this logic in Django REST Framework step by step. I've dealt with similar overlapping period scenarios before, so here's a practical approach that covers your use case:

1. Model Setup

First, let's define the core models to map your relationships clearly:

from django.db import models
from django.contrib.auth.models import User

class Job(models.Model):
    name = models.CharField(max_length=255)
    # Add any other fields relevant to your job (description, status, etc.)

class JobPlan(models.Model):
    # One-to-one with Job since each Job has exactly one plan
    job = models.OneToOneField(Job, on_delete=models.CASCADE, related_name="job_plan")
    start_date = models.DateField()
    end_date = models.DateField()

class JobPlanManager(models.Model):
    # Many managers can be linked to a single JobPlan
    job_plan = models.ForeignKey(JobPlan, on_delete=models.CASCADE, related_name="managers")
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    # Individual manager's responsibility period
    manager_start_date = models.DateField()
    manager_end_date = models.DateField()

2. Serializer Implementation

We'll create a serializer that accepts the job ID, user ID, and manager's period, while handling validation for valid date ranges:

from rest_framework import serializers
from .models import Job, JobPlan, JobPlanManager

class JobPlanManagerSerializer(serializers.ModelSerializer):
    # Expose job and user IDs for easy input
    job_id = serializers.PrimaryKeyRelatedField(
        queryset=Job.objects.all(),
        source="job_plan.job"
    )
    user_id = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all(),
        source="user"
    )

    class Meta:
        model = JobPlanManager
        fields = ["id", "job_id", "user_id", "manager_start_date", "manager_end_date"]

    def validate(self, data):
        # Ensure manager's start date doesn't come after end date
        if data["manager_start_date"] > data["manager_end_date"]:
            raise serializers.ValidationError(
                "Manager start date cannot be later than end date."
            )
        return data

3. View Logic (Core Business Logic)

We'll use a CreateAPIView (and extend to UpdateAPIView if needed) to handle the creation of JobPlanManager instances, with logic to create/update the associated JobPlan:

from rest_framework import generics
from django.db import transaction
from .models import JobPlan, JobPlanManager
from .serializers import JobPlanManagerSerializer

class JobPlanManagerCreateView(generics.CreateAPIView):
    queryset = JobPlanManager.objects.all()
    serializer_class = JobPlanManagerSerializer

    @transaction.atomic
    def perform_create(self, serializer):
        # Extract validated data
        job = serializer.validated_data["job_plan"]["job"]
        manager_start = serializer.validated_data["manager_start_date"]
        manager_end = serializer.validated_data["manager_end_date"]

        # Get or create the JobPlan for the Job
        job_plan, is_new = JobPlan.objects.get_or_create(job=job)

        # Update JobPlan dates to cover the new manager's period
        if is_new:
            # If it's a new plan, use the manager's dates directly
            job_plan.start_date = manager_start
            job_plan.end_date = manager_end
        else:
            # For existing plans, extend to the earliest start and latest end
            job_plan.start_date = min(job_plan.start_date, manager_start)
            job_plan.end_date = max(job_plan.end_date, manager_end)
        
        job_plan.save()

        # Link the new JobPlanManager to the updated JobPlan
        serializer.save(job_plan=job_plan)

Handling Updates to Existing JobPlanManagers

If you need to support updating a manager's period and automatically adjusting the JobPlan, extend the view to include UpdateAPIView and reuse the date logic:

class JobPlanManagerCreateUpdateView(generics.CreateAPIView, generics.UpdateAPIView):
    queryset = JobPlanManager.objects.all()
    serializer_class = JobPlanManagerSerializer

    @transaction.atomic
    def perform_create(self, serializer):
        self._sync_job_plan_dates(serializer)
        serializer.save(job_plan=self.job_plan)

    @transaction.atomic
    def perform_update(self, serializer):
        self._sync_job_plan_dates(serializer)
        serializer.save(job_plan=self.job_plan)

    def _sync_job_plan_dates(self, serializer):
        job = serializer.validated_data["job_plan"]["job"]
        manager_start = serializer.validated_data["manager_start_date"]
        manager_end = serializer.validated_data["manager_end_date"]

        job_plan, is_new = JobPlan.objects.get_or_create(job=job)

        # Calculate new date range
        new_start = manager_start if is_new else min(job_plan.start_date, manager_start)
        new_end = manager_end if is_new else max(job_plan.end_date, manager_end)

        job_plan.start_date = new_start
        job_plan.end_date = new_end
        job_plan.save()
        self.job_plan = job_plan

4. Extra: Adjust JobPlan When Managers Are Deleted

To keep the JobPlan dates accurate if a manager is removed, add a post-delete signal to recalculate the date range:

from django.db.models.signals import post_delete
from django.dispatch import receiver

@receiver(post_delete, sender=JobPlanManager)
def update_job_plan_after_manager_removal(sender, instance, **kwargs):
    job_plan = instance.job_plan
    remaining_managers = job_plan.managers.all()

    if remaining_managers.exists():
        # Recalculate to the earliest start and latest end of remaining managers
        new_start = min(m.manager_start_date for m in remaining_managers)
        new_end = max(m.manager_end_date for m in remaining_managers)
        job_plan.start_date = new_start
        job_plan.end_date = new_end
        job_plan.save()
    else:
        # Optional: Delete the JobPlan if no managers are left (adjust based on your needs)
        job_plan.delete()

Key Notes

  • The @transaction.atomic decorator ensures that the JobPlan update and JobPlanManager creation/update happen atomically, preventing partial data saves if something fails.
  • The logic automatically extends the JobPlan to cover all overlapping or extended manager periods, which matches your example where User 2 extends the job's timeline.

内容的提问来源于stack exchange,提问作者MarkD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:36:23