Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Commerce App

A Django eCommerce application with three types of users - buyers, vendors, and admins. Buyers browse and order products, vendors run stores and fulfil orders, and admins moderate products and view vendor revenue. The page-by-page behaviour of the app is described in the accompanying UI Plan document.

Tech stack

  • Python 3 + Django 6.0
  • SQLite (db.sqlite3, included for development)
  • Server-rendered Django templates (no separate front end)

Database choice

This project uses SQLite, Django's built-in relational database engine. SQLite is a fully relational SQL database: it stores data in tables with columns, rows, primary keys, and foreign-key relationships, and is queried with standard SQL via Django's ORM. It was chosen over a client-server engine such as MariaDB/MySQL because it is file-based and requires no separate database server or credentials, which keeps development and grading setup to a single python manage.py migrate step. The schema is created and version-managed through Django's migration system (the shop/migrations/ folder), so switching to MariaDB or PostgreSQL later only requires changing the DATABASES setting and re-running migrate.

Setup

From the project root (the folder containing manage.py):

# 1. Create and activate a virtual environment
python -m venv venv
venv\Scripts\activate          # Windows (PowerShell / cmd)
# source venv/bin/activate      # macOS / Linux

# 2. Install dependencies
pip install Django Pillow

# 3. Apply database migrations
python manage.py migrate

# 4. Start the development server
python manage.py runserver

The app is then available at http://127.0.0.1:8000/.

Pillow is required because products support image uploads. Django itself is the only other dependency.

Logging in

There is a single login page for everyone (/login/). After you submit your username and password, the app decides where to send you based on your account type:

User type How the account is created Where login takes you
Buyer "Sign up as new buyer" on the login page (or /register/buyer/) Buying home page
Vendor "Sign up as new vendor" on the login page (or /register/vendor/) Vendor home page
Admin Created from the command line (see below) Admin dashboard

Buyer login

  1. On the login page, click the new-buyer sign-up button (or go to /register/buyer/).
  2. Fill in your details and submit - you are logged in straight away and taken to the buyer home page.
  3. On later visits, log in with that username and password; you will land on the buyer home page.

Vendor login

  1. On the login page, click the new-vendor sign-up button (or go to /register/vendor/).
  2. Fill in your details, including banking information, and submit - you are taken to the vendor home page.
  3. On later visits, log in with that username and password; you will land on the vendor home page.

Admin login

Admins are Django staff/superusers. They are not created through the website - create one from the command line:

python manage.py createsuperuser

Enter a username, email, and password when prompted. Then log in at the normal /login/ page with those credentials. Because the account is a staff user, you will be routed to the admin dashboard.

Tip: the same superuser can also use Django's built-in admin site at /admin/.

Adding product categories

Categories are not created through the website - they must be added manually from the command line. Open the Django shell from the project root (the folder containing manage.py, with the virtual environment activated):

python manage.py shell

Then create the categories you need:

from shop.models import Category

Category.objects.create(name="Books")
Category.objects.create(name="Electronics")
Category.objects.create(name="Clothing")

exit()

Once added, the categories become available for vendors to assign to their products and for buyers to search/filter by.

Forgot password (console email)

This project sends email to the console, not to a real inbox. The relevant setting is:

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

This means password-reset emails (and order-confirmation emails) are printed in the terminal window where python manage.py runserver is running instead of being delivered by email. No mail server or credentials are needed for development.

To reset a password:

  1. Make sure the server is running (python manage.py runserver) and keep an eye on that terminal.

  2. On the login page, click the forgot-password button (or go to /password-reset/).

  3. Enter the email address linked to the account and submit.

  4. Switch to the terminal running the server. The reset email is printed there - find the message titled "Password Reset" and copy the reset link, which looks like:

    http://127.0.0.1:8000/reset/<uid>/<token>/
    
  5. Paste that link into your browser, enter a new password, and submit. You are then redirected back to the login page to sign in with the new password.

Reset links expire after 1 hour (PASSWORD_RESET_TIMEOUT = 3600). If the link has expired, request a new one. Nothing is printed if the email address doesn't match an existing account.

Running the automated tests

The project ships with an automated test suite in shop/tests.py that exercises the app end to end: the database models, buyer/vendor registration and login, the session-based cart, checkout and stock handling, search, store/product management, order fulfilment, the sales summary, the review system, the REST API endpoints and the password-reset flow.

From the project root (the folder containing manage.py, with the virtual environment activated):

# Run the whole suite
python manage.py test shop

Django builds a **temporary test database**, runs the tests against it, and destroys
it afterwards - your real `db.sqlite3` data is never touched. A passing run ends with
`OK`; any failures are listed individually with a traceback. No server needs to be
running, and the email backend is the console backend, so confirmation/reset emails
are captured in memory rather than sent.

### How the suite is organised

| Test class | What it covers |
|------------|----------------|
| `ModelTests` | Model fields, `__str__` values, order totals, review averages |
| `RegistrationTests` | Buyer/vendor sign-up, duplicate username and password-mismatch handling |
| `AuthenticationTests` | Login/logout and role-based redirects (buyer/vendor/admin) |
| `CartTests` | Adding to, viewing and removing items from the session cart |
| `CheckoutTests` | Turning a cart into an order, stock reduction and out-of-stock rejection |
| `SearchTests` | Product search by title and category |
| `StoreManagementTests` | Vendor store/product create, edit, delete and ownership checks |
| `OrderFulfilmentTests` | Listing orders to fulfil and updating order status |
| `SalesSummaryTests` | Revenue and 80% profit calculations on delivered orders |
| `ReviewTests` | Verified vs unverified reviews and purchase requirements |
| `ApiTests` | The five REST API endpoints and their auth/permission rules |
| `PasswordResetTests` | The forgot-password email flow |

> The `ApiTests` class needs Django REST Framework (`pip install djangorestframework`).
> It is already listed in `requirements.txt`.

The `ApiTests` class is the automated counterpart to the manual Postman walk-through
in the **API Testing Guide** below - the same endpoints are checked here without
needing Postman or a running server.


# API Testing Guide

This guide explains how to test the RESTful API built for the eCommerce
application. All testing is done with **Postman**.

The API provides five endpoints:

| # | Action | Method | URL | Auth required |
|---|--------|--------|-----|---------------|
| 1 | View all stores | GET | `/api/stores/` | No |
| 2 | Add a store | POST | `/api/stores/add/` | Yes (vendor) |
| 3 | View all products | GET | `/api/products/` | No |
| 4 | Add a product | POST | `/api/products/add/` | Yes (vendor) |
| 5 | View a product's reviews | GET | `/api/products/<product_id>/reviews/` | Yes (vendor, must own product) |

---

## Before you start

1. **Run the server.** From the project folder:

python manage.py runserver


The API is served from `http://127.0.0.1:8000/`. Leave the server running
while you test.

2. **Have a vendor login ready.** Three of the endpoints require a registered
**vendor** account, authenticated with **Basic Auth** (username + password).

- You can register a vendor through the site's normal registration page, or
- Use a test vendor account if one has been provided with the submission.

> **Important:** Basic Auth uses the account's **username**, *not* its email
> address. Use the username that was set when the vendor registered.

---

## Endpoint 1 - View all stores (GET, no auth)

1. Open a new request tab in Postman (the **+** button).
2. Set the method to **GET**.
3. Enter the URL:

http://127.0.0.1:8000/api/stores/


4. Click **Send**.

**Expected result:** Status **200 OK** and a JSON list of stores. If no stores
exist yet, an empty list `[]` is returned - this is normal, not an error.

---

## Endpoint 2 - Add a store (POST, vendor auth)

This endpoint creates a new store and links it to the authenticated vendor.

1. New request tab. Set the method to **POST**.
2. Enter the URL:

http://127.0.0.1:8000/api/stores/add/


3. Set the authentication:
- Click the **Authorization** tab.
- Set **Type** to **Basic Auth**.
- Enter the vendor's **username** and **password**.

4. Set the body:
- Click the **Body** tab.
- Select **raw**, then choose **JSON** from the dropdown on the right.
- Enter:

  ```json
  {
      "name": "BaconShop",
      "description": "We sell Bacon!"
  }
  ```

> Note: the vendor is **not** sent in the body. It is set automatically from
> the logged-in user, so the store is always attached to the correct vendor.

5. Click **Send**.

**Expected result:** Status **201 Created**, with the new store returned in the
response. The `vendor` value in the response is filled in automatically and
matches the vendor you authenticated as.

**Error cases worth checking:**
- Authenticating as a user who is **not** a vendor returns **403 Forbidden**
with a message that a registered vendor is required.
- Sending the request with **no authentication** returns **401 Unauthorized**.

---

## Endpoint 3 - View all products (GET, no auth)

1. New request tab. Set the method to **GET**.
2. Enter the URL:

http://127.0.0.1:8000/api/products/


3. Click **Send**.

**Expected result:** Status **200 OK** and a JSON list of products (or `[]` if
none exist).

---

## Endpoint 4 - Add a product (POST, vendor auth)

This endpoint creates a new product and links it to the authenticated vendor.

1. New request tab. Set the method to **POST**.
2. Enter the URL:

http://127.0.0.1:8000/api/products/add/


3. Set **Authorization** to **Basic Auth** with the vendor's username and
password (same as Endpoint 2).

4. Set the **Body** to **raw / JSON**:

```json
{
    "title": "Bacon Strips",
    "description": "Premium smoked bacon",
    "price": "49.99",
    "quantity": 10
}

title, description and price are required. store and category are optional and can be supplied as their id numbers if desired. The image field should be left out when testing through raw JSON, since file uploads are not sent this way.

  1. Click Send.

Expected result: Status 201 Created, with the new product returned and the vendor filled in automatically.

Error cases: same as the store endpoint - a non-vendor user gets 403, and no authentication gives 401.


Endpoint 5 - View a product's reviews (GET, vendor auth, ownership check)

This endpoint returns the reviews for one product. The vendor can only view reviews for a product that they own.

  1. New request tab. Set the method to GET.

  2. Enter the URL, replacing 1 with a real product id:

    http://127.0.0.1:8000/api/products/1/reviews/
    
  3. Set Authorization to Basic Auth with the vendor's username and password.

  4. Click Send.

Expected result: Status 200 OK and a JSON list of that product's reviews (or [] if the product has none).

Ownership check: If the product id does not belong to the authenticated vendor, the endpoint returns 404 Not Found by design. This is the ownership rule working correctly, not a bug. To find a product id that belongs to the vendor, use Endpoint 3 (/api/products/) and look at the vendor value of each product.


Quick reference - what a passing test looks like

Endpoint Setup Expected status
GET /api/stores/ No auth 200 + list
POST /api/stores/add/ Basic Auth (vendor) + JSON body 201 + new store
POST /api/stores/add/ Non-vendor user 403
POST /api/stores/add/ No auth 401
GET /api/products/ No auth 200 + list
POST /api/products/add/ Basic Auth (vendor) + JSON body 201 + new product
GET /api/products/<id>/reviews/ Basic Auth (vendor, owns product) 200 + list
GET /api/products/<id>/reviews/ Vendor does not own product 404

Note on response format

Endpoints that use Django REST Framework's Response (the POST endpoints and the reviews endpoint) return XML, because the project sets an XML renderer as the global default in settings.py. The data is saved correctly either way - this only affects how the response is displayed. The two product/store list endpoints return JSON, as they use Django's JsonResponse directly.

Reddit API Integration

This feature integrates a third-party API (Reddit) into the Django e-commerce application. Visiting /reddit/ fetches posts from a chosen subreddit and displays them on a page.

Implementation

The feature is implemented across all required components:

File Responsibility
functions/reddit.py Helper function get_reddit_posts() - builds the request URL, sends a GET request with a User-Agent header, and parses the JSON response into a list of posts (title, author, link).
views.py reddit_feed view - calls the helper and passes the posts into the template.
templates/shop/reddit_feed.html Loops through the posts and displays each title, author, and a clickable link to the original post.
urls.py URL pattern mapping /reddit/ to the view.

Status

The Django side works correctly. Visiting /reddit/ returns an HTTP 200, and the view, helper, and template all execute as intended (see terminal screenshots in commerce_app\planning).

The live fetch from Reddit does not return posts because Reddit responds with HTTP 403 (Forbidden) to unauthenticated requests to its .json endpoints from this network. This was confirmed in the server logs.

Fixes attempted

The following standard fixes were tried, without success:

  1. Setting a descriptive User-Agent header, then a browser-style User-Agent.
  2. Switching the request host from www.reddit.com to old.reddit.com.
  3. Beginning setup of Reddit's official OAuth API (registering a script app for a client ID and secret).

The JSON endpoint continued to return 403, and the OAuth app registration also failed to complete from this network. This is consistent with Reddit's recent tightening of access to its public JSON endpoints, which now pushes developers toward the authenticated API and appears to block requests from certain networks/IPs regardless of headers.

Summary

The third-party API integration is complete and correctly wired into the Django project. The remaining issue is Reddit's server-side access restriction, which is external to the application code.

Project layout

commerce_app/
├── manage.py                 # Django command-line entry point
├── db.sqlite3                # SQLite database (development data)
├── requirements.txt          # Python dependencies
├── README.md                 # this file
├── commerce_app/             # project configuration
│   ├── settings.py           # settings (database, email backend, DRF, etc.)
│   ├── urls.py               # root URL configuration
│   ├── wsgi.py / asgi.py     # server entry points
│   └── __init__.py
├── planning/                 # requirements, UI plan and sequence diagrams
└── shop/                     # the main application
    ├── models.py             # Vendor, Buyer, Store, Product, Order, OrderItem, Category, Review
    ├── views.py              # all page logic and auth/password-reset handling
    ├── urls.py               # URL routes (namespaced under "shop")
    ├── forms.py              # form classes
    ├── serializers.py        # Django REST Framework serializers for the API
    ├── admin.py              # Django admin registrations
    ├── apps.py               # app config
    ├── tests.py              # automated test suite (see "Running the automated tests")
    ├── migrations/           # database schema migrations
    ├── functions/
    │   └── reddit.py         # third-party (Reddit) API helper function
    ├── templates/            # HTML templates (base.html + shop/ pages)
    └── static/shop/          # CSS and static assets

About

A Django eCommerce application supporting 3 types of users (buyers, vendors, and admins) Buyers browse and order products, vendors run stores) and fulfil orders, and admins moderate products and view vendor revenue.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages