Python Django Rest - Return 具有其他字段的最低、最高和平均值的额外字段

Python Django Rest - Return extra field with lowest, highest and average values of some other field

我是 Django 和 API 的新手,我想使用 Django Rest Framework 创建一个基于 API 的 Django。

这是我想要做的:

年龄范围报告的终点:

curl -H 'Content-Type: application/json' localhost:8000/reports/employees/age/

回复:

{
    "younger": {
        "id": "1",
        "name": "Anakin Skywalker",
        "email": "skywalker@ssys.com.br",
        "department": "Architecture",
        "salary": "4000.00",
        "birth_date": "01-01-1983"},
    "older": {
        "id": "2",
        "name": "Obi-Wan Kenobi",
        "email": "kenobi@ssys.com.br",
        "department": "Back-End",
        "salary": "3000.00",
        "birth_date": "01-01-1977"},
    "average": "40.00"
}

工资范围报告的端点:

curl -H 'Content-Type: application/json' localhost:8000/reports/employees/salary/

回复:

{
    "lowest ": {
        "id": "2",
        "name": "Obi-Wan Kenobi",
        "email": "kenobi@ssys.com.br",
        "department": "Back-End",
        "salary": "3000.00",
        "birth_date": "01-01-1977"},
    "highest": {
        "id": "3",
        "name": "Leia Organa",
        "email": "organa@ssys.com.br",
        "department": "DevOps",
        "salary": "5000.00",
        "birth_date": "01-01-1980"},
    "average": "4000.00"
}

我有两个应用程序,employeesreports

这里是employees/models.py:

class Employee(models.Model):
    name = models.CharField(max_length=250, default='FirstName LastName')
    email = models.EmailField(max_length=250, default='employee@email.com')
    departament = models.CharField(max_length=250, default='Full-Stack')
    salary = models.DecimalField(max_digits=15, decimal_places=2, default=0)
    birth_date = models.DateField()

这里是employees/serializers.py:

class EmployeeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Employee
        fields = ['id', 'name', 'email', 'departament', 'salary', 'birth_date']

我不确定如何为我的报表应用程序创建视图和序列化程序。我该如何处理?

如何 return 一个额外的字段,在另一个字段的值之间进行计算?我应该在序列化程序中创建自定义字段吗?

通过阅读 docs 我发现我应该使用的查询应该是这样的:

Employee.objects.aggregate(Max("salary"), Min("salary"), Avg("salary"))

或者,例如薪水:

Employee.objects.all().order_by('salary')

但是我该如何使用它呢?

我不知道在哪里使用或如何去做,因为我对 APIs 和 Django Rest Framework 的理解仍然非常缺乏。

它应该放在我的 reports/view.py 中吗?或者在我的 employees/serializers.py?

我应该创建一个 reports/serializers.py 文件吗?

我需要 reports/model class 和 lowesthighestaverage 字段加上一个员工对象字段?

我是否应该在 reports/views.py 中覆盖 ReportSalaryListAPIView class 的 list() 函数? (这个class还不存在)

我很迷茫和困惑。请帮我指明正确的方向。

提前致谢。


编辑:

我的 employees/model.py 现在看起来像这样:

class Employee(models.Model):
    name = models.CharField(max_length=250, default='FirstName LastName')
    email = models.EmailField(max_length=250, default='employee@email.com')
    departament = models.CharField(max_length=250, default='Full-Stack')
    salary = models.DecimalField(max_digits=15, decimal_places=2, default=0)
    birth_date = models.DateField()

    @property
    def get_age(self):
        delta = relativedelta(self.birth_date.days, datetime.today()).years
        return delta

    def save(self, *args, **kwargs):
        self.age = self.get_age()
        super(Employee, self).save(*args, **kwargs)

我的employees/serializers.py现在:

class EmployeeSerializer(serializers.ModelSerializer):

    class Meta:
        model = Employee
        fields = ['id', 'name', 'email', 'departament', 'salary', 'birth_date', 'age']

我已经 运行 makemigrationsmigrate 命令与 manager.py,以防万一。

但是我现在 运行在尝试创建新员工时遇到了这个错误:

'datetime.date' object has no attribute 'days'

发生了什么事?

想法

不需要额外的字段来存储计算值,因为它们是根据数据库中的行计算的。

  1. 首先用聚合的方法求出min_salarymax_salaryavg_salary
  2. 使用 found min_salarymax_salary 查找员工。
  3. 实施序列化程序 class,序列化 highestlowestaverage 员工工资报告。
  4. 使用已实现的序列化程序 class.
  5. 序列化 lowesthighestavarage

代码

在我看来,执行逻辑的代码应该在另一层,通常称为服务层。

# employees/services.py
class EmployeeSalaryReport:
    def __init__(self, lowest, highest, average):
        self.lowest = lowest
        self.highest = highest
        self.average = average

def get_employee_salary_report():
    salary_report_dict = Employee.objects.aggregate(
        min_salary=Min("salary"),
        max_salary=Max("salary"),
        avg_salary=Avg("salary"),
    )
    lowest_salary_employee = Employee.objects.filter(
        salary=salary_report_dict.get("min_salary")
    ).first()
    highest_salary_employee = Employee.objects.filter(
        salary=salary_report_dict.get("max_salary")
    ).first()
    return EmployeeSalaryReport(
        lowest=lowest_salary_employee,
        highest=highest_salary_employee,
        average=salary_report_dict.get("avg_salary"),
    )
# employees/serializers.py
class EmployeeSalaryReportSerializer(serializers.Serializer):
    lowest = EmployeeSerializer()
    highest = EmployeeSerializer()
    average = serializers.FloatField()
# employees/views.py
from rest_framework import views
from rest_framework.response import Response

from employees.services import get_employee_salary_report
from employees.serializers import EmployeeSalaryReportSerializer


class ReportSalaryView(views.APIView):
    def get(self, request, *args, **kwargs):
        employee_salary_report = get_employee_salary_report()
        serializer = EmployeeSalaryReportSerializer(employee_salary_report)
        return Response(serializer.data)
# employees/urls.py
from django.urls import path

from employees.views import ReportSalaryView


urlpatterns = [
    ...  # other paths
    path("reports/employees/salary/", ReportSalaryView.as_view(), name="report-salary"),
]