如何在Django中按指定结构展示模型数据并完成薪资核算?
Hey there! Let's walk through how to build these two data views in Django—one grouped by date and worker, and another worker-centric view with all those calculated salary fields. I'll break it down step by step so it's easy to follow.
Since you mentioned your model structure matches a diagram, let's start with a realistic example you can tweak to fit your actual data:
from django.db import models class Worker(models.Model): name = models.CharField(max_length=100) designation = models.CharField(max_length=50) basic_monthly_salary = models.DecimalField(max_digits=10, decimal_places=2) epf_contribution_rate = models.DecimalField(max_digits=4, decimal_places=2, default=12.0) # % of salary esic_contribution_rate = models.DecimalField(max_digits=4, decimal_places=2, default=4.75) # % of salary class DailyWorkRecord(models.Model): worker = models.ForeignKey(Worker, on_delete=models.CASCADE, related_name='daily_records') work_date = models.DateField() regular_hours = models.DecimalField(max_digits=5, decimal_places=2) overtime_hours = models.DecimalField(max_digits=5, decimal_places=2, default=0) other_deductions = models.DecimalField(max_digits=10, decimal_places=2, default=0)
We'll group records by date first, then list all worker entries under each date. Logic stays in the view—templates are for display only!
Step 2.1: Build the View
from django.shortcuts import render from itertools import groupby from .models import DailyWorkRecord def date_worker_view(request): # Fetch records sorted by date (critical for groupby to work) all_records = DailyWorkRecord.objects.select_related('worker').order_by('work_date') # Group records by their work date grouped_records = groupby(all_records, key=lambda record: record.work_date) # Convert to a list of (date, record_list) pairs for easy template iteration date_groups = [(date, list(records)) for date, records in grouped_records] return render(request, 'date_worker_display.html', {'date_groups': date_groups})
Step 2.2: Template (date_worker_display.html)
<h1>按日期及劳工维度展示数据</h1> {% for date, records in date_groups %} <h2>{{ date|date:"Y年m月d日" }}</h2> <table border="1" cellpadding="8"> <thead> <tr> <th>劳工姓名</th> <th>岗位</th> <th>正常工时</th> <th>加班工时</th> </tr> </thead> <tbody> {% for record in records %} <tr> <td>{{ record.worker.name }}</td> <td>{{ record.worker.designation }}</td> <td>{{ record.regular_hours }}</td> <td>{{ record.overtime_hours }}</td> </tr> {% empty %} <tr><td colspan="4">当日无工作记录</td></tr> {% endfor %} </tbody> </table> <br> {% endfor %}
For this view, we need to compute Per Day Salary, Total Salary, Net Salary, EPF, and ESIC for each worker. We'll handle calculations in the Worker model (reusable logic!) or the view, then pass the computed data to the template.
Step 3.1: Add Calculation Methods to the Worker Model
Add property methods and a monthly calculation method to keep logic encapsulated:
class Worker(models.Model): # ... existing fields ... @property def per_day_salary(self): # Assume 26 working days per month (adjust to your company's policy) return self.basic_monthly_salary / 26 def get_monthly_pay_details(self, target_month, target_year): # Fetch all daily records for the worker in the target month/year monthly_records = self.daily_records.filter( work_date__month=target_month, work_date__year=target_year ) # Aggregate basic data total_working_days = monthly_records.count() total_overtime = sum(record.overtime_hours for record in monthly_records) total_other_deductions = sum(record.other_deductions for record in monthly_records) # Calculate core salary components hourly_rate = self.per_day_salary / 8 # Assume 8-hour workday regular_pay = self.per_day_salary * total_working_days overtime_pay = total_overtime * hourly_rate * 1.5 # 1.5x overtime rate total_salary = regular_pay + overtime_pay # Calculate statutory deductions epf_deduction = total_salary * (self.epf_contribution_rate / 100) # ESIC applies only if total salary is ≤ ₹21,000/month (adjust based on your region) esic_deduction = total_salary * (self.esic_contribution_rate / 100) if total_salary <= 21000 else 0 # Net salary = Total Salary - All Deductions net_salary = total_salary - (total_other_deductions + epf_deduction + esic_deduction) return { 'per_day_salary': self.per_day_salary, 'total_working_days': total_working_days, 'total_overtime': total_overtime, 'total_salary': total_salary, 'epf_deduction': epf_deduction, 'esic_deduction': esic_deduction, 'net_salary': net_salary }
Step 3.2: Build the Worker-Centric View
from django.shortcuts import render from datetime import datetime from .models import Worker def worker_centric_view(request): # Use current month/year by default (add GET params to let users select a month) current_month = datetime.now().month current_year = datetime.now().year all_workers = Worker.objects.all() worker_pay_data = [] # Fetch pay details for each worker for worker in all_workers: pay_details = worker.get_monthly_pay_details(current_month, current_year) worker_pay_data.append({ 'worker': worker, **pay_details }) return render(request, 'worker_centric_display.html', {'worker_pay_data': worker_pay_data})
Step 3.3: Template (worker_centric_display.html)
<h1>按劳工维度展示数据(含薪资计算)</h1> <table border="1" cellpadding="8"> <thead> <tr> <th>劳工姓名</th> <th>日薪</th> <th>工作天数</th> <th>总加班时长</th> <th>总薪资</th> <th>EPF扣除</th> <th>ESIC扣除</th> <th>净薪资</th> </tr> </thead> <tbody> {% for data in worker_pay_data %} <tr> <td>{{ data.worker.name }}</td> <td>₹{{ data.per_day_salary|floatformat:2 }}</td> <td>{{ data.total_working_days }}</td> <td>{{ data.total_overtime|floatformat:2 }}</td> <td>₹{{ data.total_salary|floatformat:2 }}</td> <td>₹{{ data.epf_deduction|floatformat:2 }}</td> <td>₹{{ data.esic_deduction|floatformat:2 }}</td> <td>₹{{ data.net_salary|floatformat:2 }}</td> </tr> {% empty %} <tr><td colspan="8">暂无劳工数据</td></tr> {% endfor %} </tbody> </table>
- Add a date picker in the worker-centric view to let users select different months/years (use GET parameters to pass
monthandyearto the view). - Use Django's
localizetemplate tag to format currency/numbers according to your region. - For large datasets, use Django's database-level aggregation (
annotate/aggregate) instead of Python loops to boost performance.
内容的提问来源于stack exchange,提问作者Ramananda Kairi

