Вычисляемые поля Django в пользовательском наборе запросов
Это кажется элементарным вопросом, но я не могу разобраться в этом.
У меня есть модель Recipe
, которая использует пользовательский RecipeQueryset
в качестве менеджера. Сейчас я много читал о том, где разместить бизнес-логику приложения, некоторые люди предлагают слой "сервисов", другие говорят поместить ее в пользовательский набор запросов, что я и пытаюсь сделать сейчас. Никто из них на самом деле не приводит пример выполнения вычислений, хотя только CRUD функциональность.
На данный момент у меня есть property
из total_cost
, определенных на модели Recipe
, но согласно многим статьям/обсуждениям я должен перенести эту "бизнес-логику" в другое место, например, в пользовательский queryset?
- Есть ли способ сделать вычисление в пользовательском методе queryset для набора объектов
Recipe
и вычислить этоtotal_cost
для каждогоRecipe
и вернуть этот набор с дополнительным полемtotal_cost
? - Как бы я добавил это, чтобы это можно было использовать в моем сериализаторе?
Модель
class RecipeQueryset(models.QuerySet):
"""Custom queryset for Recipe Model"""
def all_for_user(self, user):
return self.filter(user=user)
# this is where I think I should put it?
def total_cost(self, recipe):
return # some complex calculations across two related models
class Recipe(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
name = models.CharField(max_length=255, unique=True)
yield_count = models.FloatField()
yield_units = models.CharField(max_length=255)
objects = RecipeQueryset.as_manager()
....
# This is where it currently is
def get_recipe_ingredients(self):
"""Returns all Recipe ingredients associated with the recipe"""
return self.ingredients.all()
@property
def total_cost(self):
"""calculates the entire cost of producing the recipe"""
total_recipe_cost = 0
for r_ingredient in self.get_recipe_ingredients():
ingredient_cost = r_ingredient._get_individual_ingredient_cost()
total_recipe_cost += ingredient_cost
return total_recipe_cost
View/Api
class RecipeListApi(APIView):
class OutputSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=255)
yield_count = serializers.FloatField()
yield_units = serializers.CharField()
total_cost = serializers.FloatField() # Returns the total_cost value in the serializer
def get(self, request):
recipes = Recipe.objects.all_for_user(user=request.user)
serializer = self.OutputSerializer(recipes, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
Вы можете использовать SerializerMethodField
Здесь находится документация https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
и я рекомендую использовать ModelSerializer вместо Serializer. Также я рекомендую держать сериализатор вне представления
class OutputSerializer(serializers.ModelSerializer):
...
total_cost = serializers.SerializerMethodField("total_cost")
def total_cost(self, obj: Recipe) -> int:
"""calculates the entire cost of producing the recipe"""
total_recipe_cost = 0
for r_ingredient in obj.get_recipe_ingredients():
ingredient_cost = r_ingredient._get_individual_ingredient_cost()
total_recipe_cost += ingredient_cost
return total_recipe_cost