Online Food Ordering System

 

Online Food Ordering System

Project Overview

This project is a web-based platform that allows users to browse local restaurants, view their menus, add items to a shopping cart, place orders, and make payments securely using Stripe. Restaurants can manage their profiles and menus through an admin interface. The system is built using Django for the backend, PostgreSQL as the database, and Stripe API for payment processing. It includes user authentication, order management, and basic admin functionalities.

Key Features:

  • User registration, login, and profile management.
  • Browse restaurants and their menus.
  • Shopping cart for adding/removing items.
  • Checkout process with Stripe payment integration.
  • Order history for users.
  • Admin dashboard for managing restaurants, menus, and orders.
  • Email notifications for order confirmations (using Django's built-in email system).

The project emphasizes security (e.g., using Django's authentication), scalability (with PostgreSQL), and ease of use.

Requirements

Hardware/Software

  • Python 3.10 or higher.
  • PostgreSQL 12 or higher.
  • A Stripe account for API keys (test mode recommended for development).
  • Web browser for testing (e.g., Chrome, Firefox).

Dependencies

  • Django 4.2 (web framework).
  • psycopg2 (PostgreSQL adapter).
  • Stripe Python library.
  • django-crispy-forms (for better form rendering).
  • pillow (for image handling, if restaurant logos are added).

Install dependencies via pip:

text
pip install django psycopg2-binary stripe django-crispy-forms pillow

Setup Instructions

  1. Clone the Project:
    • Create a directory for the project: mkdir online-food-ordering && cd online-food-ordering.
    • Initialize a virtual environment: python -m venv venv and activate it (source venv/bin/activate on Unix, venv\Scripts\activate on Windows).
    • Install dependencies as listed above.
  2. Database Setup:
    • Install PostgreSQL and create a database: createdb food_ordering_db.
    • Update settings.py with your database credentials (see code below).
  3. Environment Variables:
    • Create a .env file in the project root with:
      text
      SECRET_KEY=your_django_secret_key
      DEBUG=True
      DATABASE_NAME=food_ordering_db
      DATABASE_USER=your_pg_user
      DATABASE_PASSWORD=your_pg_password
      DATABASE_HOST=localhost
      DATABASE_PORT=5432
      STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key
      STRIPE_SECRET_KEY=your_stripe_secret_key
      EMAIL_HOST_USER=your_email@example.com
      EMAIL_HOST_PASSWORD=your_email_password
    • Use python-decouple for loading env vars (install via pip).
  4. Run Migrations:
    • python manage.py makemigrations
    • python manage.py migrate
  5. Create Superuser:
    • python manage.py createsuperuser (for admin access at /admin/).
  6. Run the Server:
    • python manage.py runserver
    • Access at http://127.0.0.1:8000/.
  7. Stripe Setup:
    • Sign up at stripe.com, get test keys from dashboard.
    • In production, set DEBUG=False and use live keys.
  8. Testing:
    • Use Stripe test cards (e.g., 4242 4242 4242 4242) for payments.

Architecture

High-Level Design

  • Frontend: HTML/CSS/JS templates with Bootstrap for responsiveness.
  • Backend: Django views handle logic; RESTful URLs.
  • Database: PostgreSQL stores users, restaurants, menus, orders.
  • Payment Flow: User adds to cart → Checkout → Stripe session → Payment success → Order confirmation.
  • Security: CSRF protection, authentication decorators, HTTPS in production.

Data Flow

  1. User browses restaurants → selects menu items → adds to cart (session-based).
  2. Checkout: Create Stripe session → Redirect to Stripe → On success, update order status.
  3. Email notification sent post-payment.

Models

  • User: Extended from Django's AbstractUser for custom fields (e.g., address).
  • Restaurant: Name, address, description, image.
  • MenuItem: Name, description, price, restaurant (ForeignKey).
  • Order: User (ForeignKey), restaurant (ForeignKey), total_price, status, created_at.
  • OrderItem: Order (ForeignKey), menu_item (ForeignKey), quantity, price.
  • Payment: Order (OneToOne), stripe_charge_id, amount, timestamp.

Views

  • Class-based views for lists/details (e.g., RestaurantListView).
  • Function-based for cart and checkout.

Templates

  • Base template with navigation.
  • Specific templates for home, restaurant detail, cart, checkout.

Apps

  • accounts: User auth and profiles.
  • restaurants: Restaurant and menu management.
  • orders: Cart, orders, payments.

Usage Guide

For Users

  • Register/Login at /accounts/register/ or /accounts/login/.
  • Browse restaurants at /restaurants/.
  • View menu and add items to cart.
  • Go to /orders/cart/ to view/edit cart.
  • Checkout at /orders/checkout/, pay via Stripe.
  • View orders at /orders/history/.

For Admins/Restaurant Owners

  • Login to /admin/ to add/edit restaurants and menus.
  • (Future: Role-based access for restaurant owners to manage their own.)

Error Handling

  • 404 for invalid pages.
  • Validation errors in forms.
  • Stripe errors handled with messages.

Potential Improvements

  • Add search functionality.
  • Integrate maps for restaurant locations (Google Maps API).
  • Real-time order tracking (WebSockets).
  • Mobile app integration.
  • Analytics dashboard.

Code

Below is the complete code for the project. Create the directory structure as shown.

Project Structure

text
online-food-ordering/
├── manage.py
├── requirements.txt
├── .env
├── food_ordering/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py
├── accounts/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── migrations/
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ ├── views.py
├── restaurants/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ ├── views.py
├── orders/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── migrations/
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ ├── views.py
├── templates/
│ ├── base.html
│ ├── home.html
│ ├── accounts/register.html
│ ├── accounts/login.html
│ ├── accounts/profile.html
│ ├── restaurants/list.html
│ ├── restaurants/detail.html
│ ├── orders/cart.html
│ ├── orders/checkout.html
│ ├── orders/success.html
│ ├── orders/history.html
├── static/
│ └── css/
│ └── style.css

manage.py

python
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'food_ordering.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

requirements.txt

text
django==4.2
psycopg2-binary==2.9.9
stripe==10.8.0
django-crispy-forms==2.3
pillow==10.4.0
python-decouple==3.8

food_ordering/settings.py

python
from pathlib import Path
from decouple import config
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'crispy_forms',
'accounts',
'restaurants',
'orders',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'food_ordering.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'food_ordering.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': config('DATABASE_NAME'),
'USER': config('DATABASE_USER'),
'PASSWORD': config('DATABASE_PASSWORD'),
'HOST': config('DATABASE_HOST'),
'PORT': config('DATABASE_PORT'),
}
}
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
STATIC_URL = 'static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = 'accounts.CustomUser'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
CRISPY_TEMPLATE_PACK = 'bootstrap4'
STRIPE_PUBLISHABLE_KEY = config('STRIPE_PUBLISHABLE_KEY')
STRIPE_SECRET_KEY = config('STRIPE_SECRET_KEY')
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')

food_ordering/urls.py

python
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')),
path('restaurants/', include('restaurants.urls')),
path('orders/', include('orders.urls')),
path('', TemplateView.as_view(template_name='home.html'), name='home'),
]

accounts/models.py

python
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
address = models.TextField(blank=True, null=True)
phone_number = models.CharField(max_length=15, blank=True, null=True)
def __str__(self):
return self.username

accounts/forms.py

python
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ('username', 'email', 'password1', 'password2', 'address', 'phone_number')

accounts/views.py

python
from django.shortcuts import render, redirect
from django.contrib.auth import login
from .forms import CustomUserCreationForm
def register(request):
if request.method == 'POST':
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('home')
else:
form = CustomUserCreationForm()
return render(request, 'accounts/register.html', {'form': form})
def profile(request):
return render(request, 'accounts/profile.html')

accounts/urls.py

python
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
path('register/', views.register, name='register'),
path('login/', auth_views.LoginView.as_view(template_name='accounts/login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('profile/', views.profile, name='profile'),
]

restaurants/models.py

python
from django.db import models
class Restaurant(models.Model):
name = models.CharField(max_length=100)
address = models.TextField()
description = models.TextField(blank=True)
image = models.ImageField(upload_to='restaurants/', blank=True, null=True)
def __str__(self):
return self.name
class MenuItem(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, related_name='menu_items')
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
image = models.ImageField(upload_to='menu_items/', blank=True, null=True)
def __str__(self):
return f"{self.name} - {self.restaurant.name}"

restaurants/views.py

python
from django.views.generic import ListView, DetailView
from .models import Restaurant
class RestaurantListView(ListView):
model = Restaurant
template_name = 'restaurants/list.html'
context_object_name = 'restaurants'
class RestaurantDetailView(DetailView):
model = Restaurant
template_name = 'restaurants/detail.html'
context_object_name = 'restaurant'

restaurants/urls.py

python
from django.urls import path
from .views import RestaurantListView, RestaurantDetailView
urlpatterns = [
path('', RestaurantListView.as_view(), name='restaurant_list'),
path('<int:pk>/', RestaurantDetailView.as_view(), name='restaurant_detail'),
]

orders/models.py

python
from django.db import models
from django.conf import settings
class Order(models.Model):
STATUS_CHOICES = [
('PENDING', 'Pending'),
('COMPLETED', 'Completed'),
('CANCELLED', 'Cancelled'),
]
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
restaurant = models.ForeignKey('restaurants.Restaurant', on_delete=models.CASCADE)
total_price = models.DecimalField(max_digits=8, decimal_places=2)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='PENDING')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Order {self.id} by {self.user.username}"
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
menu_item = models.ForeignKey('restaurants.MenuItem', on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return f"{self.quantity} x {self.menu_item.name}"
class Payment(models.Model):
order = models.OneToOneField(Order, on_delete=models.CASCADE)
stripe_charge_id = models.CharField(max_length=50)
amount = models.DecimalField(max_digits=8, decimal_places=2)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Payment for Order {self.order.id}"

orders/forms.py

python
from django import forms
class CheckoutForm(forms.Form):
address = forms.CharField(widget=forms.Textarea(attrs={'rows': 3}))
phone = forms.CharField(max_length=15)

orders/views.py

python
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.conf import settings
from django.core.mail import send_mail
from .models import Order, OrderItem, Payment
from restaurants.models import MenuItem
import stripe
stripe.api_key = settings.STRIPE_SECRET_KEY
def add_to_cart(request, item_id):
item = get_object_or_404(MenuItem, id=item_id)
cart = request.session.get('cart', {})
cart[str(item_id)] = cart.get(str(item_id), 0) + 1
request.session['cart'] = cart
messages.success(request, f'Added {item.name} to cart.')
return redirect('restaurant_detail', pk=item.restaurant.id)
def cart(request):
cart = request.session.get('cart', {})
items = []
total = 0
for item_id, quantity in cart.items():
item = get_object_or_404(MenuItem, id=item_id)
subtotal = item.price * quantity
total += subtotal
items.append({'item': item, 'quantity': quantity, 'subtotal': subtotal})
return render(request, 'orders/cart.html', {'items': items, 'total': total})
def remove_from_cart(request, item_id):
cart = request.session.get('cart', {})
if str(item_id) in cart:
del cart[str(item_id)]
request.session['cart'] = cart
return redirect('cart')
@login_required
def checkout(request):
cart = request.session.get('cart', {})
if not cart:
return redirect('cart')
items = []
total = 0
restaurant = None
for item_id, quantity in cart.items():
item = get_object_or_404(MenuItem, id=item_id)
if not restaurant:
restaurant = item.restaurant
elif restaurant != item.restaurant:
messages.error(request, 'All items must be from the same restaurant.')
return redirect('cart')
subtotal = item.price * quantity
total += subtotal
items.append({'item': item, 'quantity': quantity, 'subtotal': subtotal})
if request.method == 'POST':
# Create order
order = Order.objects.create(user=request.user, restaurant=restaurant, total_price=total)
for item in items:
OrderItem.objects.create(order=order, menu_item=item['item'], quantity=item['quantity'], price=item['item'].price)
# Create Stripe session
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price_data': {
'currency': 'usd',
'product_data': {'name': 'Food Order'},
'unit_amount': int(total * 100),
},
'quantity': 1,
}],
mode='payment',
success_url=request.build_absolute_uri('/orders/success/') + '?session_id={CHECKOUT_SESSION_ID}',
cancel_url=request.build_absolute_uri('/orders/cart/'),
)
return redirect(session.url, code=303)
return render(request, 'orders/checkout.html', {'items': items, 'total': total})
@login_required
def payment_success(request):
session_id = request.GET.get('session_id')
if not session_id:
return redirect('cart')
session = stripe.checkout.Session.retrieve(session_id)
if session.payment_status == 'paid':
# Find the latest pending order for the user (assuming one at a time)
order = Order.objects.filter(user=request.user, status='PENDING').latest('created_at')
Payment.objects.create(order=order, stripe_charge_id=session.payment_intent, amount=order.total_price)
order.status = 'COMPLETED'
order.save()
# Send email
send_mail(
'Order Confirmation',
f'Your order {order.id} has been placed successfully.',
settings.EMAIL_HOST_USER,
[request.user.email],
fail_silently=False,
)
# Clear cart
request.session['cart'] = {}
return render(request, 'orders/success.html', {'order': order})
return redirect('cart')
@login_required
def order_history(request):
orders = Order.objects.filter(user=request.user).order_by('-created_at')
return render(request, 'orders/history.html', {'orders': orders})

orders/urls.py

python
from django.urls import path
from . import views
urlpatterns = [
path('add_to_cart/<int:item_id>/', views.add_to_cart, name='add_to_cart'),
path('cart/', views.cart, name='cart'),
path('remove_from_cart/<int:item_id>/', views.remove_from_cart, name='remove_from_cart'),
path('checkout/', views.checkout, name='checkout'),
path('success/', views.payment_success, name='payment_success'),
path('history/', views.order_history, name='order_history'),
]

templates/base.html

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}Online Food Ordering{% endblock %}</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'home' %}">Food Ordering</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item"><a class="nav-link" href="{% url 'restaurant_list' %}">Restaurants</a></li>
{% if user.is_authenticated %}
<li class="nav-item"><a class="nav-link" href="{% url 'cart' %}">Cart</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'order_history' %}">Orders</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'profile' %}">Profile</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'logout' %}">Logout</a></li>
{% else %}
<li class="nav-item"><a class="nav-link" href="{% url 'login' %}">Login</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'register' %}">Register</a></li>
{% endif %}
</ul>
</div>
</nav>
<div class="container mt-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.3/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

templates/home.html

html
{% extends 'base.html' %}
{% block title %}Home{% endblock %}
{% block content %}
<h1>Welcome to Online Food Ordering System</h1>
<p>Browse local restaurants and order your favorite food!</p>
<a href="{% url 'restaurant_list' %}" class="btn btn-primary">View Restaurants</a>
{% endblock %}

templates/accounts/register.html

html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}Register{% endblock %}
{% block content %}
<h2>Register</h2>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-success">Register</button>
</form>
{% endblock %}

templates/accounts/login.html

html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}Login{% endblock %}
{% block content %}
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-success">Login</button>
</form>
{% endblock %}

templates/accounts/profile.html

html
{% extends 'base.html' %}
{% block title %}Profile{% endblock %}
{% block content %}
<h2>Your Profile</h2>
<p>Username: {{ user.username }}</p>
<p>Email: {{ user.email }}</p>
<p>Address: {{ user.address }}</p>
<p>Phone: {{ user.phone_number }}</p>
{% endblock %}

templates/restaurants/list.html

html
{% extends 'base.html' %}
{% block title %}Restaurants{% endblock %}
{% block content %}
<h1>Local Restaurants</h1>
<div class="row">
{% for restaurant in restaurants %}
<div class="col-md-4">
<div class="card mb-4">
{% if restaurant.image %}
<img src="{{ restaurant.image.url }}" class="card-img-top" alt="{{ restaurant.name }}">
{% endif %}
<div class="card-body">
<h5 class="card-title"><a href="{% url 'restaurant_detail' restaurant.pk %}">{{ restaurant.name }}</a></h5>
<p class="card-text">{{ restaurant.address }}</p>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}

templates/restaurants/detail.html

html
{% extends 'base.html' %}
{% block title %}{{ restaurant.name }}{% endblock %}
{% block content %}
<h1>{{ restaurant.name }}</h1>
<p>{{ restaurant.description }}</p>
<h2>Menu</h2>
<ul class="list-group">
{% for item in restaurant.menu_items.all %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ item.name }} - ${{ item.price }}
<a href="{% url 'add_to_cart' item.id %}" class="btn btn-sm btn-primary">Add to Cart</a>
</li>
{% endfor %}
</ul>
{% endblock %}

templates/orders/cart.html

html
{% extends 'base.html' %}
{% block title %}Cart{% endblock %}
{% block content %}
<h1>Your Cart</h1>
{% if items %}
<table class="table">
<thead>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Subtotal</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for cart_item in items %}
<tr>
<td>{{ cart_item.item.name }}</td>
<td>{{ cart_item.quantity }}</td>
<td>${{ cart_item.subtotal }}</td>
<td><a href="{% url 'remove_from_cart' cart_item.item.id %}" class="btn btn-danger btn-sm">Remove</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<h4>Total: ${{ total }}</h4>
<a href="{% url 'checkout' %}" class="btn btn-success">Checkout</a>
{% else %}
<p>Your cart is empty.</p>
{% endif %}
{% endblock %}

templates/orders/checkout.html

html
{% extends 'base.html' %}
{% block title %}Checkout{% endblock %}
{% block content %}
<h1>Checkout</h1>
<table class="table">
<!-- Similar to cart table -->
{% for cart_item in items %}
<tr>
<td>{{ cart_item.item.name }}</td>
<td>{{ cart_item.quantity }}</td>
<td>${{ cart_item.subtotal }}</td>
</tr>
{% endfor %}
</table>
<h4>Total: ${{ total }}</h4>
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="address">Delivery Address</label>
<textarea class="form-control" id="address" name="address" rows="3" required></textarea>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="text" class="form-control" id="phone" name="phone" required>
</div>
<button type="submit" class="btn btn-primary">Pay with Stripe</button>
</form>
{% endblock %}

templates/orders/success.html

html
{% extends 'base.html' %}
{% block title %}Success{% endblock %}
{% block content %}
<h1>Payment Successful!</h1>
<p>Your order {{ order.id }} has been placed. Total: ${{ order.total_price }}</p>
<p>An email confirmation has been sent.</p>
<a href="{% url 'home' %}" class="btn btn-primary">Back to Home</a>
{% endblock %}

templates/orders/history.html

html
{% extends 'base.html' %}
{% block title %}Order History{% endblock %}
{% block content %}
<h1>Your Orders</h1>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Restaurant</th>
<th>Total</th>
<th>Status</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{% for order in orders %}
<tr>
<td>{{ order.id }}</td>
<td>{{ order.restaurant.name }}</td>
<td>${{ order.total_price }}</td>
<td>{{ order.status }}</td>
<td>{{ order.created_at }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

static/css/style.css

css
/* Basic styling */
body {
font-family: Arial, sans-serif;
}
.card-img-top {
height: 200px;
object-fit: cover;
}

This completes the project. To run it, follow the setup instructions. Add initial data via admin (e.g., restaurants and menu items). For production, secure with HTTPS and proper env management.

Comments

Popular posts from this blog

Intrusion Detection System Project

TruthGuard: AI-Powered Fake News Detector

Library Management System Project