Имеется django модель: class FavoriteAndShoppingCa...

Tạo vào: 12 tháng 2, 2025

Trả lời bằng O1 bởi Chat01

Câu hỏi

Имеется django модель:
class FavoriteAndShoppingCart(models.Model):
"""Модель связывает рецепт и пользователя."""
recipe = models.ForeignKey(
Recipe, on_delete=models.CASCADE,
related_name='favorite_recipes', # для доступа к объектам исходной модели
verbose_name='Рецепт')
user = models.ForeignKey(
User, on_delete=models.CASCADE,
related_name='favorite_recipes',
verbose_name='Пользователь'
)
is_in_shopping_cart = models.BooleanField(
default=False,
blank=True,
verbose_name='Рецепт в корзине')
is_favorited = models.BooleanField(
default=False,
blank=True,
verbose_name='Избранные рецепты')

text
class Meta: verbose_name = 'Выбранный рецепт' verbose_name_plural = 'Выбранные рецепты' ordering = ['recipe', 'user'] constraints = [ models.UniqueConstraint( fields=['recipe', 'user'], name='%(app_label)s_%(class)s_unique_relationships' ), models.CheckConstraint( name='%(app_label)s_%(class)s_prevent_self_favorite', check=~models.Q(recipe=models.F('user')) ) ] def __str__(self): return (f'{self.user} выбрал рецепт {self.recipe} как: \ любимый рецепт {self.is_favorited}, \ рецепт для покупок ингридиентов {self.is_in_shopping_cart}')

serializers
class ShoppingCartSerializer(serializers.ModelSerializer):
'''Сериалайзер отображает страницу "Список покупок"
рецептов в списке покупок может: много или один.
исопользуется для добавления/удаления рецептов из списка покупок
после скачивания списка покупок, обнулить ведь его надо? - добавить на вью
'''
user = serializers.SlugRelatedField(
queryset=User.objects.all(),
source='favorite_recipes', many=True,
slug_field='usersfavorites',
default=serializers.CurrentUserDefault() # Текущий пользователь
)
recipe = serializers.SlugRelatedField(
slug_field='favoriterecipes', many=True,
source='favorite_recipes',
queryset=Recipe.objects.all(), # список рецептов текущего пользователя
)
is_in_shopping_cart = serializers.SerializerMethodField()

text
class Meta: model = FavoriteAndShoppingCart fields = ['image', 'name', 'cooking_time', 'is_in_shopping_cart'] def get_is_in_shopping_cart(self, obj): # ---- возможно можно сделать полезнее """Функция отображает, находится ли рецепт в корзине покупок текущего пользователя. Возвращает False если текущий пользователь не авторизован. """ current_user = self.context.get('request').user if current_user.is_anonymous: return False return current_user.favorite_recipes.filter( # проверить выход через рeлатед нейм recipe=obj ).exists()

Нужно написать @action для ViewSet добавления и удаления рецепта в корзину

Suy nghĩ

I’m working through adding or removing a recipe from a user's shopping cart via a Django Model "FavoriteAndShoppingCart". Using an action in ViewSet for "shopping_cart", I’ll create or retrieve the object, update is_in_shopping_cart, and serialize the result.

I’m detailing the implementation of custom actions in a Django Model for managing a user's shopping cart, including add-to-cart and remove-from-cart actions with appropriate HTTP methods and responses.

Clarifying how to structure Django actions for managing a shopping cart, distinguishing between adding and removing recipes, and ensuring unique method names to avoid conflicts.

Ragionato sull'aggiunta e rimozione della ricetta nel carrello per 15 secondi

Trả lời

Ниже приведён один из вариантов реализации добавления и удаления рецептов из корзины покупок через кастомные экшены @action. Предположим, что вы хотите добавить эти действия в ваш RecipeViewSet (или любой другой ViewSet, где логически правильно выполнять эти операции).

Основная идея:

  1. При добавлении в корзину:

    • Проверяем, что рецепт существует (через get_object_or_404).
    • Находим (или создаём) объект в FavoriteAndShoppingCart.
    • Проставляем is_in_shopping_cart = True.
    • Возвращаем в ответ сериализованные данные самого рецепта (или любую другую полезную информацию).
  2. При удалении из корзины:

    • Аналогично находим объект FavoriteAndShoppingCart для текущего пользователя и рецепта.
    • Если рецепт действительно в корзине — снимаем флаг is_in_shopping_cart = False или при желании удаляем запись из таблицы.
    • Возвращаем статус 204 без тела ответа, либо можем вернуть короткое подтверждение в JSON.

Пример кода (сделаем две раздельные action — на добавление и удаление; если нужно объединить POST/DELETE в один URL, придётся в одном методе обрабатывать два http-метода, но обычно удобнее раздельно):

python
from rest_framework.decorators import action from rest_framework.response import Response from rest_framework import status from django.shortcuts import get_object_or_404 from rest_framework.permissions import IsAuthenticated class RecipeViewSet(viewsets.ModelViewSet): # ваш обычный набор настроек queryset = Recipe.objects.all() serializer_class = RecipeSerializer permission_classes = [IsAuthenticatedOrReadOnly] @action( detail=True, methods=['post'], permission_classes=[IsAuthenticated], url_path='shopping_cart', # /recipes/<pk>/shopping_cart/ ) def add_to_shopping_cart(self, request, pk=None): """Добавляем рецепт в корзину покупок.""" user = request.user recipe = get_object_or_404(Recipe, pk=pk) # получаем или создаём запись в FavoriteAndShoppingCart relation_obj, created = FavoriteAndShoppingCart.objects.get_or_create( user=user, recipe=recipe ) # если рецепт уже "в корзине", вернём 400 if relation_obj.is_in_shopping_cart: return Response( {'errors': 'Рецепт уже находится в корзине.'}, status=status.HTTP_400_BAD_REQUEST ) relation_obj.is_in_shopping_cart = True relation_obj.save() # используем любой удобный сериализатор, можно и RecipeSerializer serializer = RecipeSerializer(recipe) return Response(serializer.data, status=status.HTTP_201_CREATED) @action( detail=True, methods=['delete'], permission_classes=[IsAuthenticated], url_path='shopping_cart', # /recipes/<pk>/shopping_cart/ ) def remove_from_shopping_cart(self, request, pk=None): """Удаляем рецепт из корзины покупок.""" user = request.user recipe = get_object_or_404(Recipe, pk=pk) relation_qs = FavoriteAndShoppingCart.objects.filter( user=user, recipe=recipe, is_in_shopping_cart=True ) if not relation_qs.exists(): return Response( {'errors': 'Рецепта нет в вашей корзине, удалять нечего.'}, status=status.HTTP_400_BAD_REQUEST ) relation_obj = relation_qs.first() # Либо удаляем запись совсем, либо просто ставим флаг в False # relation_obj.delete() relation_obj.is_in_shopping_cart = False relation_obj.save() return Response(status=status.HTTP_204_NO_CONTENT)

Краткие пояснения

  1. Разделение на два экшена (add_to_shopping_cart и remove_from_shopping_cart) позволяет явно разграничить операции и разные http-методы.

    • POST (добавить) → add_to_shopping_cart.
    • DELETE (удалить) → remove_from_shopping_cart.
  2. Проверка существования объекта FavoriteAndShoppingCart:

    • В add_to_shopping_cart при создании через get_or_create вы избавляетесь от лишнего «ручного» поиска.
    • В remove_from_shopping_cart сначала фильтруем по is_in_shopping_cart=True, чтобы точно убедиться, что рецепт именно «в корзине».
  3. Ответы:

    • При добавлении: 201 (Created), возвращаем сериализованные данные рецепта (или можно вернуть «минималистичные» данные).
    • При удалении: 204 (No Content), обычно тело ответа не требуется. Если нужно — можно вернуть JSON с {"detail": "Рецепт удалён из корзины"}.
  4. Проверка на уже существующий статус:

    • Если рецепт уже «в корзине», возращаем 400, иначе выставляем флаг is_in_shopping_cart = True.
    • Аналогично при удалении, если не найден рецепт в корзине, также возвращаем 400.

Таким образом вы сможете вызывать:

text
POST /api/recipes/<recipe_id>/shopping_cart/ DELETE /api/recipes/<recipe_id>/shopping_cart/

для добавления/удаления соответствующего рецепта из корзины покупок.

Chia sẻ Q&A này