Assignment / reviews /views.py
Assignment User
Initial commit: Django REST API for place reviews
877587b
from django.db.models import Q, Case, When, Value, IntegerField
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from .models import User, Place, Review
from .serializers import (
UserSerializer,
PlaceSerializer,
PlaceDetailSerializer,
AddReviewSerializer
)
@api_view(['POST'])
@permission_classes([AllowAny])
def register(request):
"""
Register a new user with name and phone number.
Request body:
{
"name": "John Doe",
"phone": "1234567890"
}
Response:
{
"user": {
"id": 1,
"name": "John Doe",
"phone": "1234567890"
},
"token": "your-auth-token"
}
"""
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
token, _ = Token.objects.get_or_create(user=user)
return Response({
'user': serializer.data,
'token': token.key
}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
"""
Login with phone number to get authentication token.
Request body:
{
"phone": "1234567890"
}
Response:
{
"user": {
"id": 1,
"name": "John Doe",
"phone": "1234567890"
},
"token": "your-auth-token"
}
"""
phone = request.data.get('phone')
if not phone:
return Response(
{'error': 'Phone number is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
user = User.objects.get(phone=phone)
token, _ = Token.objects.get_or_create(user=user)
return Response({
'user': UserSerializer(user).data,
'token': token.key
})
except User.DoesNotExist:
return Response(
{'error': 'User with this phone number does not exist'},
status=status.HTTP_404_NOT_FOUND
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def add_review(request):
"""
Add a review for a place. Creates the place if it doesn't exist.
Request body:
{
"place_name": "Joe's Pizza",
"place_address": "123 Main St, New York, NY",
"rating": 5,
"review_text": "Best pizza in town!"
}
Response:
{
"message": "Review added successfully",
"review": {
"id": 1,
"user_name": "John Doe",
"rating": 5,
"text": "Best pizza in town!",
"created_at": "2024-01-01T12:00:00Z"
}
}
"""
serializer = AddReviewSerializer(
data=request.data,
context={'request': request}
)
if serializer.is_valid():
review = serializer.save()
from .serializers import ReviewSerializer
return Response({
'message': 'Review added successfully',
'review': ReviewSerializer(review).data
}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def search_places(request):
"""
Search for places by name and/or minimum rating.
Query parameters:
- name (optional): Search by place name (exact match first, then partial)
- min_rating (optional): Filter by minimum average rating
Example: /api/places/search/?name=pizza&min_rating=4
Response:
[
{
"id": 1,
"name": "Joe's Pizza",
"address": "123 Main St, New York, NY",
"average_rating": 4.5
}
]
"""
name_query = request.query_params.get('name', '').strip()
min_rating = request.query_params.get('min_rating', None)
# Start with all places
queryset = Place.objects.all()
# Filter by name if provided
if name_query:
# Use Q objects to combine exact and partial matches
# Annotate with priority: 1 for exact match, 2 for partial match
queryset = queryset.annotate(
match_priority=Case(
When(name__iexact=name_query, then=Value(1)),
When(name__icontains=name_query, then=Value(2)),
default=Value(3),
output_field=IntegerField()
)
).filter(
Q(name__iexact=name_query) | Q(name__icontains=name_query)
).order_by('match_priority', 'name')
else:
queryset = queryset.order_by('name')
# Get all places and filter by rating in Python (since average is a property)
places = list(queryset)
if min_rating:
try:
min_rating_value = float(min_rating)
places = [
place for place in places
if place.average_rating is not None and place.average_rating >= min_rating_value
]
except ValueError:
return Response(
{'error': 'Invalid min_rating value'},
status=status.HTTP_400_BAD_REQUEST
)
serializer = PlaceSerializer(places, many=True)
return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def place_detail(request, place_id):
"""
Get detailed information about a place including all reviews.
Current user's review appears first, followed by others sorted by newest.
Response:
{
"id": 1,
"name": "Joe's Pizza",
"address": "123 Main St, New York, NY",
"average_rating": 4.5,
"reviews": [
{
"id": 1,
"user_name": "John Doe",
"rating": 5,
"text": "Best pizza!",
"created_at": "2024-01-01T12:00:00Z"
}
]
}
"""
try:
place = Place.objects.get(pk=place_id)
serializer = PlaceDetailSerializer(
place,
context={'request': request}
)
return Response(serializer.data)
except Place.DoesNotExist:
return Response(
{'error': 'Place not found'},
status=status.HTTP_404_NOT_FOUND
)