Skip to content

Commit b2233a3

Browse files
committed
docs: some docs added and fix some bug in BookListAPI app.
1 parent e2102e1 commit b2233a3

File tree

11 files changed

+227
-129
lines changed

11 files changed

+227
-129
lines changed

BookList/settings.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@
3939
'django.contrib.messages',
4040
'django.contrib.staticfiles',
4141
'rest_framework',
42-
'BookListAPI.apps.BooklistapiConfig',
4342
'debug_toolbar',
4443
'rest_framework.authtoken',
4544
'djoser',
4645
'rest_framework_simplejwt',
4746
'rest_framework_simplejwt.token_blacklist',
48-
"restaurant.apps.RestaurantConfig"
47+
"restaurant.apps.RestaurantConfig",
48+
'BookListAPI.apps.BooklistapiConfig',
4949

5050
]
5151

@@ -90,6 +90,9 @@
9090
'default': {
9191
'ENGINE': 'django.db.backends.sqlite3',
9292
'NAME': BASE_DIR / 'db.sqlite3',
93+
},
94+
'TEST': {
95+
'NAME': BASE_DIR / 'test_db.sqlite3',
9396
}
9497
}
9598

@@ -118,7 +121,7 @@
118121

119122
LANGUAGE_CODE = 'en-us'
120123

121-
TIME_ZONE = 'UTC'
124+
TIME_ZONE = 'Asia/Tehran'
122125

123126
USE_I18N = True
124127

@@ -143,7 +146,7 @@
143146
'rest_framework.renderers.BrowsableAPIRenderer',
144147
'rest_framework_xml.renderers.XMLRenderer',
145148
],
146-
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
149+
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
147150
'PAGE_SIZE': 6,
148151
'DEFAULT_AUTHENTICATION_CLASSES': (
149152
'rest_framework.authentication.TokenAuthentication',
@@ -154,7 +157,9 @@
154157
'user': '5/minute',
155158
'anon': '2/minute',
156159
'ten': '10/minute',
157-
}
160+
},
161+
162+
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
158163
}
159164

160165
DJOSER = {
@@ -163,9 +168,14 @@
163168
"SERIALIZERS": {
164169
'user_create': 'BookList.serializers.UserCreateSerializer',
165170
},
171+
"PASSWORD_RESET_CONFIRM_URL": "api/v1/reset_confirm/{uid}/{token}",
166172
}
167173

168174
SIMPLE_JWT = {
169175
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5)
170176
}
171177

178+
179+
# email configuration
180+
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
181+
SITE_NAME = " MY SITE "

BookList/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from django.contrib import admin
1818
from django.urls import path, include, URLPattern
1919
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenBlacklistView
20-
from djoser.urls.base import urlpatterns as base_djoser_urls
20+
2121

2222
urlpatterns = [
2323
path('admin/', admin.site.urls),

BookListAPI/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ def __str__(self):
2222

2323
class Rating(models.Model):
2424
rating = models.SmallIntegerField(null=False, )
25-
book = models.ForeignKey(to=Book, on_delete=models.CASCADE, related_name='ratings')
26-
user = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='ratings')
25+
book = models.ForeignKey(to=Book, on_delete=models.CASCADE, related_name='book_ratings')
26+
user = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='user_ratings')
2727

2828
def __str__(self):
2929
return f'user = {self.user.username} - book = {self.book.title} - rating = {self.rating}'

BookListAPI/serializers.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from rest_framework.renderers import JSONRenderer
99
from rest_framework.parsers import JSONParser
1010
from rest_framework.validators import UniqueTogetherValidator
11+
12+
1113
from io import BytesIO
1214

1315

@@ -51,7 +53,7 @@ class BookSerializer(serializers.ModelSerializer):
5153
price = serializers.DecimalField(source='price', min_value=20, max_digits=10, decimal_places=2)
5254
'''
5355

54-
ratings = serializers.SerializerMethodField(read_only=True)
56+
ratings = serializers.SerializerMethodField(read_only=True, allow_null=True)
5557

5658
class Meta:
5759
model = Book
@@ -67,7 +69,9 @@ def get_full_author_name(self, book: Book) -> str:
6769
return f'mr or ms {book.author}'
6870

6971
def get_ratings(self, book: Book):
70-
return book.ratings.aggregate(Avg('rating'))['rating__avg']
72+
73+
# avg_rating should be annotated in queryset
74+
return book.avg_rating
7175

7276
def validate(self, attrs):
7377
attrs['title'] = bleach.clean(attrs['title'])
@@ -82,14 +86,14 @@ class BookCustomSerializer(serializers.Serializer):
8286

8387

8488
class RatingSerializer(serializers.ModelSerializer):
85-
username = serializers.SlugRelatedField(slug_field='username', read_only=True)
89+
username = serializers.SlugRelatedField(source='user', slug_field='username', read_only=True)
8690
user = serializers.PrimaryKeyRelatedField(write_only=True,
8791
queryset=User.objects.all(),
8892
default=serializers.CurrentUserDefault(),
8993
help_text='id of user who rated this book'
9094
)
9195

92-
book_title = serializers.SlugRelatedField(slug_field='title', read_only=True)
96+
book_title = serializers.SlugRelatedField(source='book', slug_field='title', read_only=True)
9397
book = serializers.PrimaryKeyRelatedField(write_only=True,
9498
queryset=Book.objects.all(),
9599
help_text='id of book to be rated'
@@ -105,6 +109,6 @@ class Meta:
105109
),
106110
)
107111
extra_kwargs = {
108-
'rating': {'min_value': 0, 'max_value': 5, 'allow_null': False,},
112+
'rating': {'min_value': 0, 'max_value': 5, 'allow_null': False},
109113
}
110114

BookListAPI/tests.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,64 @@
1-
from django.test import TestCase
1+
from rest_framework.test import APIClient, APITestCase
2+
from rest_framework import status
3+
from django.contrib.auth.models import User, Group
4+
from django.http import HttpRequest, HttpResponse
5+
from django.urls import reverse_lazy
26

37
# Create your tests here.
8+
sample_users = {
9+
'pooya':
10+
{'username': 'pooya', 'password': 'poy api', 'email': 'poy.ieie@gmail.com', },
11+
'ali':
12+
{'username': 'ali', 'email': 'ali.@gmail.com', 'password': 'ali api'},
13+
}
14+
15+
16+
class SecretTest(APITestCase):
17+
global sample_users
18+
19+
def setUp(self):
20+
self.base_url = 'http://127.0.0.1:8000'
21+
self.login_url = reverse_lazy('login')
22+
self.secret_url = reverse_lazy('book-list-api:secret')
23+
self.client = APIClient()
24+
self.manager_group = Group.objects.create(name='manager')
25+
self.manager_user = User.objects.create_user(**sample_users['pooya'])
26+
self.manager_user.save()
27+
self.manager_user.groups.add(self.manager_group)
28+
29+
self.non_manager_user = User.objects.create_user(**sample_users['ali'])
30+
self.non_manager_user.save()
31+
32+
def __get_token(self, user_credentials) -> str:
33+
""" login user and return token """
34+
response = self.client.post(
35+
path=self.login_url,
36+
data=user_credentials,
37+
)
38+
response_data = response.data
39+
40+
token = response_data['auth_token']
41+
return token
42+
43+
def test_manager_request(self):
44+
token = self.__get_token(sample_users['pooya'])
45+
response = self.client.get(
46+
self.secret_url,
47+
headers={'Authorization': f'Token {token}'},
48+
)
49+
self.assertEqual(response.status_code, status.HTTP_200_OK)
50+
response_data = response.json()
51+
self.assertEqual(response_data['secret_message'], 'The secret of night')
52+
53+
def test_non_manager_request(self):
54+
token = self.__get_token(sample_users['ali'])
55+
response = self.client.get(self.secret_url,
56+
headers={'Authorization': f'Token {token}'},
57+
format='json')
58+
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
59+
response_data = response.json()
60+
self.assertEqual(response_data['secret_message'], 'You are not authorized')
61+
62+
def test_ensure_json_is_default_content_type(self):
63+
response = self.client.get(self.login_url)
64+
self.assertEqual(response.headers['Content-Type'], 'application/json')

BookListAPI/urls.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@
1919
path('secret', secret_message, name='secret'),
2020
path('api-token-auth', obtain_auth_token, name='obtain-auth-token'),
2121

22-
# Rate_limit
22+
# endpoint to check the rate limit and throttling system
2323
path('throttle-check', throttle_check, name='throttle-check'),
24+
25+
# endpoint to add groups
2426
path('users/manage/groups', add_group, name='add-group'),
27+
# Post method only required and username and group_name should be provided.
28+
# user with username will be added to group with group_name.
2529

2630
path('ratings', RatingView.as_view(), name='ratings'),
31+
32+
path('reset_confirm/<str:uid>/<str:token>', reset_pass_confirm, name='reset-pass-confirm'),
2733
]
2834

2935
# simple_router = SimpleRouter(trailing_slash=False)

0 commit comments

Comments
 (0)