Python Django Rest - вернуть дополнительное поле с самым низким, самым высоким и средним значениями какого-либо другого поля

Я новичок в Django и API в целом, и я хочу создать API на основе Django с использованием Django Rest Framework.

Вот что я хочу сделать:

От конечной точки до отчета по возрастному диапазону:

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

Ответ:

{
    "younger": {
        "id": "1",
        "name": "Anakin Skywalker",
        "email": "[email protected]",
        "department": "Architecture",
        "salary": "4000.00",
        "birth_date": "01-01-1983"},
    "older": {
        "id": "2",
        "name": "Obi-Wan Kenobi",
        "email": "[email protected]",
        "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": "[email protected]",
        "department": "Back-End",
        "salary": "3000.00",
        "birth_date": "01-01-1977"},
    "highest": {
        "id": "3",
        "name": "Leia Organa",
        "email": "[email protected]",
        "department": "DevOps",
        "salary": "5000.00",
        "birth_date": "01-01-1980"},
    "average": "4000.00"
}

У меня есть два приложения, employees и reports.

Вот сотрудники/models.py:

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

Вот сотрудники/serializers.py:

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

Я не знаю, как создавать представления и сериализаторы для своего приложения для отчетов. Как мне подойти к этому?

Как я могу вернуть дополнительное поле с расчетом между значениями другого поля? Должен ли я создать настраиваемое поле в моем сериализаторе?

Прочитав документы, я понял, что запрос, который я должен использовать, должен выглядеть примерно так:

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

Или, например, для зарплаты:

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

Но как мне его использовать?

Я не знаю, где это использовать и как это сделать, поскольку мое понимание API и Django Rest Framework все еще очень слабое.

Должен ли он идти в моем отчеты/view.py? Или в моем сотрудники/serializers.py?

Должен ли я создать файл отчеты/serializers.py?

Нужен ли мне класс отчеты/модель с полями lowest, highest, average плюс поле объекта сотрудника?

Должен ли я переопределить функцию list() моего класса ReportSalaryListAPIView в моем отчеты/views.py? (Этот класс еще не существует)

Я очень потерян и сбит с толку. Пожалуйста, помогите указать мне правильное направление.

Заранее спасибо.


Редактировать:

Мой сотрудники /model.py теперь выглядит так:

class Employee(models.Model):
    name = models.CharField(max_length=250, default='FirstName LastName')
    email = models.EmailField(max_length=250, default='[email protected]')
    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)

Мой сотрудники/serializers.py сейчас:

class EmployeeSerializer(serializers.ModelSerializer):

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

На всякий случай я запустил команды makemigrations и migrate с manager.py.

Но теперь я сталкиваюсь с этой ошибкой при попытке создать нового сотрудника:

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

Что происходит?

🤔 А знаете ли вы, что...
С Python можно создавать роботов и автоматизированные системы с использованием библиотеки Raspberry Pi.


36
1

Ответ:

Решено

Идея

Никаких дополнительных полей для хранения рассчитанных значений не требуется, поскольку они вычисляются на основе строк в базе данных.

  1. Сначала вы находите min_salary, max_salary и avg_salary, используя метод агрегата.
  2. Используйте найденные min_salary и max_salary, чтобы найти сотрудников.
  3. Реализуйте класс сериализатора, который сериализует highest, lowest и average отчет о зарплате сотрудников.
  4. Сериализуйте lowest, highest и avarage с помощью реализованного класса сериализатора.

Код

На мой взгляд, код для выполнения логики должен быть на другом уровне, обычно называемом сервисным.

# 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"),
]