485 lines
17 KiB
Python
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
|
|
}) |