Django中如何将DurationField输入格式化为分:秒:毫秒
解决方案
方案一:用整数字段存储毫秒(推荐)
这种方式计算个人最佳、时间差更高效,也更容易控制输入输出格式。
1. 修改模型
把DurationField替换为PositiveIntegerField,直接存储毫秒数,同时添加格式化方法用于展示:
from django.db import models from django.utils.translation import gettext_lazy as _ import timezone class SwimTime(models.Model): swimmer = models.ForeignKey(Swimmer, on_delete=models.CASCADE) # 存储毫秒数,默认值对应01:01:001(1分1秒1毫秒=61*1000+1=61001) time_ms = models.PositiveIntegerField(_('Time (ms)'), default=61001) distance = models.PositiveIntegerField(_('Distance'), null=False, default=50) strokeType = models.CharField(_('Stroke Type'), max_length=20, choices=strokeTypes, default='FC') date = models.DateField(_('Date Recorded'), default=timezone.now) def save(self, *args, **kwargs): self.full_clean() return super().save(*args, **kwargs) # 将毫秒转换为MM:SS:mmm格式的字符串 def get_formatted_time(self): total_ms = self.time_ms minutes = total_ms // 60000 remaining_ms = total_ms % 60000 seconds = remaining_ms // 1000 ms = remaining_ms % 1000 # 格式化补零,确保是两位分、两位秒、三位毫秒 return f"{minutes:02d}:{seconds:02d}:{ms:03d}"
2. 自定义表单强制输入格式
创建表单时自定义输入字段,验证用户输入的MM:SS:mmm格式,并转换为毫秒数存储:
from django import forms from .models import SwimTime import re from django.utils.translation import gettext_lazy as _ class SwimTimeForm(forms.ModelForm): # 自定义输入字段,提示用户输入格式 time = forms.CharField(label=_('Time'), help_text=_('请输入MM:SS:mmm格式的时间(例如00:00:000)')) class Meta: model = SwimTime fields = ['swimmer', 'time', 'distance', 'strokeType', 'date'] def clean_time(self): time_str = self.cleaned_data.get('time') # 验证格式是否为两位数字:两位数字:三位数字 pattern = r'^\d{2}:\d{2}:\d{3}$' if not re.match(pattern, time_str): raise forms.ValidationError(_('时间格式无效,请使用MM:SS:mmm格式')) # 拆分时间组件并验证合法性 minutes, seconds, ms = map(int, time_str.split(':')) if seconds >= 60 or ms >= 1000: raise forms.ValidationError(_('秒数不能超过59,毫秒数不能超过999')) # 转换为总毫秒数 total_ms = minutes * 60000 + seconds * 1000 + ms return total_ms def save(self, commit=True): instance = super().save(commit=False) # 将转换后的毫秒数赋值给模型字段 instance.time_ms = self.cleaned_data['time'] if commit: instance.save() return instance
3. DetailView中展示格式化时间
在详情页模板中,调用模型的get_formatted_time方法展示格式:
<!-- swimtime_detail.html --> <h1>单圈时间详情</h1> <p>游泳者:{{ object.swimmer }}</p> <p>单圈时间:{{ object.get_formatted_time }}</p> <p>距离:{{ object.distance }}米</p> <p>泳姿:{{ object.get_strokeType_display }}</p> <p>记录日期:{{ object.date }}</p>
计算相关操作
- 个人最佳成绩:直接按
time_ms字段排序取最小值best_time = SwimTime.objects.filter(swimmer=target_swimmer).order_by('time_ms').first() - 单圈时间差:直接用两个对象的
time_ms字段相减time_diff_ms = swim_time1.time_ms - swim_time2.time_ms # 转换为可读格式的话可以复用get_formatted_time的逻辑
方案二:保留DurationField(兼容原有模型)
如果不想修改模型字段类型,可以通过自定义表单和展示方法来实现需求:
1. 模型添加格式化方法
在原有模型中添加方法,将timedelta转换为MM:SS:mmm格式:
from datetime import timedelta class SwimTime(models.Model): # 原有字段不变 swimmer = models.ForeignKey(Swimmer, on_delete=models.CASCADE) time = models.DurationField(_('Time'), default=timedelta(minutes=1, seconds=1, milliseconds=1)) distance = models.PositiveIntegerField(_('Distance'), null=False, default=50) strokeType = models.CharField(_('Stroke Type'), max_length=20, choices=strokeTypes, default='FC') date = models.DateField(_('Date Recorded'), default=timezone.now) def save(self, *args, **kwargs): self.full_clean() return super().save(*args, **kwargs) # 将DurationField的timedelta转换为MM:SS:mmm格式 def get_formatted_time(self): total_seconds = self.time.total_seconds() minutes = int(total_seconds // 60) remaining_seconds = total_seconds % 60 seconds = int(remaining_seconds) ms = int((remaining_seconds - seconds) * 1000) return f"{minutes:02d}:{seconds:02d}:{ms:03d}"
2. 自定义表单处理输入
同样创建自定义表单,将用户输入的MM:SS:mmm转换为timedelta:
from django import forms from .models import SwimTime import re from datetime import timedelta from django.utils.translation import gettext_lazy as _ class SwimTimeForm(forms.ModelForm): time = forms.CharField(label=_('Time'), help_text=_('请输入MM:SS:mmm格式的时间(例如00:00:000)')) class Meta: model = SwimTime fields = ['swimmer', 'time', 'distance', 'strokeType', 'date'] def clean_time(self): time_str = self.cleaned_data.get('time') pattern = r'^\d{2}:\d{2}:\d{3}$' if not re.match(pattern, time_str): raise forms.ValidationError(_('时间格式无效,请使用MM:SS:mmm格式')) minutes, seconds, ms = map(int, time_str.split(':')) if seconds >= 60 or ms >= 1000: raise forms.ValidationError(_('秒数不能超过59,毫秒数不能超过999')) return timedelta(minutes=minutes, seconds=seconds, milliseconds=ms)
3. DetailView展示和计算
- 展示方式和方案一相同,调用
get_formatted_time方法 - 计算操作:
- 个人最佳:
SwimTime.objects.filter(swimmer=target_swimmer).order_by('time').first() - 时间差:
time_diff = swim_time1.time - swim_time2.time,转换为毫秒的话用time_diff.total_seconds() * 1000
- 个人最佳:
内容的提问来源于stack exchange,提问作者Ruairi Glackin
相关产品推荐
相关产品推荐

