From a6b27f9c632caea6c9b5ef4c4a69cdbce96201c9 Mon Sep 17 00:00:00 2001 From: Sanskar Santosh Totla Date: Thu, 22 Jul 2021 11:37:37 +0530 Subject: [PATCH 1/4] completed tasks except ratings --- authentication/forms.py | 12 +++++ authentication/templates/base.html | 25 ++++++++++ authentication/templates/login.html | 25 ++++++++++ authentication/templates/register.html | 26 ++++++++++ authentication/urls.py | 8 +++ authentication/views.py | 42 ++++++++++++++-- library/settings.py | 6 +-- library/urls.py | 5 +- store/migrations/0003_auto_20210720_1757.py | 25 ++++++++++ store/templates/store/base.html | 3 +- store/templates/store/book_detail.html | 1 + store/templates/store/loaned_books.html | 20 +++++++- store/views.py | 55 ++++++++++++++++++--- 13 files changed, 234 insertions(+), 19 deletions(-) create mode 100644 authentication/forms.py create mode 100644 authentication/templates/base.html create mode 100644 authentication/templates/login.html create mode 100644 authentication/templates/register.html create mode 100644 store/migrations/0003_auto_20210720_1757.py diff --git a/authentication/forms.py b/authentication/forms.py new file mode 100644 index 0000000..252c05f --- /dev/null +++ b/authentication/forms.py @@ -0,0 +1,12 @@ +from django import forms +from django.contrib.auth.models import User +from django.contrib.auth.forms import UserCreationForm + +class RegisterForm(UserCreationForm): + first_name = forms.CharField(max_length=30, help_text='Required') + last_name = forms.CharField(max_length=30, required=False, help_text='Optional') + email = forms.EmailField(max_length=100, help_text='Enter a valid email-id') + + class Meta: + model = User + fields = ('first_name', 'last_name', 'username', 'email', 'password1', 'password2') \ No newline at end of file diff --git a/authentication/templates/base.html b/authentication/templates/base.html new file mode 100644 index 0000000..efa3546 --- /dev/null +++ b/authentication/templates/base.html @@ -0,0 +1,25 @@ + +{% load static %} + + + {% block title %} + Authentication + {% endblock %} + + + + + + + + + +
+
+ {% block form %} + Empty Block + {% endblock %} +
+
+ + \ No newline at end of file diff --git a/authentication/templates/login.html b/authentication/templates/login.html new file mode 100644 index 0000000..f626461 --- /dev/null +++ b/authentication/templates/login.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% block title %} + Login +{% endblock %} + +{% block form %} +
+

Login

+ {% csrf_token %} +
+ + +
+
+ + +
+ +
+ {% if message %} + {{message}} + {% endif %} +
+
+{% endblock %} \ No newline at end of file diff --git a/authentication/templates/register.html b/authentication/templates/register.html new file mode 100644 index 0000000..6c79dfb --- /dev/null +++ b/authentication/templates/register.html @@ -0,0 +1,26 @@ +{% extends "login.html" %} + +{% block title %} + Register +{% endblock %} + +{% block form %} +
+

Register

+ {% csrf_token %} + {% for field in form %} +

+ {{ field.label_tag }} +
+ {{ field }} + {% if field.help_text %} + {{field.help_text}} + {% endif %} + {% for error in field.errors %} +

{{ error }}

+ {% endfor %} +

+ {% endfor %} + +
+{% endblock %} diff --git a/authentication/urls.py b/authentication/urls.py index e69de29..2a65466 100644 --- a/authentication/urls.py +++ b/authentication/urls.py @@ -0,0 +1,8 @@ +from django.urls import path,include +from authentication.views import * + +urlpatterns = [ + path('login', loginView, name='Login'), + path('register', registerView, name='Register'), + path('logout', logoutView, name='logout'), +] \ No newline at end of file diff --git a/authentication/views.py b/authentication/views.py index 14dd530..95d2bb9 100644 --- a/authentication/views.py +++ b/authentication/views.py @@ -1,13 +1,47 @@ -from django.shortcuts import render +from django.shortcuts import redirect,render from django.contrib.auth import login,logout,authenticate +from django.contrib.auth.models import User +from django.http import HttpResponse +from .forms import RegisterForm # Create your views here. def loginView(request): - pass + if request.user.is_authenticated: + return redirect('index') + + if request.method == "POST": + data = request.POST + user = authenticate(request, username=data['Username'], password=data['Password']) + if user is not None: + login(request, user) + return redirect('index') + else: + return render(request, 'templates/login.html', {'message': "Username and/or password incorrect"}) + else: + return render(request, 'templates/login.html') + def logoutView(request): - pass + logout(request) + return redirect('index') + def registerView(request): - pass \ No newline at end of file + if request.user.is_authenticated: + return redirect('index') + + if request.method == "POST": + form = RegisterForm(request.POST) + if form.is_valid(): + data = request.POST + new_user = User.objects.create_user(data['username'], data['email'], data['password1']) + new_user.first_name = data['first_name'] + new_user.last_name = data['last_name'] + new_user.save() + login(request, new_user) + return redirect('index') + else: + return render(request, 'templates/register.html', {'form': form}) + else: + return render(request, 'templates/register.html', {'form': RegisterForm()}) \ No newline at end of file diff --git a/library/settings.py b/library/settings.py index 9009d3b..7380c0a 100644 --- a/library/settings.py +++ b/library/settings.py @@ -57,7 +57,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [os.path.join(BASE_DIR, 'authentication/')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -108,7 +108,7 @@ LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Asia/Kolkata' USE_I18N = True @@ -121,7 +121,7 @@ # https://docs.djangoproject.com/en/2.1/howto/static-files/ STATIC_URL = '/static/' -STATIC_ROOT=os.path.join(BASE_DIR,'staticfiles/') +STATIC_ROOT=os.path.join(BASE_DIR,'static/') LOGIN_REDIRECT_URL='/' LOGOUT_REDIRECT_URL = '/' diff --git a/library/urls.py b/library/urls.py index 60b7957..6db2a6b 100644 --- a/library/urls.py +++ b/library/urls.py @@ -19,7 +19,8 @@ from django.conf import settings urlpatterns = [ - path('',include('store.urls')), + path('', include('store.urls')), + path('', include('authentication.urls')), path('admin/', admin.site.urls), - path('accounts/',include('django.contrib.auth.urls')), + # path('accounts/',include('django.contrib.auth.urls')), ]+static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/store/migrations/0003_auto_20210720_1757.py b/store/migrations/0003_auto_20210720_1757.py new file mode 100644 index 0000000..3805d63 --- /dev/null +++ b/store/migrations/0003_auto_20210720_1757.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2.1 on 2021-07-20 12:27 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('store', '0002_auto_20190607_1302'), + ] + + operations = [ + migrations.AlterField( + model_name='bookcopy', + name='borrow_date', + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name='bookcopy', + name='borrower', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrower', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/store/templates/store/base.html b/store/templates/store/base.html index 64b7a6e..b3316e1 100644 --- a/store/templates/store/base.html +++ b/store/templates/store/base.html @@ -29,7 +29,8 @@
  • My Borrowed
  • Logout
  • {% else %} - +
  • Login
  • +
  • Register
  • {% endif %} diff --git a/store/templates/store/book_detail.html b/store/templates/store/book_detail.html index 2e4b9e8..9aa4878 100644 --- a/store/templates/store/book_detail.html +++ b/store/templates/store/book_detail.html @@ -36,6 +36,7 @@

    Title: {{ book.title }}

    window.location.replace("/books/loaned"); } else{ + console.log(data['message']); alert("Unable to issue this book"); } }, diff --git a/store/templates/store/loaned_books.html b/store/templates/store/loaned_books.html index d6f2044..c09ced3 100644 --- a/store/templates/store/loaned_books.html +++ b/store/templates/store/loaned_books.html @@ -37,7 +37,25 @@

    Loaned Books list

    {% endblock %} \ No newline at end of file diff --git a/store/views.py b/store/views.py index dc411b9..c132672 100644 --- a/store/views.py +++ b/store/views.py @@ -2,8 +2,9 @@ from django.shortcuts import get_object_or_404 from store.models import * from django.contrib.auth.decorators import login_required -from django.http import JsonResponse +from django.http import JsonResponse, Http404 from django.views.decorators.csrf import csrf_exempt +from django.utils import timezone # Create your views here. @@ -12,9 +13,11 @@ def index(request): def bookDetailView(request, bid): template_name = 'store/book_detail.html' + book = get_object_or_404(Book, id=bid) + num_available = BookCopy.objects.filter(book=book, status=True).count() context = { - 'book': None, # set this to an instance of the required book - 'num_available': None, # set this to the number of copies of the book available, or 0 if the book isn't available + 'book': book, # set this to an instance of the required book + 'num_available': num_available, # set this to the number of copies of the book available, or 0 if the book isn't available } # START YOUR CODE HERE @@ -25,11 +28,16 @@ def bookDetailView(request, bid): @csrf_exempt def bookListView(request): template_name = 'store/book_list.html' + get_data = request.GET + books = Book.objects.filter( + title__icontains = get_data.get('title',''), + author__icontains = get_data.get('author',''), + genre__icontains = get_data.get('genre',''), + ) context = { - 'books': None, # set this to the list of required books upon filtering using the GET parameters + 'books': books, # set this to the list of required books upon filtering using the GET parameters # (i.e. the book search feature will also be implemented in this view) } - get_data = request.GET # START YOUR CODE HERE @@ -38,8 +46,9 @@ def bookListView(request): @login_required def viewLoanedBooks(request): template_name = 'store/loaned_books.html' + books = BookCopy.objects.filter(borrower=request.user) context = { - 'books': None, + 'books': books, } ''' The above key 'books' in the context dictionary should contain a list of instances of the @@ -62,7 +71,23 @@ def loanBookView(request): If yes, then set the message to 'success', otherwise 'failure' ''' # START YOUR CODE HERE - book_id = None # get the book id from post data + book_id = request.POST['bid'] # get the book id from post data + + try: + book = Book.objects.get(id=book_id) + except: + raise Http404('Book not available') + else: + books = BookCopy.objects.filter(book=book, status=True) + + if books: + books[0].borrower = request.user + books[0].borrow_date = timezone.datetime.today().date() + books[0].status = False + books[0].save() + response_data['message'] = 'success' + else: + response_data['message'] = 'failure' return JsonResponse(response_data) @@ -77,6 +102,20 @@ def loanBookView(request): @csrf_exempt @login_required def returnBookView(request): - pass + response_data = { + 'message': None, + } + + book_copy_id = request.POST['bid'] + book = get_object_or_404(BookCopy, id=book_copy_id) + if book and book.borrower == request.user: + book.borrower = None + book.borrow_date = None + book.status = True + book.save() + response_data['message'] = 'success' + else: + response_data['message'] = 'failure' + return JsonResponse(response_data) From 69c62156d846d55ac347638bd6c5d2e1571b90cb Mon Sep 17 00:00:00 2001 From: Sanskar Santosh Totla Date: Thu, 22 Jul 2021 22:13:26 +0530 Subject: [PATCH 2/4] book ratings added --- authentication/templates/login.html | 2 +- authentication/views.py | 1 - library/settings.py | 2 +- library/urls.py | 1 - store/admin.py | 1 + store/migrations/0004_bookrating.py | 26 +++++++++++++++++ store/models.py | 8 ++++++ store/templates/store/book_detail.html | 40 +++++++++++++++++++++++++- store/urls.py | 1 + store/views.py | 35 ++++++++++++++++++++++ 10 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 store/migrations/0004_bookrating.py diff --git a/authentication/templates/login.html b/authentication/templates/login.html index f626461..30a685c 100644 --- a/authentication/templates/login.html +++ b/authentication/templates/login.html @@ -15,7 +15,7 @@

    Login

    - +
    {% if message %} {{message}} diff --git a/authentication/views.py b/authentication/views.py index 95d2bb9..016cab2 100644 --- a/authentication/views.py +++ b/authentication/views.py @@ -1,7 +1,6 @@ from django.shortcuts import redirect,render from django.contrib.auth import login,logout,authenticate from django.contrib.auth.models import User -from django.http import HttpResponse from .forms import RegisterForm # Create your views here. diff --git a/library/settings.py b/library/settings.py index 7380c0a..4ed78ed 100644 --- a/library/settings.py +++ b/library/settings.py @@ -121,7 +121,7 @@ # https://docs.djangoproject.com/en/2.1/howto/static-files/ STATIC_URL = '/static/' -STATIC_ROOT=os.path.join(BASE_DIR,'static/') +STATIC_ROOT=os.path.join(BASE_DIR,'staticfiles/') LOGIN_REDIRECT_URL='/' LOGOUT_REDIRECT_URL = '/' diff --git a/library/urls.py b/library/urls.py index 6db2a6b..474e5fd 100644 --- a/library/urls.py +++ b/library/urls.py @@ -22,5 +22,4 @@ path('', include('store.urls')), path('', include('authentication.urls')), path('admin/', admin.site.urls), - # path('accounts/',include('django.contrib.auth.urls')), ]+static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/store/admin.py b/store/admin.py index 144f0f1..3d31a26 100644 --- a/store/admin.py +++ b/store/admin.py @@ -4,3 +4,4 @@ admin.site.register(Book) admin.site.register(BookCopy) +admin.site.register(BookRating) diff --git a/store/migrations/0004_bookrating.py b/store/migrations/0004_bookrating.py new file mode 100644 index 0000000..f1d535c --- /dev/null +++ b/store/migrations/0004_bookrating.py @@ -0,0 +1,26 @@ +# Generated by Django 2.2.1 on 2021-07-22 12:42 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('store', '0003_auto_20210720_1757'), + ] + + operations = [ + migrations.CreateModel( + name='BookRating', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rating', models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)])), + ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='store.Book')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/store/models.py b/store/models.py index 17c63a3..e947d81 100644 --- a/store/models.py +++ b/store/models.py @@ -1,5 +1,6 @@ from django.db import models from django.contrib.auth.models import User +from django.core.validators import MinValueValidator, MaxValueValidator # Create your models here. class Book(models.Model): @@ -30,3 +31,10 @@ def __str__(self): else: return f'{self.book.title} - Available' +class BookRating(models.Model): + book = models.ForeignKey(Book, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + rating = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(10)]) + + def __str__(self): + return f'{self.book.title} - Rated {self.rating} by {self.user}' diff --git a/store/templates/store/book_detail.html b/store/templates/store/book_detail.html index 9aa4878..b696b1d 100644 --- a/store/templates/store/book_detail.html +++ b/store/templates/store/book_detail.html @@ -20,6 +20,17 @@

    Title: {{ book.title }}

    Rs. {{ book.mrp }}
    Available Copies:
    {{ num_available }}
    + {% if user.is_authenticated %} + {% if current_user_rated is None %} +
    Rate Book:
    + {% else %} +
    Update Rating:
    + {% endif %} +
    + + +
    + {% endif %} {% endblock %} \ No newline at end of file diff --git a/store/urls.py b/store/urls.py index 4520334..155147b 100644 --- a/store/urls.py +++ b/store/urls.py @@ -8,4 +8,5 @@ path('books/loaned/', viewLoanedBooks, name="view-loaned"), path('books/loan/', loanBookView, name="loan-book"), path('books/return/', returnBookView, name="return-book"), + path('book/rate/', rateBookView, name='rate-book'), ] diff --git a/store/views.py b/store/views.py index c132672..a637eb1 100644 --- a/store/views.py +++ b/store/views.py @@ -2,7 +2,9 @@ from django.shortcuts import get_object_or_404 from store.models import * from django.contrib.auth.decorators import login_required +from django.db.models import Avg from django.http import JsonResponse, Http404 +from django.core.exceptions import ObjectDoesNotExist from django.views.decorators.csrf import csrf_exempt from django.utils import timezone @@ -15,9 +17,14 @@ def bookDetailView(request, bid): template_name = 'store/book_detail.html' book = get_object_or_404(Book, id=bid) num_available = BookCopy.objects.filter(book=book, status=True).count() + try: + current_user_rated = BookRating.objects.get(book=book, user=request.user) + except ObjectDoesNotExist: + current_user_rated = None context = { 'book': book, # set this to an instance of the required book 'num_available': num_available, # set this to the number of copies of the book available, or 0 if the book isn't available + 'current_user_rated': current_user_rated, } # START YOUR CODE HERE @@ -119,3 +126,31 @@ def returnBookView(request): else: response_data['message'] = 'failure' return JsonResponse(response_data) + +@csrf_exempt +@login_required +def rateBookView(request): + if request.method == "POST": + bid = request.POST['bid'] + new_rating = request.POST['rating'] + if int(new_rating) >= int(0) and int(new_rating) <= int(10): + try: + book = Book.objects.get(pk=bid) + user = User.objects.get(username = request.user.username) + previous_user_rating = BookRating.objects.filter(book=book, user=user) + previous_user_rating.delete() + new_book_rating = BookRating() + new_book_rating.user = user + new_book_rating.book = book + new_book_rating.rating = new_rating + new_book_rating.save() + book.rating = BookRating.objects.filter(book=book).aggregate(rating=Avg('rating'))['rating'] + book.save() + except: + return JsonResponse({'message': 'error'}) + else: + return JsonResponse({'message' : 'success'}) + else: + return JsonResponse({'message' : 'Rating must be from 0 to 10'}) + else: + return JsonResponse({'message':"invalid request method"}) From 8bdc88596f34ee46cf3a18136c1bb782d5027449 Mon Sep 17 00:00:00 2001 From: Sanskar Santosh Totla Date: Fri, 23 Jul 2021 10:56:10 +0530 Subject: [PATCH 3/4] added features --- authentication/templates/base.html | 1 - authentication/templates/login.html | 2 ++ authentication/templates/register.html | 2 ++ store/templates/store/book_detail.html | 3 --- store/views.py | 12 ++++++++---- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/authentication/templates/base.html b/authentication/templates/base.html index efa3546..483d93f 100644 --- a/authentication/templates/base.html +++ b/authentication/templates/base.html @@ -17,7 +17,6 @@
    {% block form %} - Empty Block {% endblock %}
    diff --git a/authentication/templates/login.html b/authentication/templates/login.html index 30a685c..d970525 100644 --- a/authentication/templates/login.html +++ b/authentication/templates/login.html @@ -21,5 +21,7 @@

    Login

    {{message}} {% endif %}
    +
    Don't have an account? Register
    +
    I just want to view the books
    {% endblock %} \ No newline at end of file diff --git a/authentication/templates/register.html b/authentication/templates/register.html index 6c79dfb..cb5ef03 100644 --- a/authentication/templates/register.html +++ b/authentication/templates/register.html @@ -22,5 +22,7 @@

    Register

    {% endfor %} +
    Already have an account? Login
    +
    I just want to view the books
    {% endblock %} diff --git a/store/templates/store/book_detail.html b/store/templates/store/book_detail.html index b696b1d..e94d69f 100644 --- a/store/templates/store/book_detail.html +++ b/store/templates/store/book_detail.html @@ -47,7 +47,6 @@

    Title: {{ book.title }}

    window.location.replace("/books/loaned"); } else{ - console.log(data['message']); alert("Unable to issue this book"); } }, @@ -55,13 +54,11 @@

    Title: {{ book.title }}

    alert("Some error occured. Make sure you are logged in before borrowing a book."); window.location.replace("/login"); } - }) }) function rateBook(bid){ const rating = document.getElementById("rating").value - console.log(rating) $.ajax({ url: "{% url 'rate-book'%}", method:"POST", diff --git a/store/views.py b/store/views.py index a637eb1..c0e9dbb 100644 --- a/store/views.py +++ b/store/views.py @@ -1,3 +1,4 @@ +from django.contrib.auth.models import AnonymousUser from django.shortcuts import render from django.shortcuts import get_object_or_404 from store.models import * @@ -17,10 +18,13 @@ def bookDetailView(request, bid): template_name = 'store/book_detail.html' book = get_object_or_404(Book, id=bid) num_available = BookCopy.objects.filter(book=book, status=True).count() - try: - current_user_rated = BookRating.objects.get(book=book, user=request.user) - except ObjectDoesNotExist: + if request.user.is_anonymous: current_user_rated = None + else: + try: + current_user_rated = BookRating.objects.get(book=book, user=request.user) + except ObjectDoesNotExist: + current_user_rated = None context = { 'book': book, # set this to an instance of the required book 'num_available': num_available, # set this to the number of copies of the book available, or 0 if the book isn't available @@ -88,9 +92,9 @@ def loanBookView(request): books = BookCopy.objects.filter(book=book, status=True) if books: - books[0].borrower = request.user books[0].borrow_date = timezone.datetime.today().date() books[0].status = False + books[0].borrower = request.user books[0].save() response_data['message'] = 'success' else: From 5255dbd51dc4346c7eaa2fb1402436baee02d368 Mon Sep 17 00:00:00 2001 From: Sanskar Santosh Totla Date: Fri, 23 Jul 2021 12:30:05 +0530 Subject: [PATCH 4/4] rating improvement --- authentication/templates/base.html | 6 ++++-- authentication/templates/login.html | 14 +++++++------- store/models.py | 2 +- store/views.py | 23 +++++++++++++---------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/authentication/templates/base.html b/authentication/templates/base.html index 483d93f..f87879a 100644 --- a/authentication/templates/base.html +++ b/authentication/templates/base.html @@ -8,9 +8,11 @@ - + + - + + diff --git a/authentication/templates/login.html b/authentication/templates/login.html index d970525..7855781 100644 --- a/authentication/templates/login.html +++ b/authentication/templates/login.html @@ -7,15 +7,15 @@

    Login

    {% csrf_token %} -
    - - +
    + +
    -
    - - +
    + +
    - +
    {% if message %} {{message}} diff --git a/store/models.py b/store/models.py index e947d81..d42c2d0 100644 --- a/store/models.py +++ b/store/models.py @@ -34,7 +34,7 @@ def __str__(self): class BookRating(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE) - rating = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(10)]) + rating = models.IntegerField(default=None, validators=[MinValueValidator(0), MaxValueValidator(10)]) def __str__(self): return f'{self.book.title} - Rated {self.rating} by {self.user}' diff --git a/store/views.py b/store/views.py index c0e9dbb..3f2e34f 100644 --- a/store/views.py +++ b/store/views.py @@ -141,20 +141,23 @@ def rateBookView(request): try: book = Book.objects.get(pk=bid) user = User.objects.get(username = request.user.username) - previous_user_rating = BookRating.objects.filter(book=book, user=user) - previous_user_rating.delete() - new_book_rating = BookRating() - new_book_rating.user = user - new_book_rating.book = book - new_book_rating.rating = new_rating - new_book_rating.save() + try: + previous_user_rating = BookRating.objects.get(book=book, user=user) + except ObjectDoesNotExist: + previous_user_rating = BookRating() + if previous_user_rating.rating is None: + previous_user_rating.book = book + previous_user_rating.user = user + previous_user_rating.rating = new_rating + previous_user_rating.save() book.rating = BookRating.objects.filter(book=book).aggregate(rating=Avg('rating'))['rating'] + book.rating = round(book.rating, 2) book.save() except: return JsonResponse({'message': 'error'}) else: - return JsonResponse({'message' : 'success'}) + return JsonResponse({'message': 'success'}) else: - return JsonResponse({'message' : 'Rating must be from 0 to 10'}) + return JsonResponse({'message': 'Rating must be from 0 to 10'}) else: - return JsonResponse({'message':"invalid request method"}) + return JsonResponse({'message': "invalid request method"})