基于Django的自由职业者平台计时功能实现技术问询
实现自由职业者计时功能的Django方案
针对你的需求,我会一步步拆解实现思路,从模型设计到视图、前端的完整流程:
1. 新增计时记录模型(TimeEntry)
首先需要创建一个专门的模型来存储每次计时的起止时间戳,直接关联到Freelancer模型。把这个模型放在你的models.py里(和现有User、Freelancer等模型同文件即可):
from django.db import models from django.contrib.auth.models import AbstractUser from django.utils import timezone # 你的现有User、Freelancer、Hirer模型... class TimeEntry(models.Model): freelancer = models.ForeignKey(Freelancer, on_delete=models.CASCADE, related_name="time_entries") start_time = models.DateTimeField(default=timezone.now) end_time = models.DateTimeField(null=True, blank=True) # 可选:如果后续需要关联具体项目,可以加外键到Project模型 # project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True) @property def duration(self): """计算单次计时的时长(返回小时数)""" if self.end_time: delta = self.end_time - self.start_time return delta.total_seconds() / 3600 return 0 def __str__(self): return f"{self.freelancer.user.username} - {self.start_time.strftime('%Y-%m-%d %H:%M')}"
这个模型的核心是start_time(开始计时自动记录当前时间)和end_time(停止计时才填充),duration属性用来快速计算单次工作时长,方便后续报表统计。
2. 编写计时操作的视图
接下来需要两个视图函数,分别处理「开始计时」和「停止计时」的请求,同时限制只有登录的自由职业者才能访问:
# views.py from django.shortcuts import get_object_or_404, redirect from django.views.generic import View from django.contrib.auth.mixins import LoginRequiredMixin from .models import Freelancer, TimeEntry from django.utils import timezone class StartTimerView(LoginRequiredMixin, View): def post(self, request): # 获取当前登录用户对应的Freelancer实例 freelancer = get_object_or_404(Freelancer, user=request.user) # 先检查是否已有未结束的计时,避免重复创建 active_entry = freelancer.time_entries.filter(end_time__isnull=True).exists() if not active_entry: TimeEntry.objects.create(freelancer=freelancer) return redirect('freelancer_dashboard') # 替换成你的仪表盘URL名称 class StopTimerView(LoginRequiredMixin, View): def post(self, request): freelancer = get_object_or_404(Freelancer, user=request.user) # 找到该自由职业者最近的一条未结束的计时记录 active_entry = freelancer.time_entries.filter(end_time__isnull=True).last() if active_entry: active_entry.end_time = timezone.now() active_entry.save() return redirect('freelancer_dashboard')
记得在urls.py里配置这两个视图的路由:
# urls.py from django.urls import path from .views import StartTimerView, StopTimerView urlpatterns = [ # 你的其他路由... path('timer/start/', StartTimerView.as_view(), name='start_timer'), path('timer/stop/', StopTimerView.as_view(), name='stop_timer'), ]
3. 前端添加「开始/停止」按钮
在自由职业者的仪表盘模板里,添加两个按钮,用AJAX发送POST请求(避免页面刷新,提升体验):
<!-- freelancer_dashboard.html --> {% if user.is_freelancer %} {% with active_entry=user.freelancer.time_entries.filter(end_time__isnull=True).last %} {% if active_entry %} <button id="stop-timer-btn" class="btn btn-danger">停止计时</button> {% else %} <button id="start-timer-btn" class="btn btn-success">开始计时</button> {% endif %} {% endwith %} <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> $(document).ready(function() { // 开始计时 $('#start-timer-btn').click(function() { $.post("{% url 'start_timer' %}", function() { location.reload(); }); }); // 停止计时 $('#stop-timer-btn').click(function() { $.post("{% url 'stop_timer' %}", function() { location.reload(); }); }); }); </script> {% endif %}
4. 周工作报表计算
要生成周报表,可以通过聚合查询统计每周的总时长和报酬:
# 在视图或模型方法中实现 from django.db.models import Sum from django.utils import timezone from datetime import timedelta def get_weekly_report(freelancer): # 按周一到周日计算本周范围(可根据需求调整为周日到周六) today = timezone.now().date() start_of_week = today - timedelta(days=today.weekday()) end_of_week = start_of_week + timedelta(days=6) # 过滤本周已完成的计时记录 weekly_entries = freelancer.time_entries.filter( start_time__date__gte=start_of_week, start_time__date__lte=end_of_week, end_time__isnull=False ) total_hours = round(sum(entry.duration for entry in weekly_entries), 2) total_earnings = round(total_hours * freelancer.hourly_rate, 2) return { 'start_date': start_of_week, 'end_date': end_of_week, 'total_hours': total_hours, 'total_earnings': total_earnings, 'entries': weekly_entries }
在视图里调用这个函数,把数据传递给模板即可渲染出周报表。
注意事项
- 权限加固:可以在视图里额外检查
request.user.is_freelancer,避免非自由职业者访问计时功能。 - 时区配置:确保
settings.py里的TIME_ZONE设置正确(比如'Asia/Shanghai'),避免时间戳出现偏差。 - 异常处理:可以在视图里添加try-except块,处理计时操作中的意外情况(比如数据库写入失败)。
内容的提问来源于stack exchange,提问作者Urvin
相关产品推荐
相关产品推荐

