UserFavorites system

This commit is contained in:
Parsa Nazer
2025-10-23 12:40:40 +03:30
parent fed32e78c5
commit 8d38346267
5 changed files with 134 additions and 3 deletions
@@ -0,0 +1,28 @@
# Generated by Django 5.1.2 on 2025-10-23 08:50
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0029_shopmodel'),
('product', '0055_alter_productmodel_options'),
]
operations = [
migrations.CreateModel(
name='UserFavorites',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('products', models.ManyToManyField(blank=True, to='product.productmodel', verbose_name='Likes')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
options={
'verbose_name': 'User Favorites',
'verbose_name_plural': 'Users Favorites',
},
),
]
+14 -1
View File
@@ -292,4 +292,17 @@ class SecurityBreachAttemptModel(models.Model):
# description = models.TextField()
# def __str__(self):
# return f'{self.subject[:30]}'
# return f'{self.subject[:30]}'
from product.models import ProductModel
class UserFavorites(models.Model):
user = models.OneToOneField(User, verbose_name=_('User'), on_delete=models.CASCADE)
products = models.ManyToManyField(ProductModel, verbose_name=_('Likes'), blank=True,)
def __str__(self):
return f'{self.user} likes'
class Meta:
verbose_name = _("User Favorites")
verbose_name_plural = _("Users Favorites")
+3 -1
View File
@@ -17,5 +17,7 @@ urlpatterns = [
path('unsubscribe', views.UnsubscribeView.as_view(), name='unsubscibe'),
path('attack/view/<int:pk>', views.ChangeViewAttack.as_view(), name='attack-view'),
path('logout', views.LogoutView.as_view(), name='logout'),
path('notification/all', views.NotificationListAPIView.as_view(), name='notif-list')
path('notification/all', views.NotificationListAPIView.as_view(), name='notif-list'),
path('favorites', views.FavoritesView.as_view(), name='favorites'),
path('favorites/toggle', views.ToggleFavoriteView.as_view(), name='favorite-toggle'),
]
+74
View File
@@ -355,3 +355,77 @@ class NotificationListAPIView(APIView):
def str_to_bool(self, val):
return True if val and val == 'read' else False
from product.models import ProductModel
class AddToFavoritesSerializer(serializers.Serializer):
product_slug = serializers.SlugField(allow_unicode=True)
def validate_product_id(self, value):
if not ProductModel.objects.filter(slug=value).exists():
raise serializers.ValidationError("Product does not exist.")
return value
from product.serializers import DynamicProductSerializer
from django.shortcuts import get_object_or_404
class ToggleFavoriteView(APIView):
permission_classes = [IsAuthenticated]
@extend_schema(
request=AddToFavoritesSerializer,
responses={200: DynamicProductSerializer},
tags=["favorites"]
)
def post(self, request):
serializer = AddToFavoritesSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
product_slug = serializer.validated_data["product_slug"]
product = get_object_or_404(ProductModel, slug=product_slug)
user_fav, _ = UserFavorites.objects.get_or_create(user=request.user)
if product in user_fav.products.all():
user_fav.products.remove(product)
action = "removed"
else:
user_fav.products.add(product)
action = "added"
return Response(
{
"action": action,
"product": DynamicProductSerializer(product, context={'request': request, 'view_type': 'list'}).data
},
status=status.HTTP_200_OK
)
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes
from utils.pagination import StructurePagination
class FavoritesView(APIView):
permission_classes = [IsAuthenticated]
serializer_class = DynamicProductSerializer
pagination_class = StructurePagination
@extend_schema(
tags=["favorites"],
parameters=[
OpenApiParameter(
name="limit",
description="Number of results to return per page (pagination).",
required=False,
type=OpenApiTypes.INT,
),
OpenApiParameter(
name="offset",
description="The starting position of the results (pagination).",
required=False,
type=OpenApiTypes.INT,
),
]
)
def get(self, request):
user_fav, _ = UserFavorites.objects.get_or_create(user=request.user)
products = user_fav.products.all()
paginator = self.pagination_class()
paginated_products = paginator.paginate_queryset(products, request)
serializer = self.serializer_class(instance=paginated_products, many=True, context={'request': request, 'view_type': 'list'})
return paginator.get_paginated_response(serializer.data)
+15 -1
View File
@@ -112,6 +112,7 @@ class DynamicProductSerializer(serializers.ModelSerializer):
category = SubCategorySerializer(read_only=True)
is_new = serializers.SerializerMethodField()
related_products = serializers.SerializerMethodField()
added_to_favorites = serializers.SerializerMethodField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -131,10 +132,23 @@ class DynamicProductSerializer(serializers.ModelSerializer):
view_type = {
'list': ['id','name', 'rating', 'slug', 'category', 'variants', 'colors'],
'slider': ['id','name', 'rating', 'slug', 'category', 'variants', 'colors'],
'instance': ['id', 'name', 'description', 'rating', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'related_products', 'in_pack_items', 'variants', 'colors'],
'instance': ['id', 'name', 'description', 'rating', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'related_products', 'in_pack_items', 'variants', 'colors', 'added_to_favorites'],
'chat': ['id', 'name', 'description', 'variants']
}
def get_added_to_favorites(self, obj):
from account.models import UserFavorites
request = self.context.get('request')
if not request or not request.user.is_authenticated:
return False # not logged in users haven't added anything
try:
user_fav = UserFavorites.objects.get(user=request.user)
except UserFavorites.DoesNotExist:
return False
return obj in user_fav.products.all()
def get_variants(self, obj):
view_type = self.context.get('view_type')
if view_type == 'slider':