added possibility to remove product from cart

feature/product_models_refactor
mtyton 2023-05-28 18:35:08 +02:00
rodzic 20ea20fa79
commit 4a588bf8ea
13 zmienionych plików z 54 dodań i 29 usunięć

Wyświetl plik

@ -23,7 +23,9 @@ RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-r
# Install the project requirements. # Install the project requirements.
COPY requirements.txt / COPY requirements.txt /
COPY requirements_dev.txt /
RUN pip install -r /requirements.txt RUN pip install -r /requirements.txt
RUN pip install -r /requirements_dev.txt
# Use /app folder as a directory where the source code is stored. # Use /app folder as a directory where the source code is stored.
WORKDIR /app WORKDIR /app

Wyświetl plik

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z"/>
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z"/>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 553 B

Wyświetl plik

@ -51,7 +51,7 @@
<script type="text/javascript" src="{% static 'js/jquery-3.6.4.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/jquery-3.6.4.min.js' %}"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script> <script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
<script src="{% static 'js/cart.js' %}"></script>
{% block extra_js %} {% block extra_js %}
{# Override this in templates to add extra javascript #} {# Override this in templates to add extra javascript #}
{% endblock %} {% endblock %}

Wyświetl plik

@ -13,6 +13,8 @@ services:
- DATABASE_URL - DATABASE_URL
env_file: env_file:
- .env - .env
stdin_open: true
tty: true
db: db:
image: postgres image: postgres
restart: always restart: always

Wyświetl plik

@ -2,3 +2,4 @@ FLAKE8>=6.0.0
pre-commit>=3.3.1 pre-commit>=3.3.1
isort>=5.12 isort>=5.12
black>=23.3.0 black>=23.3.0
ipdb==0.12.3

Wyświetl plik

@ -51,9 +51,9 @@ class SessionCart(BaseCart):
self.validate_item_id(item_id) self.validate_item_id(item_id)
quantity = int(quantity) quantity = int(quantity)
item_id = int(item_id) item_id = int(item_id)
if not self.session[settings.CART_SESSION_ID].get(str(item_id)):
if not self.session[settings.CART_SESSION_ID].get(item_id):
self.session[settings.CART_SESSION_ID][item_id] = quantity self.session[settings.CART_SESSION_ID][item_id] = quantity
self.session.modified = True
else: else:
self.update_item_quantity(item_id, quantity) self.update_item_quantity(item_id, quantity)
@ -61,6 +61,7 @@ class SessionCart(BaseCart):
self.validate_item_id(item_id) self.validate_item_id(item_id)
try: try:
self.session[settings.CART_SESSION_ID].pop(item_id) self.session[settings.CART_SESSION_ID].pop(item_id)
self.session.modified = True
except KeyError: except KeyError:
# TODO - add logging # TODO - add logging
... ...
@ -68,7 +69,8 @@ class SessionCart(BaseCart):
def update_item_quantity(self, item_id: int, change: int) -> None: def update_item_quantity(self, item_id: int, change: int) -> None:
self.validate_item_id(item_id) self.validate_item_id(item_id)
try: try:
self.session[settings.CART_SESSION_ID][item_id] += change self.session[settings.CART_SESSION_ID][str(item_id)] += change
self.session.modified = True
except KeyError: except KeyError:
# TODO - add logging # TODO - add logging
self.add_item(item_id, change) self.add_item(item_id, change)

Wyświetl plik

@ -28,10 +28,10 @@ class CartProductAddSerializer(serializers.Serializer):
def validate_product_id(self, value): def validate_product_id(self, value):
try: try:
self.product = Product.obejcts.get(id=value) Product.objects.get(id=value)
except Product.objects.DoesNotExist: except Product.DoesNotExist:
raise serializers.ValidationError("Unable to add not existing product") raise serializers.ValidationError("Unable to add not existing product")
return value return value
def save(self, cart): def save(self, cart):
cart.add_item(self.product, self.validated_data["quantity"]) cart.add_item(self.validated_data["product_id"], self.validated_data["quantity"])

Wyświetl plik

@ -4,10 +4,11 @@ $(document).on('click', '.add-to-cart-button', function(event) {
event.preventDefault(); event.preventDefault();
const button = $(this); const button = $(this);
const formData = new FormData(); const formData = new FormData();
const productID = $(this).data('product-id'); const productID = parseInt($(this).data('product-id'));
const quantity = $('#quantity'+productID).val(); const quantity = parseInt($('#quantity'+productID).val());
const addToCartURL = $(this).data('add-to-cart-url'); const addToCartURL = $(this).data('add-to-cart-url');
const csrfToken = $(this).data('csrf-token'); const csrfToken = $(this).data('csrf-token');
console.log(productID);
formData.append('product_id', productID); formData.append('product_id', productID);
formData.append('quantity', quantity); // Serialize the form data correctly formData.append('quantity', quantity); // Serialize the form data correctly
button.prop('disabled', true); button.prop('disabled', true);
@ -107,15 +108,16 @@ $(document).on('click', '.add-to-cart-button', function(event) {
const button = $(this); const button = $(this);
const productId = button.data('product-id'); const productId = button.data('product-id');
const csrfToken = button.data('csrf-token'); const csrfToken = button.data('csrf-token');
const url = button.data('remove-from-cart-url');
$.ajax({ $.ajax({
type: 'DELETE', type: 'POST',
url: '/cart/item/' + parseInt(productId) + '/', url: url,
data: {"product_id": productId},
headers: { 'X-CSRFToken': csrfToken }, headers: { 'X-CSRFToken': csrfToken },
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
alert(data.message); alert("Item has been removed");
fetchCartItems(csrfToken); location.reload();
}, },
error: function() { error: function() {
alert("Error occurred while removing the item from the cart."); alert("Error occurred while removing the item from the cart.");

Wyświetl plik

@ -1,7 +1,5 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load static %}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="{% static 'js/cart.js' %}"></script>
{% block content %} {% block content %}
<section class="h-100"> <section class="h-100">

Wyświetl plik

@ -1,3 +1,5 @@
{% load static %}
<div class="card rounded-3 mb-4"> <div class="card rounded-3 mb-4">
<div class="card-body p-4"> <div class="card-body p-4">
<div class="row d-flex justify-content-between align-items-center"> <div class="row d-flex justify-content-between align-items-center">
@ -27,7 +29,12 @@
<h5 class="mb-0">{{item.product.price}} ZŁ</h5> <h5 class="mb-0">{{item.product.price}} ZŁ</h5>
</div> </div>
<div class="col-md-1 col-lg-1 col-xl-1 text-end"> <div class="col-md-1 col-lg-1 col-xl-1 text-end">
<a href="#!" class="text-danger"><i class="fas fa-trash fa-lg"></i></a> <a href="#!" class="text-danger remove-from-cart-button"
data-product-id="{{item.product.id}}"
data-csrf-token="{{csrf_token}}"
data-remove-from-cart-url={% url "cart-action-remove-product" %}>
<img src="{% static 'images/icons/trash.svg'%}" />
</a>
</div> </div>
</div> </div>
</div> </div>

Wyświetl plik

@ -1,6 +1,4 @@
{% load static %} {% load static %}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="{% static 'js/cart.js' %}"></script>
<div class="card h-100" style="width: 15rem;"> <div class="card h-100" style="width: 15rem;">
<div class="card-header">{{item.title}}</div> <div class="card-header">{{item.title}}</div>
@ -13,8 +11,10 @@
</div> </div>
<div class="col text-end"> <div class="col text-end">
<a href="#" class="btn btn-outline-success add-to-cart-button" <a href="#" class="btn btn-outline-success add-to-cart-button"
data-product-id="{{ item.id }}" data-product-id="{{item.id}}"
data-csrf-token="{{ csrf_token }}"> data-csrf-token="{{csrf_token}}"
data-add-to-cart-url={% url "cart-action-add-product" %}
>
<img src="{% static 'images/icons/cart.svg' %}" alt="Koszyk"/> <img src="{% static 'images/icons/cart.svg' %}" alt="Koszyk"/>
</a> </a>
</div> </div>

Wyświetl plik

@ -1,6 +1,5 @@
{% extends 'base.html'%} {% extends 'base.html'%}
{% load static %} {% load static %}
<script src="{% static 'js/cart.js' %}"></script>
{% block content %} {% block content %}

Wyświetl plik

@ -34,19 +34,27 @@ class CartActionView(ViewSet):
# get cart items # get cart items
cart = SessionCart(self.request) cart = SessionCart(self.request)
items = cart.get_items() items = cart.get_items()
serialzier = CartProductSerializer(instance=items, many=True) serializer = CartProductSerializer(instance=items, many=True)
return Response(serialzier.data) return Response(serializer.data)
@action(detail=False, methods=["post"]) @action(detail=False, methods=["post"])
def add_product(self, request): def add_product(self, request):
cart = SessionCart(self.request) cart = SessionCart(self.request)
serializer = CartProductAddSerializer(data=request.POST) serializer = CartProductAddSerializer(data=request.POST)
if serializer.is_valid(): if not serializer.is_valid():
return Response(serializer.errors, status=400) return Response(serializer.errors, status=400)
serializer.save(cart) serializer.save(cart)
items = cart.get_items() items = cart.get_items()
serialzier = CartProductSerializer(instance=items, many=True) serializer = CartProductSerializer(instance=items, many=True)
return Response(serialzier.data, status=201) return Response(serializer.data, status=201)
# TODO - same for remove product @action(detail=False, methods=["post"])
def remove_product(self, request):
cart = SessionCart(self.request)
product_id = request.POST.get("product_id")
cart.remove_item(product_id)
items = cart.get_items()
serializer = CartProductSerializer(instance=items, many=True)
return Response(serializer.data, status=201)