furry/products/api_views.py

485 lines
17 KiB
Python

from rest_framework import viewsets, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Q, Avg, Count
from django.shortcuts import get_object_or_404
from .models import Product, Category, Review, Wishlist, GalleryImage, CustomOrder
from .serializers import (
ProductSerializer, ProductListSerializer, ProductDetailSerializer,
CategorySerializer, ReviewSerializer, WishlistSerializer,
CustomOrderSerializer, CartSerializer, SearchFilterSerializer,
ProductStatsSerializer, GalleryImageDetailSerializer
)
class ProductViewSet(viewsets.ModelViewSet):
"""ViewSet für Produkt-API"""
queryset = Product.objects.select_related('category').prefetch_related(
'reviews', 'gallery_images', 'wishlists'
).all()
serializer_class = ProductListSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['category', 'product_type', 'fursuit_type', 'style', 'on_sale', 'is_featured']
search_fields = ['name', 'description']
ordering_fields = ['name', 'base_price', 'created_at', 'average_rating']
ordering = ['-created_at']
def get_serializer_class(self):
if self.action == 'retrieve':
return ProductDetailSerializer
elif self.action == 'create' or self.action == 'update':
return ProductSerializer
return ProductListSerializer
def get_queryset(self):
queryset = super().get_queryset()
# Filter nach Preis
min_price = self.request.query_params.get('min_price')
max_price = self.request.query_params.get('max_price')
if min_price:
queryset = queryset.filter(base_price__gte=min_price)
if max_price:
queryset = queryset.filter(base_price__lte=max_price)
# Filter nach Lagerbestand
in_stock = self.request.query_params.get('in_stock')
if in_stock == 'true':
queryset = queryset.filter(stock__gt=0)
return queryset
@action(detail=False, methods=['get'])
def featured(self, request):
"""Featured Produkte"""
products = self.get_queryset().filter(is_featured=True)
serializer = self.get_serializer(products, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def on_sale(self, request):
"""Produkte im Angebot"""
products = self.get_queryset().filter(on_sale=True)
serializer = self.get_serializer(products, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def low_stock(self, request):
"""Produkte mit niedrigem Lagerbestand"""
products = self.get_queryset().filter(stock__lte=5, stock__gt=0)
serializer = self.get_serializer(products, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def add_to_wishlist(self, request, pk=None):
"""Produkt zur Wunschliste hinzufügen"""
product = self.get_object()
user = request.user
if not user.is_authenticated:
return Response(
{'error': 'Authentication required'},
status=status.HTTP_401_UNAUTHORIZED
)
wishlist, created = Wishlist.objects.get_or_create(user=user)
wishlist.products.add(product)
return Response({
'message': 'Product added to wishlist',
'product_id': product.id
})
@action(detail=True, methods=['post'])
def remove_from_wishlist(self, request, pk=None):
"""Produkt von Wunschliste entfernen"""
product = self.get_object()
user = request.user
if not user.is_authenticated:
return Response(
{'error': 'Authentication required'},
status=status.HTTP_401_UNAUTHORIZED
)
try:
wishlist = Wishlist.objects.get(user=user)
wishlist.products.remove(product)
return Response({
'message': 'Product removed from wishlist',
'product_id': product.id
})
except Wishlist.DoesNotExist:
return Response(
{'error': 'Wishlist not found'},
status=status.HTTP_404_NOT_FOUND
)
@action(detail=False, methods=['get'])
def stats(self, request):
"""Produkt-Statistiken"""
queryset = self.get_queryset()
stats = {
'total_products': queryset.count(),
'featured_products': queryset.filter(is_featured=True).count(),
'on_sale_products': queryset.filter(on_sale=True).count(),
'low_stock_products': queryset.filter(stock__lte=5, stock__gt=0).count(),
'categories_count': Category.objects.count(),
'average_rating': queryset.aggregate(Avg('average_rating'))['average_rating__avg'] or 0,
'total_reviews': Review.objects.count(),
}
serializer = ProductStatsSerializer(stats)
return Response(serializer.data)
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
"""ViewSet für Kategorie-API"""
queryset = Category.objects.prefetch_related('products').all()
serializer_class = CategorySerializer
permission_classes = [IsAuthenticatedOrReadOnly]
@action(detail=True, methods=['get'])
def products(self, request, pk=None):
"""Produkte einer Kategorie"""
category = self.get_object()
products = category.products.all()
# Pagination
page = self.paginate_queryset(products)
if page is not None:
serializer = ProductListSerializer(page, many=True, context={'request': request})
return self.get_paginated_response(serializer.data)
serializer = ProductListSerializer(products, many=True, context={'request': request})
return Response(serializer.data)
class ReviewViewSet(viewsets.ModelViewSet):
"""ViewSet für Review-API"""
queryset = Review.objects.select_related('user', 'product').all()
serializer_class = ReviewSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend]
filterset_fields = ['product', 'rating']
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def get_queryset(self):
queryset = super().get_queryset()
product_id = self.request.query_params.get('product_id')
if product_id:
queryset = queryset.filter(product_id=product_id)
return queryset
@action(detail=False, methods=['get'])
def my_reviews(self, request):
"""Reviews des aktuellen Users"""
if not request.user.is_authenticated:
return Response(
{'error': 'Authentication required'},
status=status.HTTP_401_UNAUTHORIZED
)
reviews = self.get_queryset().filter(user=request.user)
serializer = self.get_serializer(reviews, many=True)
return Response(serializer.data)
class WishlistViewSet(viewsets.ModelViewSet):
"""ViewSet für Wishlist-API"""
serializer_class = WishlistSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Wishlist.objects.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
@action(detail=False, methods=['post'])
def add_product(self, request):
"""Produkt zur Wunschliste hinzufügen"""
product_id = request.data.get('product_id')
if not product_id:
return Response(
{'error': 'product_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
product = Product.objects.get(id=product_id)
wishlist, created = Wishlist.objects.get_or_create(user=request.user)
wishlist.products.add(product)
return Response({
'message': 'Product added to wishlist',
'product_id': product_id
})
except Product.DoesNotExist:
return Response(
{'error': 'Product not found'},
status=status.HTTP_404_NOT_FOUND
)
@action(detail=False, methods=['post'])
def remove_product(self, request):
"""Produkt von Wunschliste entfernen"""
product_id = request.data.get('product_id')
if not product_id:
return Response(
{'error': 'product_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
wishlist = Wishlist.objects.get(user=request.user)
product = Product.objects.get(id=product_id)
wishlist.products.remove(product)
return Response({
'message': 'Product removed from wishlist',
'product_id': product_id
})
except (Wishlist.DoesNotExist, Product.DoesNotExist):
return Response(
{'error': 'Wishlist or product not found'},
status=status.HTTP_404_NOT_FOUND
)
class CustomOrderViewSet(viewsets.ModelViewSet):
"""ViewSet für Custom Order-API"""
serializer_class = CustomOrderSerializer
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = ['status', 'fursuit_type', 'style']
def get_queryset(self):
return CustomOrder.objects.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
@action(detail=True, methods=['post'])
def update_status(self, request, pk=None):
"""Status eines Custom Orders aktualisieren"""
custom_order = self.get_object()
new_status = request.data.get('status')
if new_status not in dict(CustomOrder.STATUS_CHOICES):
return Response(
{'error': 'Invalid status'},
status=status.HTTP_400_BAD_REQUEST
)
custom_order.status = new_status
custom_order.save()
serializer = self.get_serializer(custom_order)
return Response(serializer.data)
class GalleryImageViewSet(viewsets.ReadOnlyModelViewSet):
"""ViewSet für Galerie-API"""
queryset = GalleryImage.objects.select_related('product').all()
serializer_class = GalleryImageDetailSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend]
filterset_fields = ['is_featured', 'fursuit_type', 'style']
@action(detail=False, methods=['get'])
def featured(self, request):
"""Featured Galerie-Bilder"""
images = self.get_queryset().filter(is_featured=True)
serializer = self.get_serializer(images, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def by_product(self, request):
"""Bilder nach Produkt filtern"""
product_id = request.query_params.get('product_id')
if not product_id:
return Response(
{'error': 'product_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
images = self.get_queryset().filter(product_id=product_id)
serializer = self.get_serializer(images, many=True)
return Response(serializer.data)
class CartViewSet(viewsets.ViewSet):
"""ViewSet für Warenkorb-API"""
permission_classes = [IsAuthenticated]
@action(detail=False, methods=['get'])
def get_cart(self, request):
"""Warenkorb abrufen"""
# Hier würde die Warenkorb-Logik implementiert
# Für jetzt ein Beispiel-Response
cart_data = {
'items': [],
'subtotal': 0,
'shipping_cost': 5.99,
'total': 5.99,
'item_count': 0
}
serializer = CartSerializer(cart_data)
return Response(serializer.data)
@action(detail=False, methods=['post'])
def add_item(self, request):
"""Item zum Warenkorb hinzufügen"""
product_id = request.data.get('product_id')
quantity = request.data.get('quantity', 1)
if not product_id:
return Response(
{'error': 'product_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
product = Product.objects.get(id=product_id)
# Hier würde die Warenkorb-Logik implementiert
return Response({
'message': 'Product added to cart',
'product_id': product_id,
'quantity': quantity
})
except Product.DoesNotExist:
return Response(
{'error': 'Product not found'},
status=status.HTTP_404_NOT_FOUND
)
@action(detail=False, methods=['post'])
def update_quantity(self, request):
"""Menge eines Items aktualisieren"""
item_id = request.data.get('item_id')
quantity = request.data.get('quantity')
if not item_id or not quantity:
return Response(
{'error': 'item_id and quantity are required'},
status=status.HTTP_400_BAD_REQUEST
)
# Hier würde die Warenkorb-Logik implementiert
return Response({
'message': 'Quantity updated',
'item_id': item_id,
'quantity': quantity
})
@action(detail=False, methods=['post'])
def remove_item(self, request):
"""Item aus Warenkorb entfernen"""
item_id = request.data.get('item_id')
if not item_id:
return Response(
{'error': 'item_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
# Hier würde die Warenkorb-Logik implementiert
return Response({
'message': 'Item removed from cart',
'item_id': item_id
})
@action(detail=False, methods=['post'])
def clear_cart(self, request):
"""Warenkorb leeren"""
# Hier würde die Warenkorb-Logik implementiert
return Response({
'message': 'Cart cleared'
})
class SearchViewSet(viewsets.ViewSet):
"""ViewSet für Such-API"""
permission_classes = [IsAuthenticatedOrReadOnly]
@action(detail=False, methods=['get'])
def products(self, request):
"""Produkte suchen"""
serializer = SearchFilterSerializer(data=request.query_params)
if not serializer.is_valid():
return Response(
serializer.errors,
status=status.HTTP_400_BAD_REQUEST
)
data = serializer.validated_data
queryset = Product.objects.select_related('category').all()
# Suchfilter anwenden
if data.get('query'):
queryset = queryset.filter(
Q(name__icontains=data['query']) |
Q(description__icontains=data['query'])
)
if data.get('category'):
queryset = queryset.filter(category__slug=data['category'])
if data.get('fursuit_type'):
queryset = queryset.filter(fursuit_type=data['fursuit_type'])
if data.get('style'):
queryset = queryset.filter(style=data['style'])
if data.get('min_price'):
queryset = queryset.filter(base_price__gte=data['min_price'])
if data.get('max_price'):
queryset = queryset.filter(base_price__lte=data['max_price'])
if data.get('on_sale'):
queryset = queryset.filter(on_sale=data['on_sale'])
if data.get('is_featured'):
queryset = queryset.filter(is_featured=data['is_featured'])
# Sortierung
sort_by = data.get('sort_by', 'newest')
if sort_by == 'newest':
queryset = queryset.order_by('-created_at')
elif sort_by == 'oldest':
queryset = queryset.order_by('created_at')
elif sort_by == 'price_low':
queryset = queryset.order_by('base_price')
elif sort_by == 'price_high':
queryset = queryset.order_by('-base_price')
elif sort_by == 'name':
queryset = queryset.order_by('name')
elif sort_by == 'rating':
queryset = queryset.order_by('-average_rating')
# Pagination
page_size = data.get('page_size', 12)
page = data.get('page', 1)
start = (page - 1) * page_size
end = start + page_size
products = queryset[start:end]
serializer = ProductListSerializer(products, many=True, context={'request': request})
return Response({
'results': serializer.data,
'total': queryset.count(),
'page': page,
'page_size': page_size,
'pages': (queryset.count() + page_size - 1) // page_size
})