Skip to content

Commit ed5a61d

Browse files
committed
refactor(restaurants): add permissions, update views, managers and more
1 parent bdd944f commit ed5a61d

File tree

7 files changed

+89
-99
lines changed

7 files changed

+89
-99
lines changed

apps/restaurants/admin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ class RestaurantAdmin(admin.ModelAdmin):
1010
"""Admin config for Restaurant model."""
1111
search_fields = ["name", "user"]
1212
list_display = ["name", "available",]
13+
list_editable = ["available"]
14+
list_filter = ["specialty", "is_open"]
1315
list_per_page = 25
1416
readonly_fields = ["pk", "slug", "is_open", "created_at", "updated_at",]
1517
ordering = ["name",]

apps/restaurants/managers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""Managers for Restaurants App."""
2+
3+
from django.db import models
4+
5+
6+
class RestaurantManager(models.Manager):
7+
"""Manager for Restaurant model."""
8+
9+
def get_all(self):
10+
"""Get all available restaurants"""
11+
return self.filter(available=True)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 5.0.4 on 2024-04-23 18:46
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('restaurants', '0004_restaurant_banner'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='restaurant',
15+
options={'ordering': ['pk'], 'verbose_name': 'Restaurant', 'verbose_name_plural': 'Restaurants'},
16+
),
17+
]

apps/restaurants/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from apps.utilities.paths import image_path, image_banner_path
88
from apps.utilities.validators import validate_phone
99
from apps.utilities.models import BaseModel
10+
from .managers import RestaurantManager
1011
from .choices import Specialty
1112

1213
User = settings.AUTH_USER_MODEL
@@ -34,8 +35,11 @@ class Restaurant(BaseModel):
3435
website = models.URLField(max_length=255, blank=True)
3536
is_open = models.BooleanField(default=False)
3637

38+
objects = RestaurantManager()
39+
3740
class Meta:
3841
"""Meta definition for Restaurant."""
42+
ordering = ["pk"]
3943
verbose_name = "Restaurant"
4044
verbose_name_plural = "Restaurants"
4145

apps/restaurants/permissions.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
from rest_framework.permissions import BasePermission, SAFE_METHODS
44

55

6-
class IsBusinessOwnerOrReadOnly(BasePermission):
6+
class IsBusinessOrReadOnly(BasePermission):
77
"""Permission to only allow business owners to edit the restaurant."""
88

9-
def has_object_permission(self, request, view, obj):
10-
if request.method in SAFE_METHODS:
11-
return True
12-
return request.user.role == "business"
9+
def has_permission(self, request, view):
10+
is_safe_method = request.method in SAFE_METHODS
11+
is_business = (
12+
request.user.is_authenticated and request.user.role == "business")
13+
return is_safe_method or is_business

apps/restaurants/urls.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from .views import (
66
RestaurantListAPIView, RestaurantDetailAPIView,
77
RestaurantCategoriesAPIView, RestaurantFoodsAPIView,
8-
RestaurantFoodDetailAPIView
98
)
109

1110

@@ -17,6 +16,4 @@
1716
RestaurantCategoriesAPIView.as_view()),
1817
path("api/v1/restaurants/<uuid:restaurant_id>/foods/",
1918
RestaurantFoodsAPIView.as_view()),
20-
path("api/v1/restaurants/<uuid:restaurant_id>/foods/<uuid:food_id>",
21-
RestaurantFoodDetailAPIView.as_view()),
2219
]

apps/restaurants/views.py

Lines changed: 49 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,108 @@
11
"""Views for Restaurants App."""
22

33
from django.db import transaction
4-
from django.http import Http404
4+
from django.core.cache import cache
55
from django.shortcuts import get_object_or_404
66
from rest_framework.views import APIView
7-
from rest_framework.permissions import IsAuthenticatedOrReadOnly, AllowAny
87
from rest_framework.response import Response
98
from rest_framework import status
109

1110
from apps.utilities.pagination import LargeSetPagination
1211
from apps.foods.models import Food
13-
from apps.foods.serializers import FoodSerializer, FoodMiniSerializer
12+
from apps.foods.serializers import FoodMiniSerializer
1413
from apps.categories.models import Category
14+
from apps.categories.serializers import CategoryListSerializer
1515
from .models import Restaurant
1616
from .serializers import RestaurantSerializer
17-
from .permissions import IsBusinessOwnerOrReadOnly
17+
from .permissions import IsBusinessOrReadOnly
1818

1919

2020
class RestaurantListAPIView(APIView):
2121
"""APIView to list and create restaurants."""
2222
serializer_class = RestaurantSerializer
23-
permission_classes = [IsAuthenticatedOrReadOnly]
23+
permission_classes = [IsBusinessOrReadOnly]
24+
cache_key = "restaurant"
25+
cache_timeout = 7200 # 2 hours
2426

2527
def get(self, request):
2628
# Get a list of restaurants
27-
stores = Restaurant.objects.filter(available=True).order_by("id")
28-
if stores.exists():
29-
paginator = LargeSetPagination()
30-
paginated_data = paginator.paginate_queryset(stores, request)
29+
paginator = LargeSetPagination()
30+
cached_data = cache.get(self.cache_key)
31+
32+
if cached_data is None:
33+
restaurants = Restaurant.objects.get_all()
34+
if not restaurants.exists():
35+
return Response(
36+
{"detail": "No restaurants available."},
37+
status=status.HTTP_404_NOT_FOUND
38+
)
39+
# Fetches the data from the database and serializes it
40+
paginated_data = paginator.paginate_queryset(restaurants, request)
3141
serializer = self.serializer_class(paginated_data, many=True)
32-
return paginator.get_paginated_response(serializer.data)
33-
return Response(
34-
{"detail": "No restaurants available."},
35-
status=status.HTTP_404_NOT_FOUND
36-
)
42+
# Set cache
43+
cache.set(self.cache_key, serializer.data, self.cache_timeout)
44+
else:
45+
# Retrieve the cached data and serialize it
46+
paginated_cached_data = paginator.paginate_queryset(
47+
cached_data, request)
48+
serializer = self.serializer_class(
49+
paginated_cached_data, many=True)
50+
51+
return paginator.get_paginated_response(serializer.data)
3752

3853
@transaction.atomic
3954
def post(self, request):
4055
# Create a new restaurant
4156
serializer = self.serializer_class(data=request.data)
4257
if serializer.is_valid():
4358
serializer.save()
44-
return Response(
45-
serializer.data,
46-
status=status.HTTP_201_CREATED
47-
)
48-
return Response(
49-
serializer.errors,
50-
status=status.HTTP_400_BAD_REQUEST
51-
)
59+
# Invalidate cache
60+
cache.delete(self.cache_key)
61+
return Response(serializer.data, status=status.HTTP_201_CREATED)
62+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
5263

5364

5465
class RestaurantDetailAPIView(APIView):
5566
"""APIView to retrieve, update, and delete a restaurant."""
5667
serializer_class = RestaurantSerializer
57-
permission_classes = [IsBusinessOwnerOrReadOnly]
68+
permission_classes = [IsBusinessOrReadOnly]
5869

5970
def get_object(self, restaurant_id):
6071
# Get a restaurant instance by id
61-
try:
62-
return Restaurant.objects.get(pk=restaurant_id)
63-
except Restaurant.DoesNotExist:
64-
raise Http404
72+
return get_object_or_404(Restaurant, pk=restaurant_id)
6573

6674
def get(self, request, restaurant_id):
6775
# Get details of a restaurant
68-
store = self.get_object(restaurant_id)
69-
serializer = self.serializer_class(store)
76+
restaurant = self.get_object(restaurant_id)
77+
serializer = self.serializer_class(restaurant)
7078
return Response(serializer.data)
7179

7280
@transaction.atomic
7381
def put(self, request, restaurant_id):
7482
# Update a restaurant
75-
store = self.get_object(restaurant_id)
76-
serializer = self.serializer_class(store, data=request.data)
83+
restaurant = self.get_object(restaurant_id)
84+
serializer = self.serializer_class(restaurant, data=request.data)
7785
if serializer.is_valid():
7886
serializer.save()
7987
return Response(serializer.data)
80-
return Response(
81-
serializer.errors,
82-
status=status.HTTP_400_BAD_REQUEST
83-
)
88+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
8489

8590
@transaction.atomic
8691
def delete(self, request, restaurant_id):
8792
# Delete a restaurant
88-
store = self.get_object(restaurant_id)
89-
store.delete()
93+
restaurant = self.get_object(restaurant_id)
94+
restaurant.available = False # Logical deletion
95+
restaurant.save()
9096
return Response(status=status.HTTP_204_NO_CONTENT)
9197

9298

9399
class RestaurantCategoriesAPIView(APIView):
94-
permission_classes = [IsAuthenticatedOrReadOnly]
100+
permission_classes = [IsBusinessOrReadOnly]
101+
serializer_class = CategoryListSerializer
95102

96-
def get(self, request, restaurant_id, formate=None):
103+
def get(self, request, restaurant_id, format=None):
97104
# Get a list of categories associated with a restaurant
98-
categories = Category.filter(restaurant=restaurant_id)
105+
categories = Category.objects.filter(restaurant=restaurant_id)
99106
if categories.exists():
100107
paginator = LargeSetPagination()
101108
paginated_data = paginator.paginate_queryset(categories, request)
@@ -108,12 +115,12 @@ def get(self, request, restaurant_id, formate=None):
108115

109116

110117
class RestaurantFoodsAPIView(APIView):
111-
permission_classes = [IsAuthenticatedOrReadOnly]
118+
permission_classes = [IsBusinessOrReadOnly]
112119

113120
def get(self, request, restaurant_id, format=None):
114121
# Get a list of foods for a restaurant
115122
restaurant = get_object_or_404(Restaurant, id=restaurant_id)
116-
foods = Food.objects.filter(restaurant=restaurant)
123+
foods = Food.objects.get_foods_by_restaurant(restaurant)
117124
if foods.exists():
118125
paginator = LargeSetPagination()
119126
paginated_data = paginator.paginate_queryset(foods, request)
@@ -123,52 +130,3 @@ def get(self, request, restaurant_id, format=None):
123130
{"detail": "No foods available."},
124131
status=status.HTTP_404_NOT_FOUND
125132
)
126-
127-
@transaction.atomic
128-
def post(self, request, restaurant_id, format=None):
129-
# Create a new food item for a restaurant
130-
restaurant = get_object_or_404(Restaurant, id=restaurant_id)
131-
serializer = FoodSerializer(data=request.data)
132-
if serializer.is_valid():
133-
serializer.save(restaurant=restaurant)
134-
return Response(
135-
serializer.data,
136-
status=status.HTTP_201_CREATED
137-
)
138-
return Response(
139-
serializer.errors,
140-
status=status.HTTP_400_BAD_REQUEST
141-
)
142-
143-
144-
class RestaurantFoodDetailAPIView(APIView):
145-
permission_classes = [AllowAny]
146-
147-
def get(self, request, restaurant_id, food_id, format=None):
148-
# Retrieve details for a food item associated with a restaurant
149-
restaurant = get_object_or_404(Restaurant, id=restaurant_id)
150-
food = get_object_or_404(Food, id=food_id, restaurant=restaurant)
151-
serializer = FoodSerializer(food)
152-
return Response(serializer.data)
153-
154-
def put(self, request, restaurant_id, food_id, format=None):
155-
# Update details for a food item associated with a restaurant
156-
restaurant = get_object_or_404(Restaurant, id=restaurant_id)
157-
food = get_object_or_404(Food, id=food_id, restaurant=restaurant)
158-
serializer = FoodSerializer(food, data=request.data)
159-
if serializer.is_valid():
160-
serializer.save()
161-
return Response(serializer.data)
162-
return Response(
163-
serializer.errors,
164-
status=status.HTTP_400_BAD_REQUEST
165-
)
166-
167-
def delete(self, request, restaurant_id, food_id, format=None):
168-
# Delete a food item associated with a restaurant
169-
restaurant = get_object_or_404(Restaurant, id=restaurant_id)
170-
food = get_object_or_404(Food, id=food_id, restaurant=restaurant)
171-
food.delete()
172-
return Response(status=status.HTTP_204_NO_CONTENT)
173-
174-
# TODO: Add new permission

0 commit comments

Comments
 (0)