Feature: Shopping Cart
Relevant source files
The following files were used as context for generating this wiki page:
The Shopping Cart is a core e-commerce module in the Becathlon application, enabling both guest and authenticated users to add products, manage quantities, and proceed to checkout. It is designed to be persistent for logged-in users and session-based for guests. Upon login, a guest's cart is automatically merged with their user account cart.
The feature is heavily reliant on AJAX for a seamless user experience, allowing for real-time updates to the cart without full page reloads. This includes adding items, updating quantities, and removing items. The backend provides a set of RESTful API endpoints to support these frontend interactions, while the frontend logic is encapsulated in a dedicated JavaScript file.
Core Concepts & Data Model
The shopping cart's data is structured around two primary models: Cart and CartItem. A Cart can be associated with either an authenticated User or a guest session_key, but not both.
Sources: apps/cart/tests.py:16-36
ER Diagram
This diagram illustrates the relationships between the Cart, CartItem, User, and Product models.
Sources: apps/cart/tests.py:38-51, apps/main/models.py
Cart Model
The Cart model represents a shopping cart. It has a unique constraint on both the user and session_key fields to ensure a user or session only has one active cart.
| Field | Type | Description |
|---|---|---|
user | ForeignKey to User | Associates the cart with a logged-in user. Nullable. |
session_key | CharField | Associates the cart with a guest session. Nullable. |
Sources: apps/cart/tests.py:38-51
Key methods on the Cart model include:
get_total_items(): Returns the total number of all items (sum of quantities).get_subtotal(): Calculates the total price of all items in the cart.get_item_count(): Returns the number of unique products in the cart.merge_carts(other_cart): Merges items from another cart, summing quantities for overlapping products.clear(): Deletes allCartItems associated with the cart.
Sources: apps/cart/tests.py:53-125
CartItem Model
The CartItem model represents a single product line item within a Cart. It has a unique constraint on the combination of cart and product to prevent duplicate product entries for the same cart.
| Field | Type | Description |
|---|---|---|
cart | ForeignKey to Cart | The cart this item belongs to. |
product | ForeignKey to Product | The product being added. |
quantity | PositiveIntegerField | The quantity of the product. |
Sources: apps/cart/tests.py:158-169
The primary method is get_subtotal(), which calculates product.price * quantity.
Sources: apps/cart/tests.py:177-183
Backend Logic
The backend is composed of utility functions for cart management, Django views that expose API endpoints, and context processors for global data access.
Sources: apps/cart/views.py, apps/cart/utils.py
Cart Management Utilities
Utility functions in apps/cart/utils.py handle the core logic of retrieving, creating, and merging carts.
get_or_create_cart(request)
This function is the central entry point for accessing a user's cart. It ensures that a cart exists for the current request, whether the user is authenticated or a guest.
The logic flow is as follows:
Sources: apps/cart/utils.py, apps/cart/tests.py:207-241
Other Utilities
transfer_guest_cart_to_user(request): On user login, this function finds the guest cart (via session key) and the user's cart. It then merges the guest cart's items into the user's cart and deletes the guest cart.validate_cart_item_stock(product, quantity): A helper function to check if the requested quantity of a product is available in stock before adding it to the cart.
Sources: apps/cart/utils.py, apps/cart/tests.py:243-294
API Endpoints
The apps/cart/views.py file defines several views that function as API endpoints for the frontend JavaScript. All modification endpoints are protected with the @require_POST decorator.
| Method | URL | View Function | Description |
|---|---|---|---|
GET | /cart/ | cart_view | Renders the full shopping cart page. |
POST | /cart/add/<product_id>/ | add_to_cart | Adds a specified quantity of a product to the cart. |
POST | /cart/update/<item_id>/ | update_cart_item | Updates the quantity of an item. If quantity is 0, the item is removed. |
POST | /cart/remove/<item_id>/ | remove_from_cart | Deletes an item from the cart. |
POST | /cart/clear/ | clear_cart | Removes all items from the cart. |
GET | /cart/api/summary/ | api_cart_summary | Returns a JSON object with cart totals and a list of items. |
GET | /cart/api/count/ | api_cart_count | A lightweight endpoint that returns only the total number of items. |
Sources: apps/cart/views.py, apps/cart/static/cart/js/cart.js
Frontend Interaction
The frontend logic in apps/cart/static/cart/js/cart.js handles all user interactions with the cart, communicating with the backend via asynchronous fetch requests.
Key Functions
addToCart(productId, quantity): Triggered from product pages. Sends a POST request to/cart/add/<product_id>/.updateCartItem(itemId, quantity): Triggered by quantity input changes or +/- buttons on the cart page. Sends a POST request to/cart/update/<itemId>/.removeCartItem(itemId): Triggered by the "Remove" button. It callsupdateCartItem(itemId, 0)after a user confirmation.clearCart(): Triggered by the "Clear Cart" button. Sends a POST request to/cart/clear/.refreshCartCount(): Called after cart modifications to update the cart badge in the site navigation by fetching from/cart/api/count/.refreshCartSummary(): Called after cart modifications on the cart page to update the subtotal and item count by fetching from/cart/api/summary/.
Sources: apps/cart/static/cart/js/cart.js
Add to Cart Sequence Diagram
This diagram shows the sequence of events when a user adds an item to their cart from a product detail page.
User Experience and UI
The cart feature is integrated across the site, from product pages to the final checkout summary.
Cart Page (cart.html)
The main cart view, rendered by apps/cart/templates/cart/cart.html, displays:
- A list of all
cart_items. - For each item: product image, name, price, a quantity selector, and a remove button.
- A summary section with the subtotal.
- Action buttons to "Continue Shopping" or "Proceed to Checkout".
- A "Clear Cart" button.
If the cart is empty, a message is displayed with a link to start shopping.
Sources: apps/cart/templates/cart/cart.html
Checkout Integration
The checkout process relies on the cart's contents.
- The
checkout_viewinapps/cart/views.pyensures a user is authenticated and their cart is not empty before proceeding. - The checkout page (
apps/order/templates/order/checkout.html) includes an "Order Summary" sidebar that iterates throughcart_itemsto display the products being purchased, their quantities, and the final subtotal.
Sources: apps/cart/views.py:171-189, apps/order/templates/order/checkout.html:194-227
Global Cart Access
The number of items in the cart is displayed in the site's main navigation bar. This is achieved by including cart.js in base templates, which calls refreshCartCount() on page load to fetch the count and update the badge.
Sources: apps/catalog/templates/catalog/base_catalog.html:48-49, apps/cart/static/cart/js/cart.js:186-198
Summary
The Becathlon Shopping Cart is a robust and user-friendly feature that supports the entire pre-purchase workflow. Its architecture cleanly separates backend data management from frontend interactivity, leveraging Django's session framework for guest users and a set of AJAX-powered API endpoints for dynamic updates. The system is designed for persistence and correctly handles the transition from a guest to an authenticated user by merging cart data, ensuring a smooth and uninterrupted shopping experience.