Reputation: 175
This seems like a basic question but for the life of me I cannot seem to figure this out.
I have a Recipe
model that uses a custom RecipeQueryset
as a manager. Now I've been doing a lot of reading on where to put business logic of the application some people suggest a "services" layer others say to put it in a custom queryset which is what I am trying to accomplish now. None of them really give an example of doing calculations though only CRUD functionality.
At the moment I have a property
of total_cost
defined on the Recipe
model but according to a lot of articles/discussions I should be moving this "business logic" to elsewhere to the custom queryset for example?
Recipe
objects and calculate that total_cost
for each Recipe
and return that queryset with the extra total_cost
field?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
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)
Upvotes: 1
Views: 727
Reputation: 136
You can use SerializerMethodField
here is the docs https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
and I recommend to use ModelSerializer instead of Serializer. Also I recommend keep serializer out of the view
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
Upvotes: 1