Django中如何持续将保险合同剩余天数保存至数据库?
Looks like you're trying to build a car insurance contract system where policies can be paused/resumed, with remaining days calculated daily and stored. Let's break down the issues in your current code and fix this properly.
1. Fix Your Model Logic (The Core Issue)
Your current save() method has bugs (like a typo remainingdaysays) and incorrect date arithmetic (subtracting raw day values won't work for cross-month calculations). Let's rework the model to track necessary data for accurate remaining days:
First, update the Contrat model with additional fields to track suspension periods:
from datetime import timedelta, date from django.db import models from django.contrib.auth.models import User class Contrat(models.Model): Statut_contrat = ( ('Encours', 'Encours'), ('Suspendre', 'Suspendre'), ) numero_de_contrat = models.CharField(max_length=10, unique=True, null=False, blank=False) statut_assurance = models.CharField(max_length=15, choices=Statut_contrat) vehicule = models.ForeignKey(Vehicule, on_delete=models.CASCADE) Conducteur = models.CharField(max_length=200) utilisateur = models.ForeignKey(User, on_delete=models.CASCADE) nombre_de_mois = models.IntegerField() montant_du_contrat = models.DecimalField(max_digits=10, decimal_places=2) categorie = models.ForeignKey(Categorie_Contrat, on_delete=models.CASCADE) created = models.DateField(auto_now_add=True) modified = models.DateField(auto_now=True) active = models.BooleanField(default=True) remainingdays = models.IntegerField(null=True) # New fields for suspension tracking expiry_date = models.DateField(null=True) # Contract end date total_suspension_days = models.IntegerField(default=0) # Total days paused suspension_start_date = models.DateField(null=True, blank=True) # When pause started def __str__(self): return self.numero_de_contrat def activeStatus(self): # Update active status based on remaining days return self.remainingdays > 0 if self.remainingdays is not None else True def save(self, *args, **kwargs): # Set expiry date on first save (if not already set) if not self.expiry_date: # Add nombre_de_mois months to created date (handle edge cases for month transitions) month_add = self.created.month + self.nombre_de_mois year_add = self.created.year + (month_add // 12) month_final = month_add % 12 or 12 # Handle days for months with fewer days (e.g., Feb) try: self.expiry_date = self.created.replace(year=year_add, month=month_final) except ValueError: # If day doesn't exist in target month, use last day of month self.expiry_date = self.created.replace(year=year_add, month=month_final, day=1) - timedelta(days=1) # Handle status transitions if self.statut_assurance == 'Suspendre' and not self.suspension_start_date: # Start of suspension: record the date self.suspension_start_date = date.today() elif self.statut_assurance == 'Encours' and self.suspension_start_date: # End of suspension: calculate days paused and add to total pause_duration = (date.today() - self.suspension_start_date).days self.total_suspension_days += pause_duration self.suspension_start_date = None # Reset start date # Calculate remaining days if self.expiry_date: # Adjust current date by subtracting total suspension days (since those days don't count) adjusted_current_date = date.today() - timedelta(days=self.total_suspension_days) remaining = (self.expiry_date - adjusted_current_date).days self.remainingdays = max(0, remaining) # Don't let it go negative # Update active status based on remaining days self.active = self.remainingdays > 0 super(Contrat, self).save(*args, **kwargs)
2. Simplify Your View Logic
Your current view has redundant calculations and incorrectly updates all contracts with the same numero_de_contrat (which should be unique). Let's clean it up:
from django.shortcuts import get_object_or_404, redirect, render from .models import Contrat from .forms import ContratAddFrom # Assume your form is defined correctly def ContratUpdate(request, id=None): detail = get_object_or_404(Contrat, id=id) if request.method == 'POST': form = ContratAddFrom(request.POST, instance=detail) if form.is_valid(): form.save() # The model's save() method handles remainingdays automatically return redirect('assurance_auto:Contrat_Liste') else: form = ContratAddFrom(instance=detail) return render(request, 'auto/UpdateContrat.html', {'form': form})
3. Implement Daily Remaining Days Update
Since you need to update remainingdays daily (even if no user edits the contract), you'll need a scheduled task. Here's a simple implementation using django-crontab:
- Install the package:
pip install django-crontab
- Add it to
INSTALLED_APPSin your settings.py:
INSTALLED_APPS = [ # ... your other apps 'django_crontab', ]
- Create a management command to update all contracts:
Createyourapp/management/commands/update_remaining_days.py:
from django.core.management.base import BaseCommand from datetime import date, timedelta from yourapp.models import Contrat class Command(BaseCommand): help = 'Updates remaining days for all active contracts' def handle(self, *args, **kwargs): contracts = Contrat.objects.filter(active=True) for contrat in contracts: # Recalculate remaining days (matches model save logic) if contrat.expiry_date: adjusted_current_date = date.today() - timedelta(days=contrat.total_suspension_days) remaining = (contrat.expiry_date - adjusted_current_date).days contrat.remainingdays = max(0, remaining) contrat.active = contrat.remainingdays > 0 contrat.save(update_fields=['remainingdays', 'active']) self.stdout.write(self.style.SUCCESS('Successfully updated remaining days'))
- Add the cron job to settings.py (runs daily at midnight):
CRONJOBS = [ ('0 0 * * *', 'yourapp.management.commands.update_remaining_days'), ]
- Activate the cron job:
python manage.py crontab add
Key Notes
- Date Accuracy: The expiry date calculation handles edge cases like months with different day counts (e.g., January to February). Adjust if you need stricter business rules (e.g., exact 30-day months).
- Suspension Handling: When a contract is paused, we stop counting days by adding the pause duration to
total_suspension_days, which adjusts the "effective" current date for remaining days calculation. - Daily Updates: The cron job ensures remaining days are updated even when no user interacts with the contract.
内容的提问来源于stack exchange,提问作者Mohamed Abdillah

