Django - Moving logic from viewsets to a model or model method in viewsets

50 Views Asked by At

I have a small educational project for a social network that includes Recipes and ShoppingCart models. Among other functionalities, the RecipeViewSet allows users to download a file containing the ingredients of recipes previously added to the shopping cart. This part of the RecipeViewSet appears as follows:

@action(
        detail=False,
        methods=['GET'],
        permission_classes=[IsAuthenticated]
    )
    def download_shopping_cart(self, request):
        user = self.request.user
        if not user.shoppingcarts.exists():
            raise ValidationError('The shopping cart is empty.')
        return FileResponse(
            get_shopping_list(
                user_cart=user.shoppingcarts.all()
            ),
            as_attachment=True,
            filename='shopping list ' + dt.now().strftime('%d-%m-%Y') + '.txt',
        )

The function that forms the list of ingredients for downloading looks like this:

def get_shopping_list(user_cart):

    ingredient_name = 'recipe__recipe_ingredients__ingredient__name'
    ingredient_unit = (
        'recipe__recipe_ingredients__ingredient__measurement_unit'
    )
    ingredient_amount = 'recipe__recipe_ingredients__amount'
    amount_sum = 'recipe__recipe_ingredients__amount__sum'
    ingredients = user_cart.select_related('recipe').values(
        ingredient_name, ingredient_unit
    ).annotate(Sum(ingredient_amount)).order_by(ingredient_name)

    recipes = [f'"{recipe.recipe.name}"' for recipe in user_cart]
    ingredients_list = [
        (f'{number}. {ingredient[ingredient_name].capitalize()} '
         f'({ingredient[ingredient_unit]}) - '
         f'{ingredient[amount_sum]}')
        for number, ingredient in enumerate(ingredients, 1)
    ]
    return '\n'.join(['Recipes:', *recipes, 'To buy:', *ingredients_list])

I want to move some of the logic (the first half of the function) that is not directly related to forming the text out of the get_shopping_list function and transfer it to a model. Therefore, my questions are, which model is better to move it to, ShoppingCart or Recipe and why? And how can this be implemented?

1

There are 1 best solutions below

0
Kapil gaikwad On

After reviewing your code, I suggest moving the query logic to the ShoppingCart model. Add a method "get_ingredients_query" to return the Django QuerySet. Also add "get_formatted_ingredients" to run the query, annotate and format the returned data.

This separates concerns well - the model handles data retrieval and preparation, while the view focuses on returning the response.

Encapsulating the logic in the model methods makes the code more readable, reusable and maintainable by abstracting away implementation details.

Hope this will helps you.