🐍 Django 4.2 · Python · SQLite · Bootstrap 5

Campus SkillSwap

A full-stack student marketplace built from the ground up in Django — no tutorials followed, no scaffolds cloned. Students post skills they can teach, browse what others offer, leave star reviews, and send booking requests. Complete auth, CRUD, search, and a personal dashboard, all wired together with Django's MVT pattern.

3DB Models
7Views
9Skill Categories
5★Rating System
100%From Scratch

The Skill Marketplace

Browse view with live search + category filter. Every card links to a full detail page with reviews and booking form.

127.0.0.1:8000/skills/
Campus SkillSwap
All Categories ▾
Tech & Coding
Python & Django Tutoring
Struggling with loops, OOP, or Django models? I'll pair-program with you until it clicks.
★★★★★ $15/hr
Tutoring
MKTG 3310 Exam Prep
Got an A in every marketing course. Let's go through past exams and concept review together.
★★★★☆ FREE
Design
Figma UI Design
I'll teach you Figma from components to auto-layout. Great for MIS students building apps.
★★★★★ $20/hr

Database Schema

Three models, two foreign key relationships. Django handles the SQL — you write the Python class, it creates the table.

👤 User (Django built-in)
idPK · int
usernameCharField
emailEmailField
passwordhashed
🎯 Skill
idPK · int
ownerFK → User
titleCharField(120)
descriptionTextField
categorychoices
is_free / priceBool / Decimal
contact_prefchoices
statuschoices
created_atauto_now_add
⭐ Review
idPK
skillFK → Skill
reviewerFK → User
rating1–5 int
commentTextField
unique_together: (skill, reviewer)
📅 BookingRequest
idPK
skillFK → Skill
requesterFK → User
messageTextField
proposed_timeCharField
statuspending/accepted…

Feature Deep Dive

Click a feature to see how it's built — from the URL pattern to the view logic.

Registration & Login

Django's built-in User model handles password hashing and session management. The custom RegisterForm wraps UserCreationForm for consistent styling.

The @login_required decorator protects any view that modifies data — post a skill, edit, delete, or send a booking. Unauthenticated users get redirected to login and bounced back to their original destination after signing in.

# users/views.py
from django.contrib.auth import login
from .forms import RegisterForm

def register(request):
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect('skills:dashboard')
    else:
        form = RegisterForm()
    return render(request,
        'registration/register.html',
        {'form': form}
    )

# Protect a view with one decorator:
@login_required
def skill_create(request):
    ...

# settings.py
LOGIN_REDIRECT_URL = 'skills:dashboard'
LOGIN_URL          = 'users:login'

Booking Request System

The skill detail page handles two separate form submissions in one view — a review form and a booking form — distinguished by a hidden field name in the POST body. Both forms are validated server-side with ModelForms.

Owners get an inbox of incoming requests on their dashboard and can accept, decline, or mark bookings complete via a single booking_update view that takes an action URL parameter.

# Dual-form POST handling
if request.method == 'POST':
    if 'submit_review' in request.POST:
        # handle star review
        review_form = ReviewForm(request.POST)
        if review_form.is_valid():
            review = review_form.save(commit=False)
            review.skill    = skill
            review.reviewer = request.user
            review.save()

    elif 'submit_booking' in request.POST:
        # handle booking request
        booking_form = BookingRequestForm(request.POST)
        if booking_form.is_valid():
            booking = booking_form.save(commit=False)
            booking.skill     = skill
            booking.requester = request.user
            booking.save()

# Owner action URL: /skills/booking/<pk>/accept/
def booking_update(request, pk, action):
    booking = get_object_or_404(BookingRequest, pk=pk)
    if action == 'accept':
        booking.status = 'accepted'
    booking.save()

Personal Dashboard

Each user gets a dashboard showing their posted skills, incoming booking requests from other students, and their own outgoing requests — all pulled from the database in a single view with three querysets.

Stats (skill count, inbox count, sent count) are computed with Django's .count() and passed as a list of tuples so the template can loop over them cleanly.

# skills/views.py — dashboard()
@login_required
def dashboard(request):
    my_skills = Skill.objects.filter(
        owner=request.user
    )
    incoming = BookingRequest.objects.filter(
        skill__owner=request.user
    ).order_by('-created_at')

    outgoing = BookingRequest.objects.filter(
        requester=request.user
    ).order_by('-created_at')

    stats = [
        ('My Skills',         my_skills.count(),  '📋'),
        ('Incoming Requests', incoming.count(),  '📬'),
        ('Requests Sent',     outgoing.count(),  '📤'),
    ]

    return render(request,
        'skills/dashboard.html',
        {'my_skills': my_skills,
         'incoming':  incoming,
         'outgoing':  outgoing,
         'stats':     stats}
    )

The Vibe-Coding Story

01
Models First
Started with models.py — designed the Skill, Review, and BookingRequest tables before writing a single view. Got the ForeignKey relationships right (especially unique_together on Review) before running migrations.
02
URLs & Views
Wired up URL namespacing with app_name = 'skills' so templates could use {% url 'skills:skill_detail' pk=skill.pk %}. Built all seven FBVs — list, detail, create, update, delete, dashboard, booking_update.
03
Forms & Validation
ModelForms saved hours — Django auto-generates form fields from model attributes. Added custom clean() validation to SkillForm: if is_free=False and price is blank, raise a ValidationError before the form ever hits the database.
04
Auth Flow
Django's @login_required decorator handles redirect logic automatically. The hardest part was correctly setting LOGIN_URL, LOGIN_REDIRECT_URL, and LOGOUT_REDIRECT_URL in settings so the flow felt seamless.
05
Bootstrap Templates
Built a shared base.html with Bootstrap 5 navbar and flash messages. Every other template extends it with {% block content %}. The Django messages framework showed success/error alerts across form submissions automatically.
06
Permissions & Safety
Every destructive operation checks skill.owner == request.user before proceeding. Tried reviewing my own skill — got blocked. Tried booking my own skill — blocked. Django's ORM makes these checks trivially easy to add.

By the Numbers

Business Problem
College campuses have untapped peer expertise — students who could teach calculus, guitar, Python, or Figma to their classmates, but there's no platform designed for that exchange. Campus SkillSwap creates a marketplace specifically for student-to-student skill sharing with built-in booking and review infrastructure.
One-Sentence Summary
A full-stack Django web application with user registration, skill CRUD, keyword + category search with annotated average ratings, a 5-star review system (one per user per skill), a booking request workflow with accept/decline/complete states, and a personal dashboard tracking all activity in one place.
Tools & Stack
Django 4.2 Python 3.11 SQLite Bootstrap 5.3 Bootstrap Icons Django ORM ModelForms @login_required
Key Features
Full CRUD on skill listings with owner-only permissions. Dual-form POST handling on the detail page (reviews + bookings). Q-object keyword search across title and description. Avg rating annotation via Avg('reviews__rating'). Booking lifecycle management (pending → accepted → completed). Flash messages on every action. Django Admin registration for all models — Skill, Review, and BookingRequest are registered in admin.py so the built-in admin panel provides full data management without extra code.
My Role
Sole developer — designed all three models, wrote every view and URL pattern, built all templates from scratch with Bootstrap, configured Django auth settings, and handled every edge case (self-review prevention, self-booking prevention, owner-only edit/delete guards).
Biggest Challenge
The dual-form view on skill detail — handling a review submission and a booking submission from the same page required understanding how Django differentiates POST submitters. Using 'submit_review' in request.POST vs 'submit_booking' in request.POST as the branch condition was the key insight.
What I Learned
Django's ORM eliminates boilerplate SQL but requires understanding queryset laziness — queries only hit the database when evaluated, so structuring them efficiently matters. I also learned why Django separates concerns so strictly: models, views, and templates each have a single responsibility, which made debugging dramatically easier.
Course Context
Built for AI: Principles and Application (4V98) at Baylor University. The course requirement was to build a Django application with at least two models, authentication, and CRUD operations. Campus SkillSwap exceeds those requirements with three models, a booking workflow, search with Q objects, average rating annotation, and full permission enforcement on all write operations.
GitHub & Demo
⌥ GitHub Repository ↗ Built locally with VS Code + GitHub Copilot. Repository available on request — includes full models.py, views.py, templates, and migration history.