From 3b3ec73e3820a8f4d53b88b37c33f027a6b872e5 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 18 Mar 2024 12:50:39 +0000 Subject: [PATCH] change models in recipes --- backend/api/filter.py | 4 +- backend/api/views.py | 25 +++++++----- backend/recipes/admin.py | 2 +- backend/recipes/constants.py | 9 +++++ backend/recipes/models.py | 78 ++++++++++++++++++++++-------------- backend/requirements.txt | 1 + 6 files changed, 74 insertions(+), 45 deletions(-) create mode 100644 backend/recipes/constants.py diff --git a/backend/api/filter.py b/backend/api/filter.py index 27ed0d42..92ec7373 100644 --- a/backend/api/filter.py +++ b/backend/api/filter.py @@ -22,11 +22,11 @@ class Meta: def is_favorited_filter(self, queryset, name, value): user = self.request.user if value and user.is_authenticated: - return queryset.filter(favorite_recipe__user=user) + return queryset.filter(favorites_recipe__user=user) return queryset def is_in_shopping_cart_filter(self, queryset, name, value): user = self.request.user if value and user.is_authenticated: - return queryset.filter(in_cart__user=user) + return queryset.filter(cart_recipe__user=user) return queryset diff --git a/backend/api/views.py b/backend/api/views.py index 9cbb84ea..52cc7b8b 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -1,3 +1,4 @@ + import csv from django.db.models import Sum @@ -126,37 +127,39 @@ def get_serializer_class(self): @action(detail=True, methods=['post', 'delete'], permission_classes=[IsAuthenticated, ]) def favorite(self, request, pk=None): - return self.add_and_delete(request, pk, Favorites) + if request.method == 'POST': + return self.add_to_list(request, pk, Favorites) + elif request.method == 'DELETE': + return self.remove_from_list(request, pk, Favorites) - @action(detail=True, methods=['post'], + @action(detail=True, methods=['post', 'delete'], permission_classes=[IsAuthenticated, ]) def shopping_cart(self, request, pk=None): if request.method == 'POST': - return self.add_to_cart(request, pk) + return self.add_to_list(request, pk, Cart) elif request.method == 'DELETE': - return self.remove_from_cart(request, pk) + return self.remove_from_list(request, pk, Cart) - def add_to_cart(self, request, pk): + def add_to_list(self, request, pk, model): try: recipe = Recipe.objects.get(id=pk) except Recipe.DoesNotExist: return Response({'There is no recipe like that.'}, status=status.HTTP_400_BAD_REQUEST) serializer = AddToRecipeSerializer(recipe) - if Cart.objects.filter(user=request.user, recipe=recipe).exists(): + if model.objects.filter(user=request.user, recipe=recipe).exists(): return Response({'There is the recipe in list.'}, status=status.HTTP_400_BAD_REQUEST) - Cart.objects.create(user=request.user, recipe=recipe) + model.objects.create(user=request.user, recipe=recipe) return Response(serializer.data, status=status.HTTP_201_CREATED) - @shopping_cart.mapping.delete - def remove_from_cart(self, request, pk): + def remove_from_list(self, request, pk, model): recipe = get_object_or_404(Recipe, id=pk) - if not Cart.objects.filter(user=request.user, + if not model.objects.filter(user=request.user, recipe=recipe).exists(): return Response({'There is no recipe in list.'}, status=status.HTTP_400_BAD_REQUEST) - Cart.objects.filter(user=request.user, recipe=recipe).delete() + model.objects.filter(user=request.user, recipe=recipe).delete() return Response(status=status.HTTP_204_NO_CONTENT) def perform_create(self, serializer): diff --git a/backend/recipes/admin.py b/backend/recipes/admin.py index 77f480d8..60904963 100644 --- a/backend/recipes/admin.py +++ b/backend/recipes/admin.py @@ -35,7 +35,7 @@ class RecipeAdmin(admin.ModelAdmin): @admin.display(description='В избранном') def count_favorites(self, obj: Recipe): - return obj.favorite_recipe.count() + return obj.favorites_recipe.count() @admin.register(Tag) diff --git a/backend/recipes/constants.py b/backend/recipes/constants.py new file mode 100644 index 00000000..0a7c93e2 --- /dev/null +++ b/backend/recipes/constants.py @@ -0,0 +1,9 @@ +INGREDIENT_NAME_MAX = 200 +MEASUREMENT_UNIT_MAX = 200 +TAG_NAME_MAX = 200 +COLOR_NAME_MAX = 7 +TAG_SLUG_MAX = 200 +RECIPE_NAME_MAX = 200 +COOKING_TIME_DEFAULT = 0 +AMOUNT_DEFAULT = 0 + diff --git a/backend/recipes/models.py b/backend/recipes/models.py index 9871244e..03bcc96b 100644 --- a/backend/recipes/models.py +++ b/backend/recipes/models.py @@ -1,20 +1,36 @@ +from colorfield.fields import ColorField from django.contrib.auth import get_user_model from django.core.validators import MinValueValidator, RegexValidator from django.db import models +from recipes.constants import (INGREDIENT_NAME_MAX, + MEASUREMENT_UNIT_MAX, + TAG_NAME_MAX, + COLOR_NAME_MAX, + TAG_SLUG_MAX, + RECIPE_NAME_MAX, + COOKING_TIME_DEFAULT, + AMOUNT_DEFAULT) + User = get_user_model() class Ingredient(models.Model): """Модель 'Ингредиент'.""" - name = models.CharField('Ингредиент', max_length=200) - measurement_unit = models.CharField('Единица измерения', max_length=200) + name = models.CharField('Ingredient', max_length=INGREDIENT_NAME_MAX) + measurement_unit = models.CharField('Measurement_unit', max_length=MEASUREMENT_UNIT_MAX) class Meta: verbose_name = 'Ингредиент' verbose_name_plural = 'Ингредиенты' ordering = ('name',) + constraints = ( + models.UniqueConstraint( + fields=('name', 'measurement_unit'), + name='unique_for_ingredient', + ), + ) def __str__(self): return self.name @@ -23,13 +39,13 @@ def __str__(self): class Tag(models.Model): """Модель 'Тег'.""" - name = models.CharField('Тег', max_length=200, unique=True) - color = models.CharField( + name = models.CharField('Tag', max_length=TAG_NAME_MAX, unique=True) + color = ColorField( 'Цвет', - max_length=7, + max_length=COLOR_NAME_MAX, unique=True, validators=[RegexValidator(regex='^#([a-fA-F0-9]{6})',)]) - slug = models.SlugField('Слаг', max_length=200, unique=True) + slug = models.SlugField('Slug', max_length=TAG_SLUG_MAX, unique=True) class Meta: verbose_name = 'Тег' @@ -43,7 +59,7 @@ def __str__(self): class Recipe(models.Model): """Модель 'Рецепт'.""" - name = models.CharField('Название', max_length=200, + name = models.CharField('Recipe', max_length=RECIPE_NAME_MAX, blank=False, null=False) image = models.ImageField('Картинка', upload_to='recipes/images/', @@ -65,7 +81,7 @@ class Recipe(models.Model): ) cooking_time = models.PositiveSmallIntegerField( 'Время приготовления', - default=0, + default=COOKING_TIME_DEFAULT, validators=( MinValueValidator(limit_value=1), ), @@ -110,7 +126,7 @@ class AmountIngredient(models.Model): ) amount = models.PositiveBigIntegerField( verbose_name='Количество', - default=0, + default=AMOUNT_DEFAULT, validators=[MinValueValidator(limit_value=1)] ) @@ -118,31 +134,45 @@ class Meta: verbose_name = 'Ингредиенты в рецепте' verbose_name_plural = 'Ингредиенты в рецептах' ordering = ('recipe',) + constraints = ( + models.UniqueConstraint( + fields=('recipe', 'ingredient'), + name='unique_for_recipe', + ), + ) + def __str__(self): return f'{self.amount} {self.ingredient}' -class Favorites(models.Model): - """Модель 'Избранное'.""" +class BaseListModel(models.Model): + """Abstract model for cart and favorites models.""" recipe = models.ForeignKey( Recipe, - verbose_name='Избранный рецепт', - related_name='favorite_recipe', + verbose_name='Recipe', on_delete=models.CASCADE, + related_name='%(class)s_recipe', ) user = models.ForeignKey( User, - verbose_name='Пользователь', - related_name='favorite_user', + verbose_name='User', on_delete=models.CASCADE, + related_name='%(class)s_user', ) pub_date = models.DateTimeField( - 'Дата публикации', + 'Publication date', auto_now_add=True, ) + class Meta: + abstract = True + + +class Favorites(BaseListModel): + """Модель 'Избранное'.""" + class Meta: verbose_name = 'Избранное' verbose_name_plural = 'Избранное' @@ -151,23 +181,9 @@ def __str__(self): return f'{self.user.username} - {self.recipe.name}' -class Cart(models.Model): +class Cart(BaseListModel): """Модель 'Корзина'.""" - recipe = models.ForeignKey( - Recipe, - verbose_name='Рецепты в списке покупок', - related_name='in_cart', - on_delete=models.CASCADE, - ) - user = models.ForeignKey( - User, - verbose_name='Автор списка', - related_name='cart', - on_delete=models.CASCADE, - ) - pub_date = models.DateTimeField('Дата публикации', auto_now_add=True,) - class Meta: verbose_name = 'Корзина' verbose_name_plural = 'Корзина' diff --git a/backend/requirements.txt b/backend/requirements.txt index 9148814e..bf5e9814 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -44,3 +44,4 @@ urllib3==2.1.0 xlrd==2.0.1 xlwt==1.3.0 django-cors-headers==3.13.0 +django-colorfield==0.11.0