From 33d83629bf6e4aa951eedb749e0181d837aef612 Mon Sep 17 00:00:00 2001 From: Joanna Gorska Date: Sun, 3 Apr 2022 17:49:14 +0000 Subject: [PATCH 1/7] Add new app checkout --- bonsai_shop/settings.py | 1 + checkout/__init__.py | 0 checkout/admin.py | 3 +++ checkout/apps.py | 6 ++++++ checkout/migrations/__init__.py | 0 checkout/models.py | 3 +++ checkout/tests.py | 3 +++ checkout/views.py | 3 +++ 8 files changed, 19 insertions(+) create mode 100644 checkout/__init__.py create mode 100644 checkout/admin.py create mode 100644 checkout/apps.py create mode 100644 checkout/migrations/__init__.py create mode 100644 checkout/models.py create mode 100644 checkout/tests.py create mode 100644 checkout/views.py diff --git a/bonsai_shop/settings.py b/bonsai_shop/settings.py index fe792ba..dbd920e 100644 --- a/bonsai_shop/settings.py +++ b/bonsai_shop/settings.py @@ -52,6 +52,7 @@ 'home', 'trees', 'trolley', + 'checkout', ] diff --git a/checkout/__init__.py b/checkout/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/checkout/admin.py b/checkout/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/checkout/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/checkout/apps.py b/checkout/apps.py new file mode 100644 index 0000000..ca03808 --- /dev/null +++ b/checkout/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CheckoutConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'checkout' diff --git a/checkout/migrations/__init__.py b/checkout/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/checkout/models.py b/checkout/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/checkout/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/checkout/tests.py b/checkout/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/checkout/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/checkout/views.py b/checkout/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/checkout/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From f79cfb6f3d2cc484dc0ce4adafdc70b49a220ec9 Mon Sep 17 00:00:00 2001 From: Joanna Gorska Date: Sun, 3 Apr 2022 18:06:16 +0000 Subject: [PATCH 2/7] Add models for checkout app and runmigrations --- checkout/migrations/0001_initial.py | 48 +++++++++++++++ checkout/models.py | 90 +++++++++++++++++++++++++++- db.sqlite3 | Bin 282624 -> 294912 bytes trees/models.py | 2 +- 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 checkout/migrations/0001_initial.py diff --git a/checkout/migrations/0001_initial.py b/checkout/migrations/0001_initial.py new file mode 100644 index 0000000..13076d0 --- /dev/null +++ b/checkout/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# Generated by Django 3.2 on 2022-04-03 18:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('trees', '0009_auto_20220329_0820'), + ] + + operations = [ + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order_number', models.CharField(editable=False, max_length=32)), + ('full_name', models.CharField(max_length=50)), + ('email', models.EmailField(max_length=254)), + ('phone_number', models.CharField(max_length=20)), + ('country', models.CharField(default='GB', max_length=2)), + ('postcode', models.CharField(blank=True, max_length=20, null=True)), + ('town_or_city', models.CharField(max_length=40)), + ('street_address1', models.CharField(max_length=80)), + ('street_address2', models.CharField(blank=True, max_length=80, null=True)), + ('county', models.CharField(blank=True, max_length=80, null=True)), + ('date', models.DateTimeField(auto_now_add=True)), + ('delivery_cost', models.DecimalField(decimal_places=2, default=0, max_digits=6)), + ('order_total', models.DecimalField(decimal_places=2, default=0, max_digits=10)), + ('grand_total', models.DecimalField(decimal_places=2, default=0, max_digits=10)), + ('original_bag', models.TextField(default='')), + ('stripe_pid', models.CharField(default='', max_length=254)), + ], + ), + migrations.CreateModel( + name='OrderLineItem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.IntegerField(default=0)), + ('lineitem_total', models.DecimalField(decimal_places=2, editable=False, max_digits=6)), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lineitems', to='checkout.order')), + ('tree', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='trees.tree')), + ], + ), + ] diff --git a/checkout/models.py b/checkout/models.py index 71a8362..d902b07 100644 --- a/checkout/models.py +++ b/checkout/models.py @@ -1,3 +1,91 @@ +import uuid + from django.db import models +from django.db.models import Sum +from django.conf import settings + +from trees.models import Tree + + +class Order(models.Model): + """ + order model + """ + order_number = models.CharField(max_length=32, null=False, editable=False) + + 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) + # set to GB + country = models.CharField(default='GB', max_length=2, 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) + delivery_cost = models.DecimalField(max_digits=6, decimal_places=2, null=False, default=0) + order_total = models.DecimalField(max_digits=10, decimal_places=2, null=False, default=0) + grand_total = models.DecimalField(max_digits=10, decimal_places=2, null=False, default=0) + # in case if customer makes the same purchase twice + original_bag = models.TextField(null=False, blank=False, default='') + stripe_pid = models.CharField(max_length=254, null=False, blank=False, default='') + + def _generate_order_number(self): + """ + Generate a random, unique order number using UUID + _ before name means it is private method to be used only inside this class + """ + return uuid.uuid4().hex.upper() + + def update_total(self): + """ + Update grand total each time a line item is added, + accounting for delivery costs. + """ + # or 0 is to prevent setting order_total to none if we manually + # delete all orders from the database + self.order_total = self.lineitems.aggregate(Sum('lineitem_total'))['lineitem_total__sum'] or 0 + if self.order_total < settings.FREE_DELIVERY_THRESHOLD: + self.delivery_cost = self.order_total * settings.STANDARD_DELIVERY_PERCENTAGE / 100 + else: + self.delivery_cost = 0 + self.grand_total = self.order_total + self.delivery_cost + self.save() + + def save(self, *args, **kwargs): + """ + Override the original save method to set the order number + if it hasn't been set already. + """ + if not self.order_number: + self.order_number = self._generate_order_number() + super().save(*args, **kwargs) + + def __str__(self): + return self.order_number + + +class OrderLineItem(models.Model): + """ + individual shopping bag item, relating to specific order from the order model + total cost for this specific item + the function will iterate through order and get each item as a seperate line item + this one will update delivery cost, order total and grand total + """ + order = models.ForeignKey(Order, null=False, blank=False, on_delete=models.CASCADE, related_name='lineitems') + tree = models.ForeignKey(Tree, null=False, blank=False, on_delete=models.CASCADE) + quantity = models.IntegerField(null=False, blank=False, default=0) + lineitem_total = models.DecimalField(max_digits=6, decimal_places=2, null=False, blank=False, editable=False) + + def save(self, *args, **kwargs): + """ + Override the original save method to set the lineitem total + and update the order total. + """ + self.lineitem_total = self.tree.price * self.quantity + super().save(*args, **kwargs) -# Create your models here. + def __str__(self): + return f'Slug {self.tree.slug} on order {self.order.order_number}' \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 989af3cdceb9be2f5613327a4ed759394c2e60ed..178839f04de786a3128f3d57bbff9ebc3c822e26 100644 GIT binary patch delta 2356 zcma)6Z)_7~7{B-4yWKjryKXJkp`*9PhL*AI+O3R%49A$T(X|yeV>G5*+m~^py^h;$ zKq7PvK_z@)R&$A;P>D-OfP^Mi5ducP5N5=LFZzM-!4DV{CDEt}N__81-CZeYlRjym zKfmXBe$Vr~^QW8V=UYxa)4Y-(h#mM_`P^Dq*W+4AbZz;L#wy6&(+$sa_qpG>d&TDI zBb>7%53tHLfyGL96P7;uZh^5!N&0S4w4b6wJwBRf^A0BWq2&JTtSSgXATG(0Dv9Z! z5Daz-y`4f2AK0)_2yF}rT|%h8XMLd9_Ee@7#0n?7cK~PMR(D6MuK{B_*(kv!xnb^S z?gsZgcZHo|qeZ9C$9Aov+SY(dj;xYGN?MYUq@s+og=%wIO^)J`fwus3vXKJwf|p_? zHc|}mtJ$trCq=ZasR`5Uz$TZ)RD2`yHh?kPM1hGd*loYs?%2MzUACREov_Ka35Q0>C*Zp@VTtRk2kR2mxmbrICuPrfAue+$YMDRi=oG0^PtVrc&0BF*^m?fnzyM zxP<~<;7E0w@OPGVX53MZ0&d{=7jL8pUj=h8#(E3P%>_FfWk=W{b~D?@2G|bPi(5L< zB+w85XUvdMLq-farb|cIkUI@IYRHk|#O(`t=p??>m_5KtkpTY^_JBf(K1U7!ty2Vk zjaww0+Vzjglcnnkuqgv$}vnjVeH@TH_gx5hG!?XdpjMKZFyPW@9L^Oqh%*^t<@zyJA!@OYnBIYDX7HX0zceX z%L0j@muNp+f``e)`rqnXnB5I0*rB?qrelrY0I6=F>1boIZoF>1AlkQS%4zbC($UZ1 z04ObfNFIP-0;^xrTvy?brQM5Uh5{{kXe=mwbOoxQ;n>DF*YHMG7Sl#&G*6?sSU5oyhT6@*(+d*jf zz^o{%l6u&~OR|b4k-|r!<9uWy9A3wJDvx4Z&8lMBgV#POWyG}E#i&^O-~!p+Xf8(E zbC&cm7(!JLGR)ewBCOMTF2l8>{^Pd%YR2XFgA-#lnls}dWy7P*Ecascu(r&aJex@% z#lz2u3SMf(*Aw)c3*9@LPRF(IBxuu8Q^1{@-j|h8ygDc-Sb~z-Sy@#uITcVV=5%&O zO=eTbWY^=%t+eHLNz*7kEv2*vm!J#yO9wxL45`VV!mmofi|`$&6+eOBQUpQ%g5QOo z$Xwo+Z)aSu;X@vI(jn`#>UX?15kSFEpI}|8%3|;MUw3TA^66mzb;f!?>HbwnpSpOB LvKOg4f4Tnwk#q16 delta 545 zcmWkqO=uHA7@coscW1L^cd{!r7AfXZVy)Pi9x75ciVZ9jG|)sr4^1nAwzkwC8iY1R zDq5+B##Q!OD>N6Whe21+f?~ly3r6Tg4_*{S1xumWgC6`k@E+fLGrVtjm5NiDvuF3% zHX$U7IE(;9qk5n-Zj*zVk18rAHjqiT_$&U14QW@(VrP3lI`LklroAR|Qfu*f?4&%D zc&EyiT_erl`7<3JIR08kdsEWDCyq5q#5s`_zr~99B$njvRF~zC#nN)py#vg#CVYOh zRocazB_~cy1M>{#^IR1(ha-}mfpO+p1)n#zoh4HiK0m$599j=}5n2^E1)D*t2tFnb z1uzJ03L#XL1F=yE>x0le!aOAr@CH=Tn`U8R4Zg1IkJ1{^?wP8(sVa8RIm(_=viIRgv##ehJ?{q1PZ7TA)7`UbET%NSd1G diff --git a/trees/models.py b/trees/models.py index e58f681..f77cf48 100644 --- a/trees/models.py +++ b/trees/models.py @@ -116,7 +116,7 @@ def random_string_generator(self, def save(self, *args, **kwargs): """ - automaticaly generates slug field + overrides the save method and automaticaly generates slug field """ if not self.slug: name_string = slugify(self.name) From 21c5389d4c086e1e1ef398c856b9aa59768ff01a Mon Sep 17 00:00:00 2001 From: Joanna Gorska Date: Sun, 3 Apr 2022 19:46:48 +0000 Subject: [PATCH 3/7] Add admin, signals and forms for checkout --- checkout/admin.py | 40 +++++++++++++++++++++++++++++++- checkout/apps.py | 7 ++++++ checkout/forms.py | 54 ++++++++++++++++++++++++++++++++++++++++++++ checkout/signals.py | 24 ++++++++++++++++++++ db.sqlite3 | Bin 294912 -> 294912 bytes 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 checkout/forms.py create mode 100644 checkout/signals.py diff --git a/checkout/admin.py b/checkout/admin.py index 8c38f3f..d232f66 100644 --- a/checkout/admin.py +++ b/checkout/admin.py @@ -1,3 +1,41 @@ from django.contrib import admin +from .models import Order, OrderLineItem -# Register your models here. + +class OrderLineItemAdminInline(admin.TabularInline): + ''' + admin for line item model + admin can add and edit line items from inside the order model + when we look at the order, we will see the list of editable line items + without going to line item interface + ''' + model = OrderLineItem + readonly_fields = ('lineitem_total',) + + +class OrderAdmin(admin.ModelAdmin): + ''' + admin for order model + ''' + # this allows to vied and edit inline items inside each order + inlines = (OrderLineItemAdminInline,) + + readonly_fields = ('order_number', 'date', + 'delivery_cost', 'order_total', + 'grand_total', 'original_bag', 'stripe_pid',) + # this settings dictates the order in which the columns are displayed + # in admin + fields = ('order_number', 'date', 'full_name', + 'email', 'phone_number', 'country', + 'postcode', 'town_or_city', 'street_address1', + 'street_address2', 'county', 'delivery_cost', + 'order_total', 'grand_total', 'original_bag', 'stripe_pid',) + # restrict the columns that show up in order list + list_display = ('order_number', 'date', 'full_name', + 'order_total', 'delivery_cost', + 'grand_total',) + # most recent orders at the top + ordering = ('-date',) + + +admin.site.register(Order, OrderAdmin) diff --git a/checkout/apps.py b/checkout/apps.py index ca03808..49ff801 100644 --- a/checkout/apps.py +++ b/checkout/apps.py @@ -2,5 +2,12 @@ class CheckoutConfig(AppConfig): + ''' + checkout config with method overiding ready method + and imorting signals + ''' default_auto_field = 'django.db.models.BigAutoField' name = 'checkout' + + def ready(self): + import checkout.signals diff --git a/checkout/forms.py b/checkout/forms.py new file mode 100644 index 0000000..8cc2970 --- /dev/null +++ b/checkout/forms.py @@ -0,0 +1,54 @@ +from django import forms +from .models import Order + + +class OrderForm(forms.ModelForm): + ''' + order form + ''' + class Meta: + ''' + meta data - which model the form reffers to + which fields from the model we want to render + NOT rendering the fields that will be automaticaly calculated + ''' + model = Order + fields = ('full_name', 'email', 'phone_number', + 'street_address1', 'street_address2', + 'town_or_city', 'postcode', 'country', + 'county',) + + def __init__(self, *args, **kwargs): + """ + overrides form __init__ method + Add placeholders and classes, remove auto-generated + labels and set autofocus on first field + """ + super().__init__(*args, **kwargs) + # adds nice placeholders + 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', + } + # sets autofocus on full name field + self.fields['full_name'].widget.attrs['autofocus'] = True + # makes country readonly + self.fields['country'].widget.attrs['readonly'] = True + + for field in self.fields: + if field != 'country': + if self.fields[field].required: + # ads a star for fields that are required on the model + placeholder = f'{placeholders[field]} *' + else: + placeholder = placeholders[field] + # sets attributes for place holder and class and removes labels + self.fields[field].widget.attrs['placeholder'] = placeholder + self.fields[field].widget.attrs['class'] = 'stripe-style-input' + self.fields[field].label = False diff --git a/checkout/signals.py b/checkout/signals.py new file mode 100644 index 0000000..01ab943 --- /dev/null +++ b/checkout/signals.py @@ -0,0 +1,24 @@ + +from django.db.models.signals import post_save, post_delete +from django.dispatch import receiver + +from .models import OrderLineItem + +@receiver(post_save, sender=OrderLineItem) +def update_on_save(sender, instance, created, **kwargs): + """ + handles signals from post save event + sender of the signal - update/ create order line item + instance of the model - sends it + created - boleon if this is a new instance or update + receiver decorator received post save signal from orderlineitem + """ + instance.order.update_total() + + +@receiver(post_delete, sender=OrderLineItem) +def update_on_delete(sender, instance, **kwargs): + """ + Update order total on lineitem delete + """ + instance.order.update_total() diff --git a/db.sqlite3 b/db.sqlite3 index 178839f04de786a3128f3d57bbff9ebc3c822e26..b2b242bfcf347252a52eda674653aaa2680cc0ab 100644 GIT binary patch delta 551 zcmZo@5Nc=;njp<6IZ?)$QF3F#vUnCR27a&2f(;J*9F4||ybO)Dj*}zv(?m>k4U82G zEv<|Vtqd*oER4;JEKN3_$+uJx=lI6JcbHF$x1Z-Wj}~_$*CQ?+j&GcNn++9ianv^% zbFun1mpWP)l;>xgq+}Xe6qy&4q!yQ$=B1lu6q=M*S(GFjBwFgFdR2sFxfc7US2_AR zrN(Eb1Vu!J6goRar6pTB8Wd!P8l?wir==Hr7l)*TR|Xm-`NjK|IU0p~2kN?{6?&wW zyZZU*mK!A&JDa8yTbP*nB^f%p1l9*f_(Ucd7^Jy6TBMt2d7GAoRv1MT=2vBhCR_SM z1^5KyTexJUR2sNOn3kHR!GvNyO%-NeMnXZ$#lXOzIyq55vH9k@?Kjsk sIyy4(od#;(#K&ki(NTK(tAC6F{P2hc+s@U_`JWMpnYMHOXa3#*07~YrJpcdz delta 381 zcmZo@5Nc=;njp<6I#I@%QFLR%vUrw22L8a!f(?%Rd^;FGpizgBm!Z+tcydsF8n3ag zk-37QnU$%Dm9hEeGx?SZqMUOX_zv@F@%HoF=F#GAqSM z_sT5G{K&Mb;KCB`0P~;}Q}^<+V1sfa10y3{0~1|Ca{&cIb1MULD Date: Sun, 3 Apr 2022 20:22:08 +0000 Subject: [PATCH 4/7] Add urls views and template for checkout --- bonsai_shop/urls.py | 1 + checkout/templates/checkout/checkout.html | 220 ++++++++++++++++++++++ checkout/urls.py | 6 + checkout/views.py | 27 ++- trolley/templates/trolley/trolley.html | 2 +- 5 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 checkout/templates/checkout/checkout.html create mode 100644 checkout/urls.py diff --git a/bonsai_shop/urls.py b/bonsai_shop/urls.py index 8c4ea95..91e9c3e 100644 --- a/bonsai_shop/urls.py +++ b/bonsai_shop/urls.py @@ -24,4 +24,5 @@ path('', include('home.urls')), path('trees/', include('trees.urls')), path('trolley/', include('trolley.urls')), + path('checkout/', include('checkout.urls')), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/checkout/templates/checkout/checkout.html b/checkout/templates/checkout/checkout.html new file mode 100644 index 0000000..63f86f9 --- /dev/null +++ b/checkout/templates/checkout/checkout.html @@ -0,0 +1,220 @@ +{% extends "base.html" %} +{% load static %} + + +{% block content %} + +
+
+ +
+ +

Checkout form

+

Below is an example form built entirely with Bootstrap's form controls. Each required form group has a validation state that can be triggered by attempting to submit the form without completing it.

+
+ +
+
+

+ Checkout + 3 +

+
    +
  • +
    +
    Product name
    + Brief description +
    + $12 +
  • +
  • +
    +
    Second product
    + Brief description +
    + $8 +
  • +
  • +
    +
    Third item
    + Brief description +
    + $5 +
  • +
  • +
    +
    Delivery
    + EXAMPLECODE +
    + -£5 +
  • +
  • + Total (GBP) + £20 +
  • +
+ +
+
+ + + +

Billing address

+
+
+
+ + +
+ Valid first name is required. +
+
+ +
+ + +
+ Valid last name is required. +
+
+ +
+ +
+ @ + +
+ Your username is required. +
+
+
+ +
+ + +
+ Please enter a valid email address for shipping updates. +
+
+ +
+ + +
+ Please enter your shipping address. +
+
+ +
+ + +
+ +
+ + +
+ Please select a valid country. +
+
+ +
+ + +
+ Please provide a valid state. +
+
+ +
+ + +
+ Zip code required. +
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +

Payment

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + + Full name as displayed on card +
+ Name on card is required +
+
+ +
+ + +
+ Credit card number is required +
+
+ +
+ + +
+ Expiration date required +
+
+ +
+ + +
+ Security code required +
+
+
+ +
+ + +
+
+
+
+ + +
+ {% endblock %} \ No newline at end of file diff --git a/checkout/urls.py b/checkout/urls.py new file mode 100644 index 0000000..38a63e8 --- /dev/null +++ b/checkout/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('', views.checkout, name='checkout'), +] diff --git a/checkout/views.py b/checkout/views.py index 91ea44a..361aec7 100644 --- a/checkout/views.py +++ b/checkout/views.py @@ -1,3 +1,26 @@ -from django.shortcuts import render +""" +views for checkout app +""" +from django.shortcuts import render, redirect, reverse +from django.contrib import messages -# Create your views here. +from .forms import OrderForm + + +def checkout(request): + """ + view to make checkout + """ + trolley = request.session.get('trolley', {}) + if not trolley: + messages.error(request, + "There's nothing in your trolley at the moment") + return redirect(reverse('all_trees')) + + order_form = OrderForm() + template = 'checkout/checkout.html' + context = { + 'order_form': order_form, + } + + return render(request, template, context) diff --git a/trolley/templates/trolley/trolley.html b/trolley/templates/trolley/trolley.html index c95f47e..706a946 100644 --- a/trolley/templates/trolley/trolley.html +++ b/trolley/templates/trolley/trolley.html @@ -24,7 +24,7 @@
Delivery: £{{ delivery|floatformat:2 }}
Back to shop From 45007b4b54298ddbd827c100a35bb48bf82f7824 Mon Sep 17 00:00:00 2001 From: Joanna Gorska Date: Sun, 3 Apr 2022 20:34:07 +0000 Subject: [PATCH 5/7] Add crispy forms settings and add to requirement.txt --- bonsai_shop/settings.py | 8 ++++++++ checkout/templates/checkout/checkout.html | 4 ++-- requirements.txt | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/bonsai_shop/settings.py b/bonsai_shop/settings.py index dbd920e..7f106c3 100644 --- a/bonsai_shop/settings.py +++ b/bonsai_shop/settings.py @@ -54,6 +54,8 @@ 'trolley', 'checkout', + 'crispy_forms', + ] MIDDLEWARE = [ @@ -68,6 +70,8 @@ ROOT_URLCONF = 'bonsai_shop.urls' +CRISPY_TEMPLATE_PACK = 'bootstrap4' + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', @@ -85,6 +89,10 @@ 'django.template.context_processors.media', 'trolley.contexts.trolley_contents', ], + 'builtins': [ + 'crispy_forms.templatetags.crispy_forms_tags', + 'crispy_forms.templatetags.crispy_forms_field', + ] }, }, ] diff --git a/checkout/templates/checkout/checkout.html b/checkout/templates/checkout/checkout.html index 63f86f9..bdf0a1b 100644 --- a/checkout/templates/checkout/checkout.html +++ b/checkout/templates/checkout/checkout.html @@ -5,7 +5,7 @@ {% block content %}
-
+
@@ -213,7 +213,7 @@

Payment

- + diff --git a/requirements.txt b/requirements.txt index 43394ff..85eb0bc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ asgiref==3.5.0 Django==3.2 django-allauth==0.41.0 +django-crispy-forms==1.14.0 oauthlib==3.2.0 Pillow==9.0.1 python3-openid==3.2.0 From a8446ed9f2a9d77823fe4318e1f922ee25c52457 Mon Sep 17 00:00:00 2001 From: Joanna Gorska Date: Sun, 3 Apr 2022 21:22:55 +0000 Subject: [PATCH 6/7] Add form to checkout form template --- checkout/templates/checkout/checkout.html | 177 ++++++++-------------- 1 file changed, 66 insertions(+), 111 deletions(-) diff --git a/checkout/templates/checkout/checkout.html b/checkout/templates/checkout/checkout.html index bdf0a1b..e947539 100644 --- a/checkout/templates/checkout/checkout.html +++ b/checkout/templates/checkout/checkout.html @@ -9,14 +9,14 @@
-

Checkout form

+

Checkout form

Below is an example form built entirely with Bootstrap's form controls. Each required form group has a validation state that can be triggered by attempting to submit the form without completing it.

- Checkout + Checkout 3

    @@ -55,161 +55,116 @@
    Delivery
+ +
- - -

Billing address

-
+

Delivery address

+ + {% csrf_token %}
- - -
- Valid first name is required. -
+ {{ order_form.full_name | as_crispy_field }} +
- - -
- Valid last name is required. -
+ {{ order_form.email | as_crispy_field }}
- -
- @ - -
- Your username is required. -
-
+ {{ order_form.phone_number | as_crispy_field }}
- - -
- Please enter a valid email address for shipping updates. -
+ {{ order_form.street_address1 | as_crispy_field }}
- - -
- Please enter your shipping address. -
+ {{ order_form.street_address2 | as_crispy_field }}
- - + {{ order_form.town_or_city | as_crispy_field }}
- - -
- Please select a valid country. -
+ {{ order_form.country | as_crispy_field }}
- - -
- Please provide a valid state. -
+ {{ order_form.county | as_crispy_field }}
- - -
- Zip code required. -
+ {{ order_form.postcode | as_crispy_field }}

-
- - -
- -
- - +
+ {% if user.is_authenticated %} + + + {% else %} + + {% endif %}

-

Payment

- -
-
- - -
-
- - -
-
- - -
-
+

Payment

+ +
-
- - - Full name as displayed on card -
- Name on card is required +
+ +
+ + + + +
-
- -
- - -
- Credit card number is required + -
- -
- - -
- Expiration date required +
+ +
-
- -
- - -
- Security code required +
+

+ + + + Your card will be charged ${{ grand_total|floatformat:2 }} +

+
-
+ +
+
- - +
From 26e5ece81d6d807bbad4ecfb1e4e4aea8fc6be25 Mon Sep 17 00:00:00 2001 From: Joanna Gorska Date: Sun, 3 Apr 2022 21:52:30 +0000 Subject: [PATCH 7/7] Add variables to display content of the bag into the checkout template --- checkout/templates/checkout/checkout.html | 34 ++++++++-------------- db.sqlite3 | Bin 294912 -> 294912 bytes 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/checkout/templates/checkout/checkout.html b/checkout/templates/checkout/checkout.html index e947539..d898795 100644 --- a/checkout/templates/checkout/checkout.html +++ b/checkout/templates/checkout/checkout.html @@ -1,5 +1,7 @@ {% extends "base.html" %} {% load static %} +{% load trolley_tools %} + {% block content %} @@ -16,41 +18,29 @@

Checkout form

- Checkout - 3 + Order Summary + {{ tree_count }}

    + {% for item in trolley_items %}
  • -
    Product name
    - Brief description -
    - $12 -
  • -
  • -
    -
    Second product
    - Brief description -
    - $8 -
  • -
  • -
    -
    Third item
    - Brief description +
    {{item.tree.name}} ({{ item.quantity }})
    + Brief description ???
    - $5 + £ {{ item.tree.price | calc_subtotal:item.quantity }}
  • + {% endfor %}
  • Delivery
    - EXAMPLECODE + Free delivery above £50
    - -£5 + £ {{ delivery | floatformat:2 }}
  • Total (GBP) - £20 + £ {{ grand_total | floatformat:2 }}
diff --git a/db.sqlite3 b/db.sqlite3 index b2b242bfcf347252a52eda674653aaa2680cc0ab..9fc0d26f0bd556df392baf3d7f1d9c38cf1237bd 100644 GIT binary patch delta 273 zcmZo@5Nc=;njp<6JyFJ)QF>#7+YBbgj>(Avev`k=$f-{;cS*@FFDfvo3Mncw3$;b*0vPgERbSd>MGO7snC`c{H(v1qNa?Q!Ju&B%|*Yz{-DNjks^(l#uNc0YL z&C1TREcXn{O(_Wt^NVs1&hRWLHTMbj&dc!&%PB6-4a~K4wkXQX3Cc)vxAbs{PY-a* zbg3$}GW5<%vM|rEN=^$l4NWab&2@FNC`~Sj$}q^YbT3RXboFvdE(kMpFD#1BDsc=f z^T{zVGB7gIH89aNG*>V(v@$icGBDILG%+(Uv~1RywOwZxLYO|BOJ)w4LKW H^N$7q3tw8f delta 270 zcmZo@5Nc=;njp<6IZ?)$QF3E~+lOZf z8G7d>nU;rHIp-$^6j|n!rv#)V1{H*rRhAhP`v>NvdALP->biOQ1Y{QEW*g*I#(O&& z85kMq8kp!BnkyJuS{WN!8CvRDm|2(`n>Opr+O9K;@oWk|nnXLte?}l?+RpKx`9}i) Dzus7(