Database Schema
StudioSync uses PostgreSQL as its relational database. This document outlines the core database models and their relationships.
Architecture
The database is organized into modular apps, each with its own set of models:
- core: Users, studios, teachers, students, families, bands
- lessons: Lessons, recurring patterns, lesson notes, goals, lesson plans
- billing: Invoices, invoice line items, payments, payment methods
- inventory: Inventory items, checkout logs, practice rooms, room reservations
- resources: Resources, resource checkouts
- messaging: Message threads, messages
- notifications: Notifications
Core Models
User
The custom user model with email-based authentication.
Fields:
id(UUID): Primary keyemail(EmailField): Unique, used for loginpassword(CharField): Hashed passwordfirst_name,last_name(CharField): User's namerole(CharField): User role - 'admin', 'teacher', 'student', 'parent'phone_number(CharField): Optional contact numberavatar(ImageField): Profile pictureis_active,is_staff,is_superuser(BooleanField): Account statuscreated_at,updated_at,last_login(DateTimeField): Timestamps
Relationships:
- One-to-One with Teacher (via
teacher_profile) - One-to-One with Student (via
student_profile)
Studio
Represents a music studio or school.
Fields:
id(UUID): Primary keyname(CharField): Studio nameslug(SlugField): URL-friendly identifierdescription(TextField): Studio descriptionwebsite,email,phone(CharField): Contact informationaddress,city,state,country,postal_code(CharField): Locationlogo(ImageField): Studio logotheme_color(CharField): Brand colorsettings(JSONField): Custom settingslayout_data(JSONField): Studio builder layout datais_active(BooleanField): Studio statuscreated_at,updated_at(DateTimeField): Timestamps
Relationships:
- Has many Teachers
- Has many Students
- Has many Resources
- Has many Inventory Items
Teacher
Teacher profile extending User.
Fields:
id(UUID): Primary keyuser(OneToOneField → User): Associated user accountstudio(ForeignKey → Studio): Studio affiliationbio(TextField): Teacher biographyspecialties(JSONField): List of specialties/instrumentshourly_rate(DecimalField): Default lesson rateis_accepting_students(BooleanField): Availability statusbooking_buffer_minutes(IntegerField): Time between lessonsis_active(BooleanField): Active statuscreated_at,updated_at(DateTimeField): Timestamps
Relationships:
- Belongs to User
- Belongs to Studio
- Has many Lessons (as teacher)
- Has many Students (as primary teacher)
Student
Student profile extending User.
Fields:
id(UUID): Primary keyuser(OneToOneField → User): Associated user accountstudio(ForeignKey → Studio): Studio enrollmentfamily(ForeignKey → Family, nullable): Family membershipprimary_teacher(ForeignKey → Teacher, nullable): Default teacherinstrument(CharField): Primary instrumentspecialties(JSONField): List of instruments/subjectsdate_of_birth(DateField): Birth dateemergency_contact_name,emergency_contact_phone(CharField): Emergency infoenrollment_date(DateField): When student joinednotes(TextField): General notesis_active(BooleanField): Active enrollment statuscreated_at,updated_at(DateTimeField): Timestamps
Relationships:
- Belongs to User
- Belongs to Studio
- Optionally belongs to Family
- Optionally has primary Teacher
- Has many Lessons
- Has many Goals
- Has many Resource Checkouts
Family
Groups multiple students for billing purposes.
Fields:
id(UUID): Primary keystudio(ForeignKey → Studio): Studio associationname(CharField): Family nameprimary_contact(ForeignKey → User): Primary parent/guardianphone_number(CharField): Contact phoneaddress(TextField): Mailing addressbilling_email(EmailField): Email for invoicescreated_at,updated_at(DateTimeField): Timestamps
Relationships:
- Has many Students
- Has many Invoices
- Has many Payment Methods
Band
Groups students into bands/ensembles for billing.
Fields:
id(UUID): Primary keystudio(ForeignKey → Studio): Studio associationname(CharField): Band nameprimary_contact(ForeignKey → User): Band leader/contactmembers(ManyToManyField → User): Band membersemail,phone_number(CharField): Contact informationaddress,city,state,postal_code,country(CharField): Locationnotes(TextField): Additional informationcreated_at,updated_at(DateTimeField): Timestamps
Relationships:
- Has many User members
- Has many Invoices
- Has many Payment Methods
Lessons Models
Lesson
Individual lesson instance.
Fields:
id(UUID): Primary keystudio(ForeignKey → Studio): Studio where lesson occursteacher(ForeignKey → Teacher): Instructorstudent(ForeignKey → Student): Student attendingscheduled_start,scheduled_end(DateTimeField): Scheduled timeactual_start,actual_end(DateTimeField, nullable): Actual timelocation(CharField): Room or platformlesson_type(CharField): Private, group, ensemble, onlinestatus(CharField): Scheduled, in_progress, completed, cancelled, no_showrecurring_pattern(ForeignKey → RecurringPattern, nullable): If from recurringcancellation_reason(TextField): Why cancelledcreated_at,updated_at(DateTimeField): Timestamps
Relationships:
- Belongs to Studio, Teacher, Student
- Optionally belongs to RecurringPattern
- Has many LessonNotes
Methods:
duration_minutes(): Calculate lesson length
RecurringPattern
Defines recurring lesson schedules.
Fields:
id(UUID): Primary keystudio(ForeignKey → Studio): Studio associationteacher(ForeignKey → Teacher): Assigned teacherstudent(ForeignKey → Student): Enrolled studentfrequency(CharField): Weekly, bi-weekly, monthlyday_of_week(CharField): Monday-Sundaytime(TimeField): Lesson start timeduration_minutes(IntegerField): Lesson lengthstart_date(DateField): Pattern startend_date(DateField, nullable): Pattern endis_active(BooleanField): Active patterncreated_at,updated_at(DateTimeField): Timestamps
Relationships:
- Generates many Lessons
LessonNote
Detailed notes for a lesson.
Fields:
id(UUID): Primary keylesson(ForeignKey → Lesson): Associated lessonteacher(ForeignKey → Teacher): Note authorsummary(TextField): Overview of lessontopics_covered,assignments,strengths,challenges(JSONField): Structured datahomework(TextField): Practice assignmentsvisible_to_student,visible_to_parent(BooleanField): Visibility settingscreated_at,updated_at(DateTimeField): Timestamps
StudentGoal
Student progress goals.
Fields:
id(UUID): Primary keystudent(ForeignKey → Student): Goal ownerteacher(ForeignKey → Teacher): Goal creatortitle(CharField): Goal namedescription(TextField): Detailed explanationtarget_date(DateField, nullable): Completion targetstatus(CharField): Active, achieved, abandonedprogress_percentage(IntegerField): 0-100created_at,updated_at(DateTimeField): Timestamps
LessonPlan
Reusable lesson plan templates.
Fields:
id(UUID): Primary keystudio(ForeignKey → Studio): Studio associationteacher(ForeignKey → Teacher): Plan creatortitle(CharField): Plan namedescription(TextField): Overviewspecialty(CharField): Instrument/subjectduration_minutes(IntegerField): Recommended lengthobjectives,materials,activities(JSONField): Lesson componentstags(JSONField): Searchable keywordsis_public(BooleanField): Share with other teacherscreated_at,updated_at(DateTimeField): Timestamps
Billing Models
Invoice
Invoice for a family or band.
Fields:
id(UUID): Primary keystudio(ForeignKey → Studio): Issuing studioband(ForeignKey → Band): Billing entityinvoice_number(CharField): Unique invoice identifierstatus(CharField): Draft, sent, paid, partial, overdue, cancelledissue_date(DateField): Invoice creation datedue_date(DateField): Payment deadlinesubtotal,tax,total,amount_paid(DecimalField): Financial amountsnotes(TextField): Additional informationcreated_at,updated_at(DateTimeField): Timestamps
Relationships:
- Has many InvoiceLineItems
- Has many Payments
Methods:
balance_due(): Calculate remaining balanceis_overdue(): Check if past due datecalculate_totals(): Recalculate from line items
InvoiceLineItem
Individual line on an invoice.
Fields:
id(UUID): Primary keyinvoice(ForeignKey → Invoice): Parent invoicedescription(CharField): Item descriptionquantity(IntegerField): Number of itemsunit_price(DecimalField): Price per itemtotal_price(DecimalField): quantity × unit_pricelesson(ForeignKey → Lesson, nullable): Associated lesson if applicablecreated_at(DateTimeField): Timestamp
Payment
Payment record for invoices.
Fields:
id(UUID): Primary keyinvoice(ForeignKey → Invoice): Invoice being paidamount(DecimalField): Payment amountpayment_method(CharField): Cash, check, credit_card, etc.transaction_id(CharField): External payment IDstatus(CharField): Pending, completed, failed, refundednotes(TextField): Payment notesprocessed_by(ForeignKey → User, nullable): Who processedprocessed_at,refunded_at(DateTimeField): Timestampscreated_at(DateTimeField): Timestamp
PaymentMethod
Saved payment methods for recurring billing.
Fields:
id(UUID): Primary keyband(ForeignKey → Band): Ownerpayment_type(CharField): Credit card, bank accountlast_four(CharField): Last 4 digitsstripe_payment_method_id(CharField): Stripe referenceis_default,is_active(BooleanField): Status flagscreated_at,updated_at(DateTimeField): Timestamps
Inventory Models
InventoryItem
Physical items tracked by the studio.
Fields:
name(CharField): Item namecategory(CharField): Instrument, equipment, sheet-music, accessories, otherquantity,available_quantity(IntegerField): Total and availablecondition(CharField): Excellent, good, fair, needs-repairstatus(CharField): Available, checked-out, maintenance, retiredlocation(CharField): Where storedvalue(DecimalField): Item valueserial_number(CharField): Serial/inventory numberis_borrowable(BooleanField): Can students check outmax_checkout_days(IntegerField): Maximum lending periodpurchase_date,last_maintenance(DateField): Important datesnotes(TextField): Additional infocreated_at,updated_at(DateTimeField): Timestampscreated_by(ForeignKey → User): Who added item
Methods:
is_low_stock(): Check if available quantity ≤ 2
CheckoutLog
Track item checkouts.
Fields:
item(ForeignKey → InventoryItem): Checked out itemstudent(ForeignKey → User): Who borrowedquantity(IntegerField): How manycheckout_date,due_date,return_date(DateTimeField/DateField): Datesstatus(CharField): Pending, approved, returned, overdue, cancellednotes(TextField): Special instructionsapproved_by(ForeignKey → User, nullable): Approverapproved_at(DateTimeField, nullable): Approval time
Methods:
is_overdue(): Check if past due date
PracticeRoom
Practice rooms available for reservation.
Fields:
name(CharField): Room identifiercapacity(IntegerField): Max occupancydescription(TextField): Room descriptionequipment(TextField): Available equipmenthourly_rate(DecimalField): Rental costis_active(BooleanField): Room availability
RoomReservation
Student practice room bookings.
Fields:
room(ForeignKey → PracticeRoom): Reserved roomstudent(ForeignKey → User): Who reservedstart_time,end_time(DateTimeField): Reservation periodstatus(CharField): Pending, confirmed, cancelled, completed, no-showtotal_cost(DecimalField): Calculated costis_paid(BooleanField): Payment statusnotes(TextField): Additional infocreated_at,updated_at(DateTimeField): Timestamps
Methods:
save(): Auto-calculate total_cost from duration and rateclean(): Validate no overlapping reservations
Resources Models
Resource
Digital and physical resources.
Fields:
id(UUID): Primary keystudio(ForeignKey → Studio): Owning studiouploaded_by(ForeignKey → User): Creatortitle(CharField): Resource namedescription(TextField): Detailsresource_type(CharField): PDF, audio, video, image, physical, link, otherfile(FileField): Digital file (if applicable)file_size(BigIntegerField): File size in bytesmime_type(CharField): Content typeexternal_url(URLField): External link (if applicable)tags,category(CharField/JSONField): Organizationis_physical_item(BooleanField): Physical vs digitalquantity_total,quantity_available(IntegerField): For physical itemsis_lendable(BooleanField): Can be checked outcheckout_duration_days(IntegerField): Lending periodis_public(BooleanField): Visible to all studentsshared_with_students(ManyToManyField → Student): Specific accesscreated_at,updated_at(DateTimeField): Timestamps
ResourceCheckout
Track lending of physical resources.
Fields:
id(UUID): Primary keyresource(ForeignKey → Resource): Borrowed resourcestudent(ForeignKey → Student): Borrowerstatus(CharField): Checked_out, returned, overdue, lostchecked_out_at,due_date,returned_at(DateTimeField/DateField): Datescheckout_notes,return_notes(TextField): Documentationcreated_at,updated_at(DateTimeField): Timestamps
Methods:
is_overdue(): Check if past due date
Messaging Models
MessageThread
Conversation between users.
Fields:
id(UUID): Primary keystudio(ForeignKey → Studio): Studio contextparticipants(ManyToManyField → User): Thread memberssubject(CharField): Thread titlecreated_at,updated_at(DateTimeField): Timestamps
Message
Individual message in a thread.
Fields:
id(UUID): Primary keythread(ForeignKey → MessageThread): Parent threadsender(ForeignKey → User): Message authorcontent(TextField): Message textis_read(BooleanField): Read statusread_by(ManyToManyField → User): Who has readcreated_at(DateTimeField): Timestamp
Notifications Models
Notification
System notifications for users.
Fields:
id(UUID): Primary keyuser(ForeignKey → User): Recipienttitle(CharField): Notification titlemessage(TextField): Notification contentnotification_type(CharField): Type of notificationis_read(BooleanField): Read statuslink(CharField): Optional action URLcreated_at(DateTimeField): Timestamp
Database Indexes
Key indexes for performance:
Lessons:
scheduled_startteacher, scheduled_startstudent, scheduled_startstatus
Invoices:
invoice_number(unique)band, statusdue_date
Payments:
transaction_idinvoice, status
Resources:
resource_typestudio, is_public
Common Patterns
UUID Primary Keys
All models use UUID primary keys for:
- Security (non-sequential)
- Distribution (no collision risk)
- API design (clean URLs)
Timestamps
All models include:
created_at: When record was createdupdated_at: Last modification time
Soft Deletes
Many models use is_active instead of hard deletes to preserve history.
JSON Fields
Used for flexible, structured data:
specialties,tags,objectives,activities- Searchable using PostgreSQL JSON operators
Related Documentation
- Architecture - Overall system architecture
- API Reference - API endpoints