From f504a73397b36f7a5447cee716853a36440cfd59 Mon Sep 17 00:00:00 2001 From: Awoyalejohn Date: Tue, 30 Aug 2022 02:31:16 +0100 Subject: [PATCH] Update apps to be pep8 compliant --- cart/contexts.py | 36 ++++--- cart/urls.py | 12 ++- cart/views.py | 57 +++++----- checkout/admin.py | 60 +++++++---- checkout/forms.py | 42 ++++---- checkout/models.py | 65 +++++++---- checkout/signals.py | 2 + checkout/urls.py | 31 ++++-- checkout/views.py | 201 ++++++++++++++++++++--------------- checkout/webhook_handler.py | 67 ++++++------ checkout/webhooks.py | 17 +-- graphicsdesignspace/urls.py | 27 +++-- graphicsdesignspace/views.py | 8 +- home/urls.py | 4 +- home/views.py | 5 +- products/admin.py | 9 +- products/forms.py | 7 +- products/models.py | 15 +-- products/urls.py | 18 ++-- products/views.py | 178 +++++++++++++++++-------------- profiles/forms.py | 40 ++++--- profiles/models.py | 28 +++-- profiles/urls.py | 42 ++++++-- profiles/views.py | 117 ++++++++++++-------- reviews/admin.py | 5 +- reviews/choices.py | 11 +- reviews/forms.py | 2 +- reviews/models.py | 15 +-- reviews/urls.py | 14 ++- reviews/views.py | 58 +++++----- testimonials/admin.py | 9 +- testimonials/forms.py | 6 +- testimonials/models.py | 10 +- testimonials/urls.py | 36 +++++-- testimonials/views.py | 100 ++++++++++------- wishlist/admin.py | 6 +- wishlist/models.py | 20 ++-- wishlist/urls.py | 18 +++- wishlist/views.py | 76 +++++++------ 39 files changed, 894 insertions(+), 580 deletions(-) diff --git a/cart/contexts.py b/cart/contexts.py index b58ca52..3700b41 100644 --- a/cart/contexts.py +++ b/cart/contexts.py @@ -3,26 +3,29 @@ from django.shortcuts import get_object_or_404 from products.models import Product + def cart_contents(request): cart_items = [] subtotal = 0 product_count = 0 - cart = request.session.get('cart',{}) + cart = request.session.get("cart", {}) for slug, quantity in cart.items(): product = get_object_or_404(Product, slug=slug) subtotal += product.price product_count += quantity - cart_items.append({ - 'slug': slug, - 'quantity': quantity, - 'product': product, - }) + cart_items.append( + { + "slug": slug, + "quantity": quantity, + "product": product, + } + ) if subtotal > settings.DISCOUNT_THRESHOLD: discount = subtotal * Decimal(settings.DISCOUNT_PERCENTAGE / 100) discount_delta = 0 - + else: discount = 0 discount_delta = settings.DISCOUNT_THRESHOLD - subtotal @@ -30,15 +33,14 @@ def cart_contents(request): total = subtotal - discount context = { - 'cart_items': cart_items, - 'subtotal': subtotal, - 'product_count': product_count, - 'discount': discount, - 'discount_delta': discount_delta, - 'discount_threshold': settings.DISCOUNT_THRESHOLD, - 'discount_percentage': settings.DISCOUNT_PERCENTAGE, - 'total': total, - + "cart_items": cart_items, + "subtotal": subtotal, + "product_count": product_count, + "discount": discount, + "discount_delta": discount_delta, + "discount_threshold": settings.DISCOUNT_THRESHOLD, + "discount_percentage": settings.DISCOUNT_PERCENTAGE, + "total": total, } - return context \ No newline at end of file + return context diff --git a/cart/urls.py b/cart/urls.py index 1ce3d6a..c750734 100644 --- a/cart/urls.py +++ b/cart/urls.py @@ -2,7 +2,11 @@ from cart.views import DisplayCartView, AddToCart, RemoveFromCart urlpatterns = [ - path('', DisplayCartView.as_view(), name='display_cart'), - path('add//', AddToCart.as_view(), name='add_to_cart'), - path('remove//', RemoveFromCart.as_view(), name='remove_from_cart'), -] \ No newline at end of file + path("", DisplayCartView.as_view(), name="display_cart"), + path("add//", AddToCart.as_view(), name="add_to_cart"), + path( + "remove//", + RemoveFromCart.as_view(), + name="remove_from_cart" + ), +] diff --git a/cart/views.py b/cart/views.py index b380a21..08335fc 100644 --- a/cart/views.py +++ b/cart/views.py @@ -4,55 +4,62 @@ from django.views.generic import View from django.views.generic import TemplateView -# Create your views here. class DisplayCartView(TemplateView): - """ A view that displays the contents of the cart """ - + """A view that displays the contents of the cart""" + template_name = "cart/cart.html" class AddToCart(View): - """ A view to add items to the cart page """ + """A view to add items to the cart page""" + def post(self, request, slug): product = get_object_or_404(Product, slug=slug) quantity = 1 - #gets the redirect url - redirect_url = request.POST.get('redirect_url') - #checks if the cart is in the session and then creates an empty dict if it isn't - cart = request.session.get('cart', {}) - - #checks if the slug is in the cart dictionary + # gets the redirect url + redirect_url = request.POST.get("redirect_url") + # checks if the cart is in the session + # and then creates an empty dict if it isn't + cart = request.session.get("cart", {}) + + # checks if the slug is in the cart dictionary if slug in list(cart.keys()): - messages.error(request, f"You already added {product.name} to your cart!") + messages.error( + request, + f"You already added {product.name} to your cart!" + ) return redirect(redirect_url) - + else: cart[slug] = quantity - messages.success(request, f'Added {product.name} to your cart') + messages.success( + request, + f"Added {product.name} to your cart" + ) + + # Adds the amount of that item to its value in the dictionary + request.session["cart"] = cart - #Adds the amount of that item to its value in the dictionary - request.session['cart'] = cart - return redirect(redirect_url) class RemoveFromCart(View): - """ A view to remove an item from the cart page """ + """A view to remove an item from the cart page""" + def post(self, request, slug): try: product = get_object_or_404(Product, slug=slug) - #checks if the cart is in the session and then creates an empty dict if it isn't - cart = request.session.get('cart', {}) + # checks if the cart is in the session + # and then creates an empty dict if it isn't + cart = request.session.get("cart", {}) - #deletes the item in the list + # deletes the item in the list cart.pop(slug) - messages.success(request, f'Removed {product.name} from your cart') + messages.success(request, f"Removed {product.name} from your cart") - request.session['cart'] = cart + request.session["cart"] = cart return HttpResponse(status=200) except Exception as e: - messages.error(request, f'Error removing item: {e}') + messages.error(request, f"Error removing item: {e}") return HttpResponse(status=500) - - diff --git a/checkout/admin.py b/checkout/admin.py index 3a1aae4..dfeab5b 100644 --- a/checkout/admin.py +++ b/checkout/admin.py @@ -1,31 +1,55 @@ from django.contrib import admin from .models import Order, OrderLineItem -# Register your models here.class OrderAdmin(admin.ModelAdmin): + class OrderLineItemAdminInline(admin.TabularInline): model = OrderLineItem - readonly_fields = ('lineitem_subtotal',) + readonly_fields = ("lineitem_subtotal",) class OrderAdmin(admin.ModelAdmin): inlines = (OrderLineItemAdminInline,) - readonly_fields = ('order_number', 'date', - 'discount', 'order_subtotal', - 'total','original_cart', - 'stripe_pid') - - fields = ('order_number', 'user_profile', 'date', 'full_name', - 'email', 'phone_number', 'country', - 'postcode', 'town_or_city', 'street_address1', - 'street_address2', 'county', 'discount', - 'order_subtotal', 'total', 'original_cart', - 'stripe_pid') - - list_display = ('order_number', 'date', 'full_name', - 'order_subtotal', 'discount', - 'total',) + readonly_fields = ( + "order_number", + "date", + "discount", + "order_subtotal", + "total", + "original_cart", + "stripe_pid", + ) + + fields = ( + "order_number", + "user_profile", + "date", + "full_name", + "email", + "phone_number", + "country", + "postcode", + "town_or_city", + "street_address1", + "street_address2", + "county", + "discount", + "order_subtotal", + "total", + "original_cart", + "stripe_pid", + ) + + list_display = ( + "order_number", + "date", + "full_name", + "order_subtotal", + "discount", + "total", + ) + + ordering = ("-date",) - ordering = ('-date',) admin.site.register(Order, OrderAdmin) diff --git a/checkout/forms.py b/checkout/forms.py index 7e3ad4f..1517334 100644 --- a/checkout/forms.py +++ b/checkout/forms.py @@ -5,10 +5,17 @@ class OrderForm(forms.ModelForm): class Meta: model = Order - fields = ('full_name', 'email', 'phone_number', - 'street_address1', 'street_address2', - 'town_or_city', 'postcode', 'country', - 'county',) + fields = ( + "full_name", + "email", + "phone_number", + "street_address1", + "street_address2", + "town_or_city", + "postcode", + "country", + "county", + ) def __init__(self, *args, **kwargs): """ @@ -17,23 +24,22 @@ def __init__(self, *args, **kwargs): """ super().__init__(*args, **kwargs) placeholders = { - 'full_name': 'Full Name', - 'email': 'Email Address', - 'phone_number': 'Phone Number', - 'postcode': 'Postal Code', - 'town_or_city': 'Town or City', - 'street_address1': 'Street Address 1', - 'street_address2': 'Street Address 2', - 'county': 'County, State or Locality', + "full_name": "Full Name", + "email": "Email Address", + "phone_number": "Phone Number", + "postcode": "Postal Code", + "town_or_city": "Town or City", + "street_address1": "Street Address 1", + "street_address2": "Street Address 2", + "county": "County, State or Locality", } - self.fields['full_name'].widget.attrs['autofocus'] = True + self.fields["full_name"].widget.attrs["autofocus"] = True for field in self.fields: - if field != 'country': + if field != "country": if self.fields[field].required: - placeholder = f'{placeholders[field]} *' + placeholder = f"{placeholders[field]} *" else: placeholder = placeholders[field] - self.fields[field].widget.attrs['placeholder'] = placeholder - # self.fields[field].widget.attrs['class'] = 'stripe-style-input' - self.fields[field].label = False \ No newline at end of file + self.fields[field].widget.attrs["placeholder"] = placeholder + self.fields[field].label = False diff --git a/checkout/models.py b/checkout/models.py index a0f8d78..b77b204 100644 --- a/checkout/models.py +++ b/checkout/models.py @@ -9,31 +9,41 @@ from profiles.models import UserProfile - -# Create your models here. class Order(models.Model): order_number = models.CharField(max_length=32, null=False, editable=False) - user_profile = models.ForeignKey(UserProfile, on_delete=models.SET_NULL, - null=True, blank=True, related_name='orders') + user_profile = models.ForeignKey( + UserProfile, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="orders", + ) full_name = models.CharField(max_length=50, null=False, blank=False) email = models.EmailField(max_length=254, null=False, blank=False) phone_number = models.CharField(max_length=20, null=False, blank=False) - country = CountryField(blank_label='Country *', null=False, blank=False) + country = CountryField(blank_label="Country *", null=False, blank=False) postcode = models.CharField(max_length=20, null=True, blank=True) town_or_city = models.CharField(max_length=40, null=False, blank=False) street_address1 = models.CharField(max_length=80, null=False, blank=False) street_address2 = models.CharField(max_length=80, null=True, blank=True) county = models.CharField(max_length=80, null=True, blank=True) date = models.DateTimeField(auto_now_add=True) - discount = models.DecimalField(max_digits=6, decimal_places=2, null=False, default=0) - order_subtotal = models.DecimalField(max_digits=10, decimal_places=2, null=False, default=0) - total = models.DecimalField(max_digits=10, decimal_places=2, null=False, default=0) - original_cart = models.TextField(null=False, blank=False, default='') - stripe_pid = models.CharField(max_length=254, null=False, blank=False, default='') + discount = models.DecimalField( + max_digits=6, decimal_places=2, null=False, default=0 + ) + order_subtotal = models.DecimalField( + max_digits=10, decimal_places=2, null=False, default=0 + ) + total = models.DecimalField( + max_digits=10, decimal_places=2, null=False, default=0 + ) + original_cart = models.TextField(null=False, blank=False, default="") + stripe_pid = models.CharField( + max_length=254, null=False, blank=False, default="" + ) class Meta: - ordering = ['-date'] - + ordering = ["-date"] def _generate_order_number(self): """ @@ -46,9 +56,15 @@ def update_subtotal(self): Update total each time a line item is added, accounting for discounts """ - self.order_subtotal = self.lineitems.aggregate(Sum('lineitem_subtotal'))['lineitem_subtotal__sum'] or 0 + self.order_subtotal = ( + self.lineitems.aggregate( + Sum("lineitem_subtotal") + )["lineitem_subtotal__sum"] or 0 + ) if self.order_subtotal > settings.DISCOUNT_THRESHOLD: - self.discount = self.order_subtotal * settings.DISCOUNT_PERCENTAGE / 100 + self.discount = ( + self.order_subtotal * settings.DISCOUNT_PERCENTAGE / 100 + ) else: self.discount = 0 self.total = self.order_subtotal - self.discount @@ -66,13 +82,22 @@ def save(self, *args, **kwargs): def __str__(self): return self.order_number - + class OrderLineItem(models.Model): - order = models.ForeignKey(Order, null=False, blank=False, on_delete=models.CASCADE, related_name='lineitems') - product = models.ForeignKey(Product, null=False, blank=False, on_delete=models.CASCADE) + order = models.ForeignKey( + Order, + null=False, + blank=False, + on_delete=models.CASCADE, + related_name="lineitems", + ) + product = models.ForeignKey( + Product, null=False, blank=False, on_delete=models.CASCADE + ) quantity = models.IntegerField(null=False, blank=False, default=0) - lineitem_subtotal = models.DecimalField(max_digits=6, decimal_places=2, null=False, blank=False, editable=False) - + lineitem_subtotal = models.DecimalField( + max_digits=6, decimal_places=2, null=False, blank=False, editable=False + ) def save(self, *args, **kwargs): """ @@ -83,4 +108,4 @@ def save(self, *args, **kwargs): super().save(*args, **kwargs) def __str__(self): - return f'order {self.order.order_number}' \ No newline at end of file + return f"order {self.order.order_number}" diff --git a/checkout/signals.py b/checkout/signals.py index d923b65..66299bc 100644 --- a/checkout/signals.py +++ b/checkout/signals.py @@ -3,6 +3,7 @@ from .models import OrderLineItem + @receiver(post_save, sender=OrderLineItem) def update_on_save(sender, instance, created, **kwargs): """ @@ -10,6 +11,7 @@ def update_on_save(sender, instance, created, **kwargs): """ instance.order.update_subtotal() + @receiver(post_delete, sender=OrderLineItem) def update_on_delete(sender, instance, **kwargs): """ diff --git a/checkout/urls.py b/checkout/urls.py index 841f79a..207f973 100644 --- a/checkout/urls.py +++ b/checkout/urls.py @@ -1,11 +1,28 @@ from django.urls import path -from checkout.views import Checkout, CheckoutSuccess, cache_checkout_data, CheckoutDownloads +from checkout.views import ( + Checkout, + CheckoutSuccess, + cache_checkout_data, + CheckoutDownloads, +) from .webhooks import webhook urlpatterns = [ - path('', Checkout.as_view(), name='checkout'), - path('checkout_success/', CheckoutSuccess.as_view(), name='checkout_success'), - path('checkout_success//downloads', CheckoutDownloads.as_view(), name='checkout_downloads'), - path('cache_checkout_data/', cache_checkout_data, name='cache_checkout_data'), - path('wh/', webhook, name='webhook'), -] \ No newline at end of file + path("", Checkout.as_view(), name="checkout"), + path( + "checkout_success/", + CheckoutSuccess.as_view(), + name="checkout_success", + ), + path( + "checkout_success//downloads", + CheckoutDownloads.as_view(), + name="checkout_downloads", + ), + path( + "cache_checkout_data/", + cache_checkout_data, + name="cache_checkout_data" + ), + path("wh/", webhook, name="webhook"), +] diff --git a/checkout/views.py b/checkout/views.py index 63b069d..bd975e0 100644 --- a/checkout/views.py +++ b/checkout/views.py @@ -1,9 +1,13 @@ -from django.shortcuts import get_object_or_404, render, redirect, reverse, HttpResponse +from django.shortcuts import ( + get_object_or_404, + render, redirect, + reverse, + HttpResponse + ) from django.views.decorators.http import require_POST from django.contrib import messages from django.conf import settings from django.views.generic import View, TemplateView -from django.views.generic.detail import DetailView from .forms import OrderForm from .models import Order, OrderLineItem from products.models import Product @@ -16,44 +20,48 @@ import json - - - - - @require_POST def cache_checkout_data(request): try: req_json = json.loads(request.body) - pid = req_json['client_secret'].split('_secret')[0] + pid = req_json["client_secret"].split("_secret")[0] stripe.api_key = settings.STRIPE_SECRET_KEY - stripe.PaymentIntent.modify(pid, metadata={ - 'cart': json.dumps(request.session.get('cart', {})), - 'save_info': req_json['save_info'], - 'username': request.user, - }) + stripe.PaymentIntent.modify( + pid, + metadata={ + "cart": json.dumps(request.session.get("cart", {})), + "save_info": req_json["save_info"], + "username": request.user, + }, + ) return HttpResponse(status=200) except Exception as e: - messages.error(request, 'Sorry, your payment cannot be \ - processed right now. Please try again later.') + messages.error( + request, + "Sorry, your payment cannot be \ + processed right now. Please try again later.", + ) return HttpResponse(content=e, status=400) - # Create your views here. class Checkout(View): - """ A view to allow purchasing of the items from the checkout page """ + """A view to allow purchasing of the items from the checkout page""" + def get(self, request): stripe_public_key = settings.STRIPE_PUBLIC_KEY stripe_secret_key = settings.STRIPE_SECRET_KEY - cart = request.session.get('cart', {}) + cart = request.session.get("cart", {}) if not cart: - messages.error(request, "There's nothing in your cart at the moment") - return redirect(reverse('products')) + messages.error( + request, + "There's nothing in your cart at the moment" + ) + return redirect(reverse("products")) current_cart = cart_contents(request) - total = current_cart['total'] + total = current_cart["total"] stripe_total = round(total * 100) stripe.api_key = stripe_secret_key intent = stripe.PaymentIntent.create( @@ -61,60 +69,65 @@ def get(self, request): currency=settings.STRIPE_CURRENCY, ) - - # Checks if the user is authenticated, if it is true it gets the profile - # and prefills all its fields with the relaevant data for the order form + # Checks if the user is authenticated, + # if it is true it gets the profile + # and prefills all its fields with + # the relaevant data for the order form if self.request.user.is_authenticated: try: profile = UserProfile.objects.get(user=self.request.user) - order_form = OrderForm(initial={ - 'full_name': profile.user.get_full_name(), - 'email': profile.user.email, - 'phone_number': profile.default_phone_number, - 'country': profile.default_country, - 'postcode': profile.default_postcode, - 'town_or_city': profile.default_town_or_city, - 'street_address1': profile.default_street_address1, - 'street_address2': profile.default_street_address2, - 'county': profile.default_county, - }) + order_form = OrderForm( + initial={ + "full_name": profile.user.get_full_name(), + "email": profile.user.email, + "phone_number": profile.default_phone_number, + "country": profile.default_country, + "postcode": profile.default_postcode, + "town_or_city": profile.default_town_or_city, + "street_address1": profile.default_street_address1, + "street_address2": profile.default_street_address2, + "county": profile.default_county, + } + ) except UserProfile.DoesNotExist: order_form = OrderForm() else: order_form = OrderForm() if not stripe_public_key: - messages.warning(request, 'Stripe public key is missing. \ - Did you forget to set in your environment?') + messages.warning( + request, + "Stripe public key is missing. \ + Did you forget to set in your environment?", + ) - template = 'checkout/checkout.html' + template = "checkout/checkout.html" context = { - 'order_form': order_form, - 'stripe_public_key': stripe_public_key, - 'client_secret': intent.client_secret, + "order_form": order_form, + "stripe_public_key": stripe_public_key, + "client_secret": intent.client_secret, } return render(request, template, context) - def post(self, request): - cart = request.session.get('cart', {}) + cart = request.session.get("cart", {}) form_data = { - 'full_name': request.POST['full_name'], - 'email': request.POST['email'], - 'phone_number': request.POST['phone_number'], - 'country': request.POST['country'], - 'postcode': request.POST['postcode'], - 'town_or_city': request.POST['town_or_city'], - 'street_address1': request.POST['street_address1'], - 'street_address2': request.POST['street_address2'], - 'county': request.POST['county'], + "full_name": request.POST["full_name"], + "email": request.POST["email"], + "phone_number": request.POST["phone_number"], + "country": request.POST["country"], + "postcode": request.POST["postcode"], + "town_or_city": request.POST["town_or_city"], + "street_address1": request.POST["street_address1"], + "street_address2": request.POST["street_address2"], + "county": request.POST["county"], } order_form = OrderForm(form_data) if order_form.is_valid(): order = order_form.save(commit=False) - pid = request.POST.get('client_secret').split('_secret')[0] + pid = request.POST.get("client_secret").split("_secret")[0] order.stripe_pid = pid order.original_cart = json.dumps(cart) order.save() @@ -122,32 +135,40 @@ def post(self, request): try: product = Product.objects.get(slug=slug) order_line_item = OrderLineItem( - order=order, - product=product, - quantity=item_data + order=order, product=product, quantity=item_data ) order_line_item.save() except Product.DoesNotExist: - messages.error(request, ( - "One of the products in your cart wasn't found in our database. " - "Please call us for assistance!") + messages.error( + request, + ( + "One of the products in your cart \ + wasn't found in our database." + "Please call us for assistance!" + ), ) order.delete() - return redirect(reverse('display_cart')) + return redirect(reverse("display_cart")) - request.session['save_info'] = 'save-info' in request.POST - return redirect(reverse('checkout_success', args=[order.order_number])) + request.session["save_info"] = "save-info" in request.POST + return redirect( + reverse("checkout_success", args=[order.order_number]) + ) else: - messages.error(request, 'There was an error with your form. \ - Please double check your information.') + messages.error( + request, + "There was an error with your form. \ + Please double check your information.", + ) class CheckoutSuccess(View): """ A view to handle successful checkouts """ + def get(self, request, order_number): - save_info = request.session.get('save_info') + save_info = request.session.get("save_info") order = get_object_or_404(Order, order_number=order_number) if self.request.user.is_authenticated: @@ -159,39 +180,45 @@ def get(self, request, order_number): # Save the user's info if save_info: profile_data = { - 'default_phone_number': order.phone_number, - 'default_country': order.country, - 'default_postcode': order.postcode, - 'default_town_or_city': order.town_or_city, - 'default_street_address1': order.street_address1, - 'default_street_address2': order.street_address2, - 'default_county': order.county, + "default_phone_number": order.phone_number, + "default_country": order.country, + "default_postcode": order.postcode, + "default_town_or_city": order.town_or_city, + "default_street_address1": order.street_address1, + "default_street_address2": order.street_address2, + "default_county": order.county, } - user_profile_form = UserProfileForm(profile_data, instance=profile) + user_profile_form = UserProfileForm( + profile_data, + instance=profile + ) if user_profile_form.is_valid(): user_profile_form.save() - - messages.success(request, f"Order successfuly processed! \ + + messages.success( + request, + f"Order successfuly processed! \ Your order number is {order_number}. A confirmation \ - email will be sent to {order.email}.") - - if 'cart' in request.session: - del request.session['cart'] - - template = 'checkout/checkout_success.html' - context = {'order':order,} - return render(request, template, context) + email will be sent to {order.email}.", + ) + if "cart" in request.session: + del request.session["cart"] + template = "checkout/checkout_success.html" + context = { + "order": order, + } + return render(request, template, context) class CheckoutDownloads(TemplateView): - """ A view display orders for users to download """ - template_name = 'checkout/checkout_downloads.html' + """A view display orders for users to download""" + + template_name = "checkout/checkout_downloads.html" def get_context_data(self, order_number, **kwargs): context = super(CheckoutDownloads, self).get_context_data(**kwargs) order = get_object_or_404(Order, order_number=order_number) - context['order'] = order + context["order"] = order return context - \ No newline at end of file diff --git a/checkout/webhook_handler.py b/checkout/webhook_handler.py index 644c850..b28b25b 100644 --- a/checkout/webhook_handler.py +++ b/checkout/webhook_handler.py @@ -21,26 +21,23 @@ def _send_confirmation_email(self, order): """Send the user a confirmation email""" cust_email = order.email subject = render_to_string( - 'checkout/confirmation_emails/confirmation_email_subject.txt', - {'order': order}) + "checkout/confirmation_emails/confirmation_email_subject.txt", + {"order": order}, + ) body = render_to_string( - 'checkout/confirmation_emails/confirmation_email_body.txt', - {'order': order, 'contact_email': settings.DEFAULT_FROM_EMAIL}) - - send_mail( - subject, - body, - settings.DEFAULT_FROM_EMAIL, - [cust_email] + "checkout/confirmation_emails/confirmation_email_body.txt", + {"order": order, "contact_email": settings.DEFAULT_FROM_EMAIL}, ) + send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [cust_email]) + def handle_event(self, event): """ Handle a generic/unknown/unexpected webhook event """ return HttpResponse( - content=f'Unhandled webhook received: {event["type"]}', - status=200) + content=f'Unhandled webhook received: {event["type"]}', status=200 + ) def handle_payment_intent_succeeded(self, event): """ @@ -55,7 +52,7 @@ def handle_payment_intent_succeeded(self, event): shipping_details = intent.shipping total = round(intent.charges.data[0].amount / 100, 2) - #Clean data in the shipping details + # Clean data in the shipping details for field, value in shipping_details.address.items(): if value == "": shipping_details.address[field] = None @@ -63,15 +60,19 @@ def handle_payment_intent_succeeded(self, event): # Update profile information if save_info was checked profile = None username = intent.metadata.username - if username != 'AnonymousUser': + if username != "AnonymousUser": profile = UserProfile.objects.get(user__username=username) if save_info: profile.default_phone_number = shipping_details.phone profile.default_country = shipping_details.address.country profile.default_postcode = shipping_details.address.postal_code profile.default_town_or_city = shipping_details.address.city - profile.default_street_address1 = shipping_details.address.line1 - profile.default_street_address2 = shipping_details.address.line2 + profile.default_street_address1 = ( + shipping_details.address.line1 + ) + profile.default_street_address2 = ( + shipping_details.address.line2 + ) profile.default_county = shipping_details.address.state profile.save() @@ -96,15 +97,16 @@ def handle_payment_intent_succeeded(self, event): order_exists = True break - except Order.DoesNotExist: attempt += 1 time.sleep(1) if order_exists: - self._send_confirmation_email(order) + self._send_confirmation_email(order) return HttpResponse( - content=f'Webhook received: {event["type"]} | SUCCESS: Verified order already in database', - status=200) + content=f'Webhook received: {event["type"]} | \ + SUCCESS: Verified order already in database', + status=200, + ) else: order = None try: @@ -125,22 +127,24 @@ def handle_payment_intent_succeeded(self, event): for slug, item_data in json.loads(cart).items(): product = Product.objects.get(slug=slug) order_line_item = OrderLineItem( - order=order, - product=product, - quantity=item_data + order=order, product=product, quantity=item_data ) order_line_item.save() - except Exception as e: + except Exception as e: if order: order.delete() return HttpResponse( - content=f'Webhook received: {event["type"]} | ERROR: {e}', - status=500) + content=f'Webhook received: {event["type"]} | \ + ERROR: {e}', + status=500, + ) - self._send_confirmation_email(order) + self._send_confirmation_email(order) return HttpResponse( - content=f'Webhook received: {event["type"]} | SUCCESS: Created order in webhook', - status=200) + content=f'Webhook received: {event["type"]} | \ + SUCCESS: Created order in webhook', + status=200, + ) def handle_payment_intent_payment_failed(self, event): """ @@ -148,6 +152,5 @@ def handle_payment_intent_payment_failed(self, event): """ return HttpResponse( content=f'Webhook received: {event["type"]}', - status=200) - - + status=200 + ) diff --git a/checkout/webhooks.py b/checkout/webhooks.py index 9b0b6c9..a85cfde 100644 --- a/checkout/webhooks.py +++ b/checkout/webhooks.py @@ -7,6 +7,7 @@ import stripe + @require_POST @csrf_exempt def webhook(request): @@ -17,13 +18,11 @@ def webhook(request): # Get the webhook data and verify its signature payload = request.body - sig_header = request.META['HTTP_STRIPE_SIGNATURE'] + sig_header = request.META["HTTP_STRIPE_SIGNATURE"] event = None try: - event = stripe.Webhook.construct_event( - payload, sig_header, wh_secret - ) + event = stripe.Webhook.construct_event(payload, sig_header, wh_secret) except ValueError as e: # Invalid payload return HttpResponse(status=400) @@ -38,12 +37,14 @@ def webhook(request): # Map webhook events to relevant handler functions event_map = { - 'payment_intent.succeeded': handler.handle_payment_intent_succeeded, - 'payment_intent.payment_failed': handler.handle_payment_intent_payment_failed, + "payment_intent.succeeded": handler.handle_payment_intent_succeeded, + "payment_intent.payment_failed": ( + handler.handle_payment_intent_payment_failed, + ) } # Get the webhook type from Stripe - event_type = event['type'] + event_type = event["type"] # If there's a handler for it, get it from the event map # Use the generic one by default @@ -51,4 +52,4 @@ def webhook(request): # Call the event handler with the event response = event_handler(event) - return response \ No newline at end of file + return response diff --git a/graphicsdesignspace/urls.py b/graphicsdesignspace/urls.py index 754cd9b..529ec58 100644 --- a/graphicsdesignspace/urls.py +++ b/graphicsdesignspace/urls.py @@ -17,20 +17,19 @@ from django.urls import path, include from django.conf import settings from django.conf.urls.static import static -from .views import handler404 urlpatterns = [ - path('admin/', admin.site.urls), - path('accounts/', include('allauth.urls')), - path('', include('home.urls')), - path('products/', include('products.urls')), - path('cart/', include('cart.urls')), - path('checkout/', include('checkout.urls')), - path('profile/', include('profiles.urls')), - path('quotes/', include('quotes.urls')), - path('reviews/', include('reviews.urls')), - path('testimonials/', include('testimonials.urls')), - path('wishlist/', include('wishlist.urls')), + path("admin/", admin.site.urls), + path("accounts/", include("allauth.urls")), + path("", include("home.urls")), + path("products/", include("products.urls")), + path("cart/", include("cart.urls")), + path("checkout/", include("checkout.urls")), + path("profile/", include("profiles.urls")), + path("quotes/", include("quotes.urls")), + path("reviews/", include("reviews.urls")), + path("testimonials/", include("testimonials.urls")), + path("wishlist/", include("wishlist.urls")), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -handler404 = 'graphicsdesignspace.views.handler404' -handler403 = 'graphicsdesignspace.views.handler403' +handler404 = "graphicsdesignspace.views.handler404" +handler403 = "graphicsdesignspace.views.handler403" diff --git a/graphicsdesignspace/views.py b/graphicsdesignspace/views.py index 9417abb..2827f3b 100644 --- a/graphicsdesignspace/views.py +++ b/graphicsdesignspace/views.py @@ -1,9 +1,11 @@ from django.shortcuts import render + def handler404(request, exception): - """ Error Handler 404 - Page Not Found """ + """Error Handler 404 - Page Not Found""" return render(request, "errors/404.html", status=404) + def handler403(request, exception): - """ Error Handler 403 - Forbidden """ - return render(request, "errors/403.html", status=403) \ No newline at end of file + """Error Handler 403 - Forbidden""" + return render(request, "errors/403.html", status=403) diff --git a/home/urls.py b/home/urls.py index a996256..ac9bfc7 100644 --- a/home/urls.py +++ b/home/urls.py @@ -2,5 +2,5 @@ from home.views import IndexView urlpatterns = [ - path('', IndexView.as_view(), name='home'), -] \ No newline at end of file + path("", IndexView.as_view(), name="home"), +] diff --git a/home/views.py b/home/views.py index c694870..676392a 100644 --- a/home/views.py +++ b/home/views.py @@ -1,8 +1,7 @@ from django.views.generic import TemplateView -# Create your views here. class IndexView(TemplateView): - """ A view to return the index page """ - + """A view to return the index page""" + template_name = "home/index.html" diff --git a/products/admin.py b/products/admin.py index 528bd15..7d2e954 100644 --- a/products/admin.py +++ b/products/admin.py @@ -2,15 +2,14 @@ from django.contrib import admin from products.models import Category, Product -# Register your models here. + @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): - list_display = ('name', 'slug') + list_display = ("name", "slug") prepopulated_fields = {"slug": ("name",)} + @admin.register(Product) class ProductAdmin(admin.ModelAdmin): - list_display = ('name', 'slug', 'category', 'price', 'rating', 'image') + list_display = ("name", "slug", "category", "price", "rating", "image") prepopulated_fields = {"slug": ("name",)} - - diff --git a/products/forms.py b/products/forms.py index 4cdf453..a34d9d8 100644 --- a/products/forms.py +++ b/products/forms.py @@ -4,10 +4,11 @@ class ProductForm(forms.ModelForm): - class Meta: model = Product # Renders all fields except for the slug field - exclude = ('slug','rating') + exclude = ("slug", "rating") - image = forms.ImageField(label='Image', required=False, widget=CustomClearableFileInput) \ No newline at end of file + image = forms.ImageField( + label="Image", required=False, widget=CustomClearableFileInput + ) diff --git a/products/models.py b/products/models.py index 97ef531..f2103b4 100644 --- a/products/models.py +++ b/products/models.py @@ -1,31 +1,32 @@ from django.db import models from django.template.defaultfilters import slugify -# Create your models here. + class Category(models.Model): name = models.CharField(max_length=250, unique=True) slug = models.SlugField(max_length=250, unique=True) class Meta: - verbose_name_plural = 'Categories' - + verbose_name_plural = "Categories" + def __str__(self): return self.name class Product(models.Model): - category = models.ForeignKey('Category', on_delete=models.CASCADE) + category = models.ForeignKey("Category", on_delete=models.CASCADE) name = models.CharField(max_length=250, unique=True) slug = models.SlugField(max_length=250, unique=True) description = models.TextField() price = models.DecimalField(max_digits=6, decimal_places=2) - rating = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True) + rating = models.DecimalField( + max_digits=6, decimal_places=2, null=True, blank=True + ) image = models.ImageField(null=True, blank=True) def __str__(self): return self.name - + def save(self, *args, **kwargs): self.slug = slugify(self.name) super().save(*args, **kwargs) - diff --git a/products/urls.py b/products/urls.py index 2b34a37..c547bfc 100644 --- a/products/urls.py +++ b/products/urls.py @@ -4,13 +4,17 @@ ProductDetailView, AddProductView, EditProductView, - DeleteProductView + DeleteProductView, ) urlpatterns = [ - path('', ProductListView.as_view(), name='products'), - path('add/', AddProductView.as_view(), name='add_product'), - path('edit//', EditProductView.as_view(), name='edit_product'), - path('delete//', DeleteProductView.as_view(), name='delete_product'), - path('/', ProductDetailView.as_view(), name='product_detail'), -] \ No newline at end of file + path("", ProductListView.as_view(), name="products"), + path("add/", AddProductView.as_view(), name="add_product"), + path("edit//", EditProductView.as_view(), name="edit_product"), + path( + "delete//", + DeleteProductView.as_view(), + name="delete_product" + ), + path("/", ProductDetailView.as_view(), name="product_detail"), +] diff --git a/products/views.py b/products/views.py index c59053d..136859c 100644 --- a/products/views.py +++ b/products/views.py @@ -16,9 +16,10 @@ from profiles.models import UserProfile from django.db.models import Avg -# Create your views here. + class ProductListView(View): - """ A view to list all products """ + """A view to list all products""" + def get(self, request): products = Product.objects.all() query = None @@ -26,90 +27,99 @@ def get(self, request): sort = None direction = None - if 'sort' in request.GET: - sortkey = request.GET['sort'] + if "sort" in request.GET: + sortkey = request.GET["sort"] sort = sortkey - if sortkey == 'name': - sortkey = 'lower_name' - products = products.annotate(lower_name=Lower('name')) - if sortkey == 'category': - sortkey = 'category__name' - - if 'direction' in request.GET: - direction = request.GET['direction'] - if direction == 'desc': - sortkey = f'-{sortkey}' + if sortkey == "name": + sortkey = "lower_name" + products = products.annotate(lower_name=Lower("name")) + if sortkey == "category": + sortkey = "category__name" + + if "direction" in request.GET: + direction = request.GET["direction"] + if direction == "desc": + sortkey = f"-{sortkey}" products = products.order_by(sortkey) - if 'category' in request.GET: - categories = request.GET['category'].split(',') + if "category" in request.GET: + categories = request.GET["category"].split(",") products = products.filter(category__slug__in=categories) categories = Category.objects.filter(slug__in=categories) - if 'q' in request.GET: - query = request.GET['q'] + if "q" in request.GET: + query = request.GET["q"] if not query: messages.error(request, "You didn't enter any search criteria") - return redirect(reverse('products')) - + return redirect(reverse("products")) + queries = ( Q(name__icontains=query) | Q(description__icontains=query) | Q(category__name__icontains=query) ) products = products.filter(queries) - - current_sorting = f'{sort}_{direction}' + + current_sorting = f"{sort}_{direction}" context = { - 'product_list': products, - 'search_term': query, - 'current_categories': categories, - 'current_sorting': current_sorting - } - return render(request, 'products/product_list.html', context) + "product_list": products, + "search_term": query, + "current_categories": categories, + "current_sorting": current_sorting, + } + return render(request, "products/product_list.html", context) class ProductDetailView(View): - """ + """ A view to display and individual item's product page And to create and display customer product reviews Also displays recently viewed products and related products """ + def get(self, request, slug): product = get_object_or_404(Product, slug=slug) reviews = product.product_review.all() - avg_reviews = product.product_review.all().aggregate(Avg('rating')) + avg_reviews = product.product_review.all().aggregate(Avg("rating")) form = ReviewForm() - # Display recently viewed products code from sessions tutorial on youtube + # Display recently viewed products + # code from sessions tutorial on youtube # https://www.youtube.com/watch?v=N-R5mT-nIDk&t=989s recently_viewed_products = None - if 'recently_viewed' in request.session: - if slug in request.session['recently_viewed']: - request.session['recently_viewed'].remove(slug) - - products = Product.objects.filter(slug__in=request.session['recently_viewed']) + if "recently_viewed" in request.session: + if slug in request.session["recently_viewed"]: + request.session["recently_viewed"].remove(slug) + + products = Product.objects.filter( + slug__in=request.session["recently_viewed"] + ) recently_viewed_products = sorted( - products, key=lambda x: request.session['recently_viewed'].index(x.slug) + products, + key=lambda x: request.session["recently_viewed"].index(x.slug) ) - request.session['recently_viewed'].insert(0, slug) - if len(request.session['recently_viewed']) > 5: - request.session['recently_viewed'].pop() + request.session["recently_viewed"].insert(0, slug) + if len(request.session["recently_viewed"]) > 5: + request.session["recently_viewed"].pop() else: - request.session['recently_viewed'] = [slug] - + request.session["recently_viewed"] = [slug] + request.session.modified = True - # Display related products code from related products tutorial on youtube + # Display related products code + # from related products tutorial on youtube # https://www.youtube.com/watch?v=fqIBA2Vpws0&t=178s - related_products = Product.objects.filter(category=product.category).exclude(slug=slug)[:5] - + related_products = Product.objects.filter( + category=product.category + ).exclude( + slug=slug + )[:5] if request.user.is_authenticated: - profile = get_object_or_404(UserProfile, user=self.request.user ) + profile = get_object_or_404(UserProfile, user=self.request.user) wishlist = get_object_or_404(WishList, user_profile=profile) wish_list = [] @@ -118,18 +128,18 @@ def get(self, request, slug): else: wish_list = None - template = 'products/product_detail.html' + template = "products/product_detail.html" context = { - 'product': product, - 'reviews': reviews, - 'form': form, - 'avg_reviews': avg_reviews, - 'recently_viewed_products':recently_viewed_products, - 'related_products': related_products, - 'wishlist': wish_list, + "product": product, + "reviews": reviews, + "form": form, + "avg_reviews": avg_reviews, + "recently_viewed_products": recently_viewed_products, + "related_products": related_products, + "wishlist": wish_list, } return render(request, template, context) - + def post(self, request, slug): product = get_object_or_404(Product, slug=slug) reviews = product.product_review.all() @@ -138,34 +148,40 @@ def post(self, request, slug): form.instance.user = UserProfile.objects.get(user=self.request.user) if form.is_valid(): form.save() - avg_reviews = reviews.aggregate(Avg('rating')) - product.rating = avg_reviews['rating__avg'] + avg_reviews = reviews.aggregate(Avg("rating")) + product.rating = avg_reviews["rating__avg"] product.save() - messages.success(request, 'Review was successful') + messages.success(request, "Review was successful") return HttpResponseRedirect(self.request.path_info) - class SuperUserCheck(UserPassesTestMixin, View): - """ - A CBV mixin to prevent access from users that are not superusers - From https://stackoverflow.com/questions/67351312/django-check-if-superuser-in-class-based-view + """ + A CBV mixin to prevent access from + users that are not superusers From + https://stackoverflow.com/questions/67351312/ + django-check-if-superuser-in-class-based-view """ + def test_func(self): return self.request.user.is_superuser class AddProductView(SuperUserCheck, CreateView): - """ Add a product to the store """ + """Add a product to the store""" + model = Product form_class = ProductForm - template_name = 'products/add_product.html' - success_message = 'Successfully added product!' - error_message = 'Failed to add product. Please ensure the form is valid.' + template_name = "products/add_product.html" + success_message = "Successfully added product!" + error_message = "Failed to add product. Please ensure the form is valid." def get_success_url(self): - return reverse_lazy('product_detail', kwargs={'slug': self.object.slug}) + return reverse_lazy( + "product_detail", + kwargs={"slug": self.object.slug} + ) def form_valid(self, form): messages.success(self.request, self.success_message) @@ -177,15 +193,19 @@ def form_invalid(self, form): class EditProductView(SuperUserCheck, UpdateView): - """ Edits a product in the store """ + """Edits a product in the store""" + model = Product form_class = ProductForm - template_name = 'products/edit_product.html' - success_message = 'Successfully Updated product!' - error_message = 'Failed to update product. Please ensure the form is valid.' + template_name = "products/edit_product.html" + success_message = "Successfully Updated product!" + error_message = "Failed to update product.\ + Please ensure the form is valid." def get_success_url(self): - return reverse_lazy('product_detail', kwargs={'slug': self.object.slug}) + return reverse_lazy( + "product_detail", kwargs={"slug": self.object.slug} + ) def form_valid(self, form): messages.success(self.request, self.success_message) @@ -199,15 +219,15 @@ def get_context_data(self, **kwargs): context = super(EditProductView, self).get_context_data(**kwargs) product = get_object_or_404(Product, slug=self.object.slug) - messages.info(self.request, f'You are editing {product.name}') - context['product'] = product + messages.info(self.request, f"You are editing {product.name}") + context["product"] = product return context class DeleteProductView(SuperUserCheck, SuccessMessageMixin, DeleteView): - """ Deletes a product in the store """ - model = Product - template_name = 'products/delete_product.html' - success_url = reverse_lazy('products') - success_message = 'Successfully deleted product!' + """Deletes a product in the store""" + model = Product + template_name = "products/delete_product.html" + success_url = reverse_lazy("products") + success_message = "Successfully deleted product!" diff --git a/profiles/forms.py b/profiles/forms.py index 288fffb..728a2b0 100644 --- a/profiles/forms.py +++ b/profiles/forms.py @@ -7,7 +7,7 @@ class UserProfileForm(forms.ModelForm): class Meta: model = UserProfile # Renders all fields except for the user field - exclude = ('user', 'profile_image', 'first_name', 'last_name') + exclude = ("user", "profile_image", "first_name", "last_name") def __init__(self, *args, **kwargs): """ @@ -16,37 +16,43 @@ def __init__(self, *args, **kwargs): """ super().__init__(*args, **kwargs) placeholders = { - 'default_phone_number': 'Phone Number', - 'default_postcode': 'Postal Code', - 'default_town_or_city': 'Town or City', - 'default_street_address1': 'Street Address 1', - 'default_street_address2': 'Street Address 2', - 'default_county': 'County, State or Locality', + "default_phone_number": "Phone Number", + "default_postcode": "Postal Code", + "default_town_or_city": "Town or City", + "default_street_address1": "Street Address 1", + "default_street_address2": "Street Address 2", + "default_county": "County, State or Locality", } - self.fields['default_phone_number'].widget.attrs['autofocus'] = True + self.fields["default_phone_number"].widget.attrs["autofocus"] = True for field in self.fields: - if field != 'default_country': + if field != "default_country": if self.fields[field].required: - placeholder = f'{placeholders[field]} *' + placeholder = f"{placeholders[field]} *" else: placeholder = placeholders[field] - self.fields[field].widget.attrs['placeholder'] = placeholder - # self.fields[field].widget.attrs['class'] = 'stripe-style-input' + self.fields[field].widget.attrs["placeholder"] = placeholder self.fields[field].label = False class ProfileInfoForm(forms.ModelForm): class Meta: model = UserProfile - fields = ('profile_image', 'default_phone_number', 'first_name', 'last_name') + fields = ( + "profile_image", + "default_phone_number", + "first_name", + "last_name" + ) labels = { - 'default_phone_number': 'Phone number', + "default_phone_number": "Phone number", } - + class UserForm(forms.ModelForm): class Meta: model = User - fields = ('username', 'email',) - + fields = ( + "username", + "email", + ) diff --git a/profiles/models.py b/profiles/models.py index fe4e38a..f6e6cd8 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -8,18 +8,29 @@ class UserProfile(models.Model): - """ - A user profile for maintaining billing address - details and order history information """ + A user profile for maintaining billing address + details and order history information + """ + user = models.OneToOneField(User, on_delete=models.CASCADE) - default_phone_number = models.CharField(max_length=20, null=True, blank=True) - default_street_address1 = models.CharField(max_length=80, null=True, blank=True) - default_street_address2 = models.CharField(max_length=80, null=True, blank=True) - default_town_or_city = models.CharField(max_length=40, null=True, blank=True) + default_phone_number = models.CharField( + max_length=20, null=True, blank=True + ) + default_street_address1 = models.CharField( + max_length=80, null=True, blank=True + ) + default_street_address2 = models.CharField( + max_length=80, null=True, blank=True + ) + default_town_or_city = models.CharField( + max_length=40, null=True, blank=True + ) default_county = models.CharField(max_length=80, null=True, blank=True) default_postcode = models.CharField(max_length=20, null=True, blank=True) - default_country = CountryField(blank_label='Country', null=True, blank=True) + default_country = CountryField( + blank_label="Country", null=True, blank=True + ) profile_image = models.ImageField(null=True, blank=True) first_name = models.CharField(max_length=250, null=True, blank=True) last_name = models.CharField(max_length=250, null=True, blank=True) @@ -27,6 +38,7 @@ class UserProfile(models.Model): def __str__(self): return self.user.username + @receiver(post_save, sender=User) def create_or_update_user_profile(sender, instance, created, **kwargs): """ diff --git a/profiles/urls.py b/profiles/urls.py index 13b3578..e9733ac 100644 --- a/profiles/urls.py +++ b/profiles/urls.py @@ -1,12 +1,36 @@ from django.urls import path -from profiles.views import ProfileBillingDetails, OrderHistory, OrderHistoryDetail, ProfileDownloads, ProfileInfo, ProfileReviews, ProfileTestimonials +from profiles.views import ( + ProfileBillingDetails, + OrderHistory, + OrderHistoryDetail, + ProfileDownloads, + ProfileInfo, + ProfileReviews, + ProfileTestimonials, +) urlpatterns = [ - path('', ProfileInfo.as_view(), name='profile_info'), - path('profile_billing_details/', ProfileBillingDetails.as_view(), name='profile_billing_details'), - path('order_history/', OrderHistory.as_view(), name='order_history'), - path('order_history_detail/', OrderHistoryDetail.as_view(), name='order_history_detail'), - path('order_history_detail//downloads', ProfileDownloads.as_view(), name='profile_downloads'), - path('reviews/', ProfileReviews.as_view(), name='profile_reviews'), - path('testimonials/', ProfileTestimonials.as_view(), name='profile_testimonials'), -] \ No newline at end of file + path("", ProfileInfo.as_view(), name="profile_info"), + path( + "profile_billing_details/", + ProfileBillingDetails.as_view(), + name="profile_billing_details", + ), + path("order_history/", OrderHistory.as_view(), name="order_history"), + path( + "order_history_detail/", + OrderHistoryDetail.as_view(), + name="order_history_detail", + ), + path( + "order_history_detail//downloads", + ProfileDownloads.as_view(), + name="profile_downloads", + ), + path("reviews/", ProfileReviews.as_view(), name="profile_reviews"), + path( + "testimonials/", + ProfileTestimonials.as_view(), + name="profile_testimonials" + ), +] diff --git a/profiles/views.py b/profiles/views.py index 5f1d63b..ebb8c40 100644 --- a/profiles/views.py +++ b/profiles/views.py @@ -9,122 +9,155 @@ from checkout.models import Order + class ProfileInfo(LoginRequiredMixin, View): - """ A view to display form info from the user model """ + """A view to display form info from the user model""" + def get(self, request): - # from django update user profile tutorial https://dev.to/earthcomfy/django-update-user-profile-33ho + # from django update user profile tutorial + # https://dev.to/earthcomfy/django-update-user-profile-33ho profile = get_object_or_404(UserProfile, user=request.user) user_form = UserForm(instance=request.user) profile_info_form = ProfileInfoForm(instance=profile) - template = 'profiles/profile_info.html' + template = "profiles/profile_info.html" context = { - 'profile': profile, - 'user_form': user_form, - 'profile_info_form': profile_info_form + "profile": profile, + "user_form": user_form, + "profile_info_form": profile_info_form, } return render(request, template, context) def post(self, request): - # from django update user profile tutorial https://dev.to/earthcomfy/django-update-user-profile-33ho + # from django update user profile tutorial + # https://dev.to/earthcomfy/django-update-user-profile-33ho profile = get_object_or_404(UserProfile, user=request.user) user_form = UserForm(self.request.POST, instance=request.user) - profile_info_form = ProfileInfoForm(self.request.POST, request.FILES, instance=profile) + profile_info_form = ProfileInfoForm( + self.request.POST, request.FILES, instance=profile + ) if user_form.is_valid() and profile_info_form.is_valid(): user_form.save() profile_info_form.save() - messages.success(request, 'Your Profile has been updated successfully') - return HttpResponseRedirect(reverse('profile_info')) + messages.success( + request, + "Your Profile has been updated successfully" + ) + return HttpResponseRedirect(reverse("profile_info")) class ProfileBillingDetails(LoginRequiredMixin, View): - """ Display the user's Profile """ + """Display the user's Profile""" + template_name = "profiles/profile.html" + def get(self, request): profile = get_object_or_404(UserProfile, user=request.user) form = UserProfileForm(instance=profile) - template = 'profiles/profile_billing_details.html' + template = "profiles/profile_billing_details.html" context = { - 'profile':profile, - 'form': form, + "profile": profile, + "form": form, } return render(request, template, context) - def post (self, request): + def post(self, request): profile = get_object_or_404(UserProfile, user=self.request.user) form = UserProfileForm(request.POST, instance=profile) if form.is_valid(): form.save() - messages.success(request, 'Profile updated successfully') + messages.success(request, "Profile updated successfully") return HttpResponseRedirect(self.request.path_info) else: - messages.error(request, 'Update failed. Please ensure the form is valid.') + messages.error( + request, + "Update failed. Please ensure the form is valid." + ) class OrderHistory(LoginRequiredMixin, TemplateView): - template_name = 'profiles/profile_order_history.html' - def get_context_data(self, **kwargs): + template_name = "profiles/profile_order_history.html" + + def get_context_data(self, **kwargs): context = super(OrderHistory, self).get_context_data(**kwargs) profile = get_object_or_404(UserProfile, user=self.request.user) orders = profile.orders.all() - context['orders'] = orders + context["orders"] = orders return context class OrderHistoryDetail(UserPassesTestMixin, TemplateView): - template_name = 'checkout/checkout_success.html' + template_name = "checkout/checkout_success.html" + def get_context_data(self, order_number, **kwargs): context = super(OrderHistoryDetail, self).get_context_data(**kwargs) order = get_object_or_404(Order, order_number=order_number) - messages.info(self.request, ( - f'This is a past confirmation for order number {order_number}. ' - 'A confirmation email was sent on the order date.' - )) - context['order'] = order - context['from_profile'] = True + messages.info( + self.request, + ( + f"This is a past confirmation \ + for order number {order_number}. " + "A confirmation email was sent on the order date." + ), + ) + context["order"] = order + context["from_profile"] = True return context - # restric access mixin from https://stackoverflow.com/questions/58217055/how-can-i-restrict-access-to-a-view-to-only-super-users-in-django + # restric access mixin from + # https://stackoverflow.com/questions/58217055/ + # how-can-i-restrict-access-to-a-view-to-only-super-users-in-django + def test_func(self): order_number = self.kwargs.get("order_number") order = get_object_or_404(Order, order_number=order_number) - return self.request.user.is_superuser or self.request.user == order.user_profile.user + return ( + self.request.user.is_superuser or + self.request.user == order.user_profile.user + ) class ProfileDownloads(UserPassesTestMixin, TemplateView): - """ A view display orders for users to download """ - template_name = 'profiles/profile_downloads.html' + """A view display orders for users to download""" + + template_name = "profiles/profile_downloads.html" def get_context_data(self, order_number, **kwargs): context = super(ProfileDownloads, self).get_context_data(**kwargs) order = get_object_or_404(Order, order_number=order_number) - context['order'] = order + context["order"] = order return context - # restric access mixin from https://stackoverflow.com/questions/58217055/how-can-i-restrict-access-to-a-view-to-only-super-users-in-django + # restric access mixin from + # https://stackoverflow.com/questions/58217055/ + # how-can-i-restrict-access-to-a-view-to-only-super-users-in-django + def test_func(self): order_number = self.kwargs.get("order_number") order = get_object_or_404(Order, order_number=order_number) - return self.request.user.is_superuser or self.request.user == order.user_profile.user + return ( + self.request.user.is_superuser or + self.request.user == order.user_profile.user + ) class ProfileReviews(LoginRequiredMixin, TemplateView): - template_name = 'profiles/profile_reviews.html' - def get_context_data(self, **kwargs): + template_name = "profiles/profile_reviews.html" + + def get_context_data(self, **kwargs): context = super(ProfileReviews, self).get_context_data(**kwargs) profile = get_object_or_404(UserProfile, user=self.request.user) reviews = profile.user_review.all() - context['reviews'] = reviews + context["reviews"] = reviews return context class ProfileTestimonials(LoginRequiredMixin, TemplateView): - template_name = 'profiles/profile_testimonials.html' - def get_context_data(self, **kwargs): + template_name = "profiles/profile_testimonials.html" + + def get_context_data(self, **kwargs): context = super(ProfileTestimonials, self).get_context_data(**kwargs) profile = get_object_or_404(UserProfile, user=self.request.user) testimonials = profile.user_testmonial.all() - context['testimonials'] = testimonials + context["testimonials"] = testimonials return context - - diff --git a/reviews/admin.py b/reviews/admin.py index d732e8c..c3a83f9 100644 --- a/reviews/admin.py +++ b/reviews/admin.py @@ -1,8 +1,7 @@ from django.contrib import admin from .models import Review -# Register your models here. + @admin.register(Review) class ReviewAdmin(admin.ModelAdmin): - list_display = ('title', 'user', 'product', 'submitted') - + list_display = ("title", "user", "product", "submitted") diff --git a/reviews/choices.py b/reviews/choices.py index a637106..1538404 100644 --- a/reviews/choices.py +++ b/reviews/choices.py @@ -1,8 +1,7 @@ RATING_CHOICES = [ - (1, '1 Star'), - (2, '2 Star'), - (3, '3 Star'), - (4, '4 Star'), - (5, '5 Star'), + (1, "1 Star"), + (2, "2 Star"), + (3, "3 Star"), + (4, "4 Star"), + (5, "5 Star"), ] - diff --git a/reviews/forms.py b/reviews/forms.py index d7f218b..59863dd 100644 --- a/reviews/forms.py +++ b/reviews/forms.py @@ -5,4 +5,4 @@ class ReviewForm(forms.ModelForm): class Meta: model = Review - exclude = ('product', 'user') \ No newline at end of file + exclude = ("product", "user") diff --git a/reviews/models.py b/reviews/models.py index 53331aa..149e0df 100644 --- a/reviews/models.py +++ b/reviews/models.py @@ -4,19 +4,22 @@ from profiles.models import UserProfile from .choices import RATING_CHOICES + class Review(models.Model): rating = models.IntegerField(choices=RATING_CHOICES, default=1) title = models.CharField(max_length=250) - product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='product_review') - user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='user_review') + product = models.ForeignKey( + Product, on_delete=models.CASCADE, related_name="product_review" + ) + user = models.ForeignKey( + UserProfile, on_delete=models.CASCADE, related_name="user_review" + ) body = models.TextField() - submitted = models.DateTimeField(auto_now_add=True) + submitted = models.DateTimeField(auto_now_add=True) edited_on = models.DateTimeField(auto_now=True) class Meta: - ordering = ['-submitted'] + ordering = ["-submitted"] def __str__(self): return self.title - - diff --git a/reviews/urls.py b/reviews/urls.py index b7c8a64..1d63158 100644 --- a/reviews/urls.py +++ b/reviews/urls.py @@ -2,6 +2,14 @@ from .views import UpdateReview, DeleteReview urlpatterns = [ - path('edit_review/', UpdateReview.as_view(), name='edit_review'), - path('delete_review/', DeleteReview.as_view(), name='delete_review'), -] \ No newline at end of file + path( + "edit_review/", + UpdateReview.as_view(), + name="edit_review" + ), + path( + "delete_review/", + DeleteReview.as_view(), + name="delete_review" + ), +] diff --git a/reviews/views.py b/reviews/views.py index 43599e9..4bbdc37 100644 --- a/reviews/views.py +++ b/reviews/views.py @@ -9,18 +9,24 @@ from django.db.models import Avg - class UpdateReview(LoginRequiredMixin, View): - """ A view to edit a product review """ + """A view to edit a product review""" + def get(self, request, review_id): review = get_object_or_404(Review, id=review_id) - if not (self.request.user.is_superuser or self.request.user == review.user.user): - messages.error(request, "You are not authorised to view this page!") - return HttpResponseRedirect(reverse('home')) + if not ( + self.request.user.is_superuser or + self.request.user == review.user.user + ): + messages.error( + request, + "You are not authorised to view this page!" + ) + return HttpResponseRedirect(reverse("home")) slug = review.product.slug form = ReviewForm(instance=review) - context = {'review': review, 'form':form} - template = 'reviews/edit_review.html' + context = {"review": review, "form": form} + template = "reviews/edit_review.html" return render(request, template, context) def post(self, request, review_id): @@ -31,23 +37,30 @@ def post(self, request, review_id): form = ReviewForm(self.request.POST, instance=review) if form.is_valid(): form.save() - avg_reviews = reviews.aggregate(Avg('rating')) - product.rating = avg_reviews['rating__avg'] + avg_reviews = reviews.aggregate(Avg("rating")) + product.rating = avg_reviews["rating__avg"] product.save() - messages.success(request, 'Review was updated successfully') - return HttpResponseRedirect(reverse('product_detail', args=[slug])) + messages.success(request, "Review was updated successfully") + return HttpResponseRedirect(reverse("product_detail", args=[slug])) class DeleteReview(LoginRequiredMixin, View): - """ A view for users to delete reviews """ + """A view for users to delete reviews""" + def get(self, request, review_id): review = get_object_or_404(Review, id=review_id) - if not (self.request.user.is_superuser or self.request.user == review.user.user): - messages.error(request, "You are not authorised to view this page!") - return HttpResponseRedirect(reverse('home')) + if not ( + self.request.user.is_superuser or + self.request.user == review.user.user + ): + messages.error( + request, + "You are not authorised to view this page!" + ) + return HttpResponseRedirect(reverse("home")) slug = review.product.slug - context = {'review': review} - template = 'reviews/delete_review.html' + context = {"review": review} + template = "reviews/delete_review.html" return render(request, template, context) def post(self, request, review_id): @@ -56,11 +69,8 @@ def post(self, request, review_id): reviews = product.product_review.all() slug = review.product.slug review.delete() - avg_reviews = reviews.aggregate(Avg('rating')) - product.rating = avg_reviews['rating__avg'] + avg_reviews = reviews.aggregate(Avg("rating")) + product.rating = avg_reviews["rating__avg"] product.save() - messages.success(request, 'Review has been deleted') - return HttpResponseRedirect(reverse('product_detail', args=[slug])) - - - + messages.success(request, "Review has been deleted") + return HttpResponseRedirect(reverse("product_detail", args=[slug])) diff --git a/testimonials/admin.py b/testimonials/admin.py index 56d6c2e..52d4e11 100644 --- a/testimonials/admin.py +++ b/testimonials/admin.py @@ -1,8 +1,11 @@ from django.contrib import admin from .models import Testimonial -# Register your models here. + @admin.register(Testimonial) class TestimonialAdmin(admin.ModelAdmin): - list_display = ('user', 'body', 'approved',) - + list_display = ( + "user", + "body", + "approved", + ) diff --git a/testimonials/forms.py b/testimonials/forms.py index 5abb57a..9a4cc1f 100644 --- a/testimonials/forms.py +++ b/testimonials/forms.py @@ -5,8 +5,8 @@ class TestimonialForm(forms.ModelForm): class Meta: model = Testimonial - fields = ('body',) + fields = ("body",) labels = { - 'body': 'Your Testimonial', - } \ No newline at end of file + "body": "Your Testimonial", + } diff --git a/testimonials/models.py b/testimonials/models.py index d823559..f9fca6c 100644 --- a/testimonials/models.py +++ b/testimonials/models.py @@ -1,11 +1,13 @@ from django.db import models from profiles.models import UserProfile -# Create your models here. + class Testimonial(models.Model): - user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='user_testmonial') + user = models.ForeignKey( + UserProfile, on_delete=models.CASCADE, related_name="user_testmonial" + ) body = models.TextField() - approved = models.BooleanField('Approved', default=False) + approved = models.BooleanField("Approved", default=False) def __str__(self): - return self.user.user.username \ No newline at end of file + return self.user.user.username diff --git a/testimonials/urls.py b/testimonials/urls.py index d267652..1c7162e 100644 --- a/testimonials/urls.py +++ b/testimonials/urls.py @@ -1,10 +1,32 @@ from django.urls import path -from .views import TestimonialListView, AddTestimonialView, EditTestimonialView, DeleteTestimonialView, ApproveTestimonialsView +from .views import ( + TestimonialListView, + AddTestimonialView, + EditTestimonialView, + DeleteTestimonialView, + ApproveTestimonialsView, +) urlpatterns = [ - path('', TestimonialListView.as_view(), name='testimonials'), - path('add_testimonial/', AddTestimonialView.as_view(), name='add_testimonial'), - path('edit_testimonial/', EditTestimonialView.as_view(), name='edit_testimonial'), - path('delete_testimonial/', DeleteTestimonialView.as_view(), name='delete_testimonial'), - path('approve_testimonials', ApproveTestimonialsView.as_view(), name='approve_testimonials'), -] \ No newline at end of file + path("", TestimonialListView.as_view(), name="testimonials"), + path( + "add_testimonial/", + AddTestimonialView.as_view(), + name="add_testimonial" + ), + path( + "edit_testimonial/", + EditTestimonialView.as_view(), + name="edit_testimonial", + ), + path( + "delete_testimonial/", + DeleteTestimonialView.as_view(), + name="delete_testimonial", + ), + path( + "approve_testimonials", + ApproveTestimonialsView.as_view(), + name="approve_testimonials", + ), +] diff --git a/testimonials/views.py b/testimonials/views.py index 043f6aa..c1744f9 100644 --- a/testimonials/views.py +++ b/testimonials/views.py @@ -8,27 +8,38 @@ from .models import Testimonial from profiles.models import UserProfile from .forms import TestimonialForm -from django.views.generic import ListView, CreateView, UpdateView, DeleteView, View +from django.views.generic import ( + ListView, + CreateView, + UpdateView, + DeleteView, + View + ) -# Create your views here. class TestimonialListView(ListView): - """ Displays a list of all approved user testimonials """ + """Displays a list of all approved user testimonials""" + model = Testimonial - template_name = 'testimonials/testimonial_list.html' - context_object_name = 'testimonials' + template_name = "testimonials/testimonial_list.html" + context_object_name = "testimonials" class AddTestimonialView(LoginRequiredMixin, CreateView): - """ creates and adds a user testimonial to be approved for the testimonila page """ + """ + creates and adds a user testimonial + to be approved for the testimonila page + """ + model = Testimonial form_class = TestimonialForm - template_name = 'testimonials/add_testimonial.html' - success_message = 'Successfully added testimonial' - error_message = 'Failed to add testimonial. Please ensure the form is valid.' + template_name = "testimonials/add_testimonial.html" + success_message = "Successfully added testimonial" + error_message = "Failed to add testimonial.\ + Please ensure the form is valid." def get_success_url(self): - return reverse_lazy('testimonials') + return reverse_lazy("testimonials") def form_valid(self, form): form.instance.user = UserProfile.objects.get(user=self.request.user) @@ -41,19 +52,21 @@ def form_invalid(self, form): class EditTestimonialView(UserPassesTestMixin, UpdateView): - """ A view for users to edit Testimonials """ + """A view for users to edit Testimonials""" + model = Testimonial form_class = TestimonialForm - template_name = 'testimonials/edit_testimonial.html' - success_message = 'Successfully updated testimonial' - error_message = 'Failed to update testimonial. Please ensure the form is valid.' + template_name = "testimonials/edit_testimonial.html" + success_message = "Successfully updated testimonial" + error_message = "Failed to update testimonial.\ + Please ensure the form is valid." def get_object(self): id = self.kwargs.get("id") return get_object_or_404(Testimonial, id=id) def get_success_url(self): - return reverse_lazy('testimonials') + return reverse_lazy("testimonials") def form_valid(self, form): messages.success(self.request, self.success_message) @@ -63,55 +76,69 @@ def form_invalid(self, form): messages.error(self.request, self.error_message) return super().form_invalid(form) - # restric access mixin from https://stackoverflow.com/questions/58217055/how-can-i-restrict-access-to-a-view-to-only-super-users-in-django + # restric access mixin from + # https://stackoverflow.com/questions/58217055/ + # how-can-i-restrict-access-to-a-view-to-only-super-users-in-django def test_func(self): id = self.kwargs.get("id") testimonial = get_object_or_404(Testimonial, id=id) - return self.request.user.is_superuser or self.request.user == testimonial.user.user + return ( + self.request.user.is_superuser or + self.request.user == testimonial.user.user + ) class DeleteTestimonialView(UserPassesTestMixin, SuccessMessageMixin, DeleteView): - """ Deletes a testimonial """ + """Deletes a testimonial""" + model = Testimonial - template_name = 'testimonials/delete_testimonial.html' - success_url = reverse_lazy('testimonials') - success_message = 'Successfully deleted testimonial!' + template_name = "testimonials/delete_testimonial.html" + success_url = reverse_lazy("testimonials") + success_message = "Successfully deleted testimonial!" def get_object(self): id = self.kwargs.get("id") return get_object_or_404(Testimonial, id=id) - # restric access mixin from https://stackoverflow.com/questions/58217055/how-can-i-restrict-access-to-a-view-to-only-super-users-in-django + # restric access mixin from + # https://stackoverflow.com/questions/58217055/ + # how-can-i-restrict-access-to-a-view-to-only-super-users-in-django def test_func(self): id = self.kwargs.get("id") testimonial = get_object_or_404(Testimonial, id=id) - return self.request.user.is_superuser or self.request.user == testimonial.user.user - + return ( + self.request.user.is_superuser or + self.request.user == testimonial.user.user + ) -class SuperUserCheck(UserPassesTestMixin, View): - """ - A CBV mixin to prevent access from users that are not superusers - From https://stackoverflow.com/questions/67351312/django-check-if-superuser-in-class-based-view +class SuperUserCheck(UserPassesTestMixin, View): + """ + A CBV mixin to prevent access from users that are not + superusers From https://stackoverflow.com/questions/67351312/ + django-check-if-superuser-in-class-based-view """ + def test_func(self): return self.request.user.is_superuser + class ApproveTestimonialsView(SuperUserCheck, View): - """ A view for approving user testimonials """ + """A view for approving user testimonials""" + def get(self, request): # From Approval with checkboxes tutorial on You Tube # https://www.youtube.com/watch?v=FzV_Py68Y_I testimonials = Testimonial.objects.all() - template = 'testimonials/approve_testimonials.html' - context = {'testimonials': testimonials} + template = "testimonials/approve_testimonials.html" + context = {"testimonials": testimonials} return render(request, template, context) - + def post(self, request): testimonials = Testimonial.objects.all() - checkboxes = request.POST.getlist('checkboxes') + checkboxes = request.POST.getlist("checkboxes") # Uncheck all events testimonials.update(approved=False) @@ -121,9 +148,4 @@ def post(self, request): Testimonial.objects.filter(pk=int(checkbox)).update(approved=True) messages.success(request, "Successfully updated Testimonial approvals") - return HttpResponseRedirect(reverse('testimonials')) - - - - - + return HttpResponseRedirect(reverse("testimonials")) diff --git a/wishlist/admin.py b/wishlist/admin.py index 5dd5444..5ec8e7c 100644 --- a/wishlist/admin.py +++ b/wishlist/admin.py @@ -1,11 +1,11 @@ from django.contrib import admin from .models import WishList, WishListItem -# Register your models here. + class WishListItemAdminInline(admin.TabularInline): model = WishListItem - + + @admin.register(WishList) class WishListAdmin(admin.ModelAdmin): inlines = (WishListItemAdminInline,) - diff --git a/wishlist/models.py b/wishlist/models.py index 92c245a..ea9c803 100644 --- a/wishlist/models.py +++ b/wishlist/models.py @@ -1,27 +1,33 @@ from django.db import models from profiles.models import UserProfile -from products.models import Product +from products.models import Product from django.db.models.signals import post_save from django.dispatch import receiver -# Create your models here. + class WishList(models.Model): - user_profile = models.OneToOneField(UserProfile, on_delete=models.CASCADE, related_name='wish_list') + user_profile = models.OneToOneField( + UserProfile, on_delete=models.CASCADE, related_name="wish_list" + ) def __str__(self): return f"{self.user_profile}'s Wish List" class WishListItem(models.Model): - wish_list = models.ForeignKey(WishList, on_delete=models.CASCADE, related_name='wish_list_item') - product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='wish_list_item') + wish_list = models.ForeignKey( + WishList, on_delete=models.CASCADE, related_name="wish_list_item" + ) + product = models.ForeignKey( + Product, on_delete=models.CASCADE, related_name="wish_list_item" + ) class Meta: - unique_together = ('wish_list', 'product') + unique_together = ("wish_list", "product") @receiver(post_save, sender=UserProfile) def create_wish_list(sender, instance, created, **kwargs): - """ Create a wishlist for a user's profile """ + """Create a wishlist for a user's profile""" if created: WishList.objects.create(user_profile=instance) diff --git a/wishlist/urls.py b/wishlist/urls.py index 6621e0f..bb93376 100644 --- a/wishlist/urls.py +++ b/wishlist/urls.py @@ -2,8 +2,16 @@ from .views import WishListView, AddToWishList, RemoveFromWishList, AddToCart urlpatterns = [ - path('', WishListView.as_view(), name='wish_list'), - path('add//', AddToWishList.as_view(), name='add_to_wish_list'), - path('remove//', RemoveFromWishList.as_view(), name='remove_from_wish_list'), - path('add_to_cart//', AddToCart.as_view(), name='add_to_cart_from_wishlist'), -] \ No newline at end of file + path("", WishListView.as_view(), name="wish_list"), + path("add//", AddToWishList.as_view(), name="add_to_wish_list"), + path( + "remove//", + RemoveFromWishList.as_view(), + name="remove_from_wish_list", + ), + path( + "add_to_cart//", + AddToCart.as_view(), + name="add_to_cart_from_wishlist", + ), +] diff --git a/wishlist/views.py b/wishlist/views.py index b64d346..4190ae9 100644 --- a/wishlist/views.py +++ b/wishlist/views.py @@ -6,84 +6,98 @@ from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin -# Create your views here. + class WishListView(LoginRequiredMixin, TemplateView): - """ A view to display items in the users wishlist """ - template_name = 'wishlist/wish_list.html' + """A view to display items in the users wishlist""" + + template_name = "wishlist/wish_list.html" def get_context_data(self, **kwargs): context = super(WishListView, self).get_context_data(**kwargs) profile = get_object_or_404(UserProfile, user=self.request.user) wish_list_items = profile.wish_list.wish_list_item.all() - context['wish_list_items'] = wish_list_items + context["wish_list_items"] = wish_list_items return context class AddToWishList(LoginRequiredMixin, View): - """ A view to add items to the user's wishlist """ + """A view to add items to the user's wishlist""" + def post(self, request, slug): try: profile = get_object_or_404(UserProfile, user=self.request.user) user_wishlist = get_object_or_404(WishList, user_profile=profile) product = get_object_or_404(Product, slug=slug) - wish_list_item, created = WishListItem.objects.get_or_create(wish_list=user_wishlist, product=product) + wish_list_item, created = WishListItem.objects.get_or_create( + wish_list=user_wishlist, product=product + ) if created: - # A new wislist item object created - messages.success(request, f"Added {product.name} to your wish list") + # A new wislist item object created + messages.success( + request, + f"Added {product.name} to your wish list" + ) else: - # wishlist item object already exists - messages.error(request, f"Error {product.name} already in wishlist!") + # wishlist item object already exists + messages.error( + request, + f"Error {product.name} already in wishlist!" + ) return HttpResponse(status=200) except Exception as e: - messages.error(request, f'Error adding: {e}') + messages.error(request, f"Error adding: {e}") return HttpResponse(status=500) - class RemoveFromWishList(LoginRequiredMixin, View): - """ A view to remove an item from the user's wishlist """ + """A view to remove an item from the user's wishlist""" + def post(self, request, slug): try: profile = get_object_or_404(UserProfile, user=self.request.user) user_wishlist = get_object_or_404(WishList, user_profile=profile) product = get_object_or_404(Product, slug=slug) - wish_list_item = get_object_or_404(WishListItem, wish_list=user_wishlist, product=product) - redirect_url = request.POST.get('redirect_url') + wish_list_item = get_object_or_404( + WishListItem, wish_list=user_wishlist, product=product + ) + redirect_url = request.POST.get("redirect_url") wish_list_item.delete() - messages.success(request, f'Removed {product.name} from wishlist') + messages.success(request, f"Removed {product.name} from wishlist") return HttpResponse(status=200) except Exception as e: - messages.error(request, f'Error removing: {e}') + messages.error(request, f"Error removing: {e}") return HttpResponse(status=500) class AddToCart(LoginRequiredMixin, View): - """ A view to add items to the cart from the wishlist """ + """A view to add items to the cart from the wishlist""" + def post(self, request, slug): try: product = get_object_or_404(Product, slug=slug) quantity = 1 - #checks if the cart is in the session and then creates an empty dict if it isn't - cart = request.session.get('cart', {}) - - #checks if the slug is in the cart dictionary + # checks if the cart is in the session and + # then creates an empty dict if it isn't + cart = request.session.get("cart", {}) + + # checks if the slug is in the cart dictionary if slug in list(cart.keys()): - messages.error(request, f"You already added {product.name} to your cart!") - + messages.error( + request, f"You already added {product.name} to your cart!" + ) + else: cart[slug] = quantity - messages.success(request, f'Added {product.name} to your cart') + messages.success(request, f"Added {product.name} to your cart") + + # Adds the amount of that item to its value in the dictionary + request.session["cart"] = cart - #Adds the amount of that item to its value in the dictionary - request.session['cart'] = cart - return HttpResponse(status=200) except Exception as e: - messages.error(request, f'Error adding: {e}') + messages.error(request, f"Error adding: {e}") return HttpResponse(status=500) - -