Cruise models
Cruise models (English fallback)
Aug. 17, 2025
Posted by admin
Nhom |
Notes |
|
from django.db import models from django.urls import reverse from django.contrib.auth import get_user_model from django.utils.translation import gettext_lazy as _ from decimal import Decimal from datetime import datetime, timedelta from apps.geo.models import Location
User = get_user_model()
|
Booking |
class CruiseBooking(models.Model): """Cruise bookings made through the platform""" STATUS_CHOICES = [ ('pending', 'Pending'), ('confirmed', 'Confirmed'), ('cancelled', 'Cancelled'), ('completed', 'Completed'), ]
booking_number = models.CharField(max_length=20, unique=True) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cruise_bookings') cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='bookings') cabin_category = models.ForeignKey(CabinCategory, on_delete=models.CASCADE)
# Guest information primary_guest_name = models.CharField(max_length=200) primary_guest_email = models.EmailField() primary_guest_phone = models.CharField(max_length=20) number_of_guests = models.PositiveIntegerField(default=2) special_requests = models.TextField(blank=True)
# Pricing base_price = models.DecimalField(max_digits=10, decimal_places=2) taxes_and_fees = models.DecimalField(max_digits=10, decimal_places=2) total_price = models.DecimalField(max_digits=10, decimal_places=2) commission_rate = models.DecimalField(max_digits=5, decimal_places=2, default=10) commission_amount = models.DecimalField(max_digits=10, decimal_places=2)
# Status and dates status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='pending') booking_date = models.DateTimeField(auto_now_add=True) confirmation_date = models.DateTimeField(null=True, blank=True) cancellation_date = models.DateTimeField(null=True, blank=True)
# Payment tracking deposit_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0) deposit_paid = models.BooleanField(default=False) final_payment_due = models.DateField(null=True, blank=True) final_payment_made = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: ordering = ['-created_at']
def save(self, *args, **kwargs): if not self.booking_number: # Generate unique booking number import uuid self.booking_number = f"CR{uuid.uuid4().hex[:8].upper()}" super().save(*args, **kwargs)
def __str__(self): return f"Booking {self.booking_number} - {self.cruise}"
|
Cruise Line |
class CruiseLine(models.Model): """Major cruise companies like Royal Caribbean, Norwegian, etc.""" name = models.CharField(max_length=100, verbose_name=_("Name")) name_vi = models.CharField(max_length=100, blank=True, verbose_name=_("Vietnamese Name")) code = models.CharField(max_length=10, unique=True, help_text=_("Short code like 'RCL', 'NCL'"), verbose_name=_("Code")) description = models.TextField(blank=True, verbose_name=_("Description")) description_vi = models.TextField(blank=True, verbose_name=_("Vietnamese Description")) logo = models.ImageField(upload_to='cruise_lines/', blank=True, null=True, verbose_name=_("Logo")) website = models.URLField(blank=True, verbose_name=_("Website")) star_rating = models.DecimalField(max_digits=2, decimal_places=1, default=3.0, help_text=_("1-5 star rating"), verbose_name=_("Star Rating")) is_active = models.BooleanField(default=True, verbose_name=_("Is Active")) owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cruise_lines', verbose_name=_("Owner")) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
class Meta: ordering = ['name'] verbose_name = _("Cruise Line") verbose_name_plural = _("Cruise Lines")
def __str__(self): return self.name
@property def ship_count(self): return self.ships.filter(is_active=True).count()
|
Cruise Line Cruise |
class Cruise(models.Model): """Specific cruise departures""" STATUS_CHOICES = [ ('active', 'Active'), ('sold_out', 'Sold Out'), ('cancelled', 'Cancelled'), ('completed', 'Completed'), ]
DEAL_TYPES = [ ('early_bird', 'Early Bird'), ('last_minute', 'Last Minute'), ('group', 'Group Discount'), ('senior', 'Senior Discount'), ('military', 'Military Discount'), ('repeat', 'Repeat Guest'), ('two_for_one', 'Two for One'), ]
ship = models.ForeignKey(Ship, on_delete=models.CASCADE, related_name='cruises') itinerary = models.ForeignKey(CruiseItinerary, on_delete=models.CASCADE, related_name='cruises') departure_date = models.DateField() return_date = models.DateField() status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='active')
# Pricing and deals is_deal = models.BooleanField(default=False) deal_type = models.CharField(max_length=15, choices=DEAL_TYPES, blank=True) deal_discount_percent = models.DecimalField(max_digits=5, decimal_places=2, default=0) deal_description = models.CharField(max_length=200, blank=True) deal_description_vi = models.CharField(max_length=200, blank=True) deal_expires = models.DateTimeField(null=True, blank=True)
# Additional offerings airfare_included = models.BooleanField(default=False) shore_excursions_included = models.BooleanField(default=False) wifi_included = models.BooleanField(default=False) drinks_package_included = models.BooleanField(default=False) gratuities_included = models.BooleanField(default=False)
# Internal tracking booking_deadline = models.DateField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: verbose_name = _("Cruise") verbose_name_plural = _("Cruises") ordering = ['departure_date']
def __str__(self): return f"{self.ship.name} - {self.departure_date}"
@property def days_until_departure(self): if self.departure_date is None: return None from datetime import date return (self.departure_date - date.today()).days
@property def is_last_minute(self): days = self.days_until_departure if days is None: return False return days <= 90
def get_absolute_url(self): return reverse('cruise:detail', kwargs={'pk': self.pk})
|
Cruise Line Cruise Image |
class CruiseImage(models.Model): """Images for cruises, ships, and itineraries""" IMAGE_TYPES = [ ('ship_exterior', 'Ship Exterior'), ('ship_interior', 'Ship Interior'), ('cabin', 'Cabin'), ('dining', 'Dining'), ('entertainment', 'Entertainment'), ('destination', 'Destination'), ('amenity', 'Amenity'), ]
cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='images', null=True, blank=True) ship = models.ForeignKey(Ship, on_delete=models.CASCADE, related_name='images', null=True, blank=True) image = models.ImageField(upload_to='cruise_images/') image_type = models.CharField(max_length=20, choices=IMAGE_TYPES) caption = models.CharField(max_length=200, blank=True) caption_vi = models.CharField(max_length=200, blank=True) is_featured = models.BooleanField(default=False) order = models.PositiveIntegerField(default=0)
class Meta: ordering = ['order']
def __str__(self): if self.cruise: return f"{self.cruise} - {self.image_type}" elif self.ship: return f"{self.ship} - {self.image_type}" return f"Image {self.pk}"
|
Cruise Line Cruise itinerary |
class CruiseItinerary(models.Model): """Cruise routes and destinations""" ITINERARY_TYPES = [ ('round_trip', 'Round Trip'), ('one_way', 'One Way'), ('open_jaw', 'Open Jaw'), ]
name = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True) cruise_line = models.ForeignKey(CruiseLine, on_delete=models.CASCADE, related_name='itineraries') duration_days = models.PositiveIntegerField() itinerary_type = models.CharField(max_length=15, choices=ITINERARY_TYPES, default='round_trip') departure_port = models.ForeignKey(Location, on_delete=models.CASCADE, related_name='departure_cruises') arrival_port = models.ForeignKey(Location, on_delete=models.CASCADE, related_name='arrival_cruises') description = models.TextField(blank=True) description_vi = models.TextField(blank=True) highlights = models.TextField(blank=True, help_text="Key highlights of this itinerary") highlights_vi = models.TextField(blank=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: ordering = ['duration_days', 'name'] verbose_name_plural = 'Cruise Itineraries'
def __str__(self): return f"{self.duration_days}-day {self.name}"
@property def port_count(self): return self.ports.count()
|
Cruise Line Cruise itinerary port |
class ItineraryPort(models.Model): """Ports visited during a cruise itinerary""" itinerary = models.ForeignKey(CruiseItinerary, on_delete=models.CASCADE, related_name='ports') port = models.ForeignKey(Location, on_delete=models.CASCADE, related_name='itinerary_visits') day_number = models.PositiveIntegerField() arrival_time = models.TimeField(null=True, blank=True) departure_time = models.TimeField(null=True, blank=True) is_sea_day = models.BooleanField(default=False) notes = models.TextField(blank=True, help_text="Special notes about this port visit") notes_vi = models.TextField(blank=True)
class Meta: ordering = ['itinerary', 'day_number'] constraints = [ models.UniqueConstraint(fields=['itinerary', 'day_number'], name='unique_itinerary_day') ]
def __str__(self): if self.is_sea_day: return f"Day {self.day_number}: Sea Day" return f"Day {self.day_number}: {self.port.name}"
|
Cruise Line Cruise Price |
class CruisePrice(models.Model): """Pricing for different cabin categories on specific cruises""" cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='prices') cabin_category = models.ForeignKey(CabinCategory, on_delete=models.CASCADE) price_per_person = models.DecimalField(max_digits=10, decimal_places=2) single_supplement = models.DecimalField(max_digits=10, decimal_places=2, default=0) third_guest_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) fourth_guest_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
# Availability total_cabins = models.PositiveIntegerField(default=0) available_cabins = models.PositiveIntegerField(default=0)
# Taxes and fees port_charges = models.DecimalField(max_digits=8, decimal_places=2, default=0) government_fees = models.DecimalField(max_digits=8, decimal_places=2, default=0)
created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: constraints = [ models.UniqueConstraint(fields=['cruise', 'cabin_category'], name='unique_cruise_cabin_category') ] ordering = ['cruise', 'cabin_category__cabin_type']
def __str__(self): return f"{self.cruise} - {self.cabin_category.name}: ${self.price_per_person}"
@property def total_price_per_person(self): price = self.price_per_person or 0 port_charges = self.port_charges or 0 government_fees = self.government_fees or 0 return price + port_charges + government_fees
@property def discounted_price(self): price = self.price_per_person or 0 if self.cruise.is_deal and self.cruise.deal_discount_percent > 0: discount = (price * self.cruise.deal_discount_percent) / 100 return price - discount return price
@property def savings_amount(self): if self.cruise.is_deal: price = self.price_per_person or 0 return price - self.discounted_price return Decimal('0.00')
@property def is_available(self): return self.available_cabins > 0
|
Cruise Line Cruise Review |
class CruiseReview(models.Model): """Customer reviews for cruises""" RATING_CHOICES = [(i, str(i)) for i in range(1, 6)]
cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='reviews') user = models.ForeignKey(User, on_delete=models.CASCADE) overall_rating = models.IntegerField(choices=RATING_CHOICES) ship_rating = models.IntegerField(choices=RATING_CHOICES) service_rating = models.IntegerField(choices=RATING_CHOICES) dining_rating = models.IntegerField(choices=RATING_CHOICES) entertainment_rating = models.IntegerField(choices=RATING_CHOICES) value_rating = models.IntegerField(choices=RATING_CHOICES)
title = models.CharField(max_length=200) review_text = models.TextField() pros = models.TextField(blank=True) cons = models.TextField(blank=True)
travel_date = models.DateField() cabin_category = models.ForeignKey(CabinCategory, on_delete=models.SET_NULL, null=True, blank=True) traveler_type = models.CharField(max_length=50, blank=True, help_text="e.g., Couple, Family, Solo")
is_verified = models.BooleanField(default=False) helpful_votes = models.PositiveIntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: ordering = ['-created_at'] constraints = [ models.UniqueConstraint(fields=['cruise', 'user'], name='unique_cruise_user_review') ]
def __str__(self): return f"{self.cruise} - {self.title} ({self.overall_rating}/5)"
|
Cruise Line Cruise Wishlist |
class CruiseWishlist(models.Model): """User wishlist for cruises""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cruise_wishlist') cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='wishlisted_by') created_at = models.DateTimeField(auto_now_add=True)
class Meta: constraints = [ models.UniqueConstraint(fields=['user', 'cruise'], name='unique_user_cruise_wishlist') ] ordering = ['-created_at']
def __str__(self): return f"{self.user.username} - {self.cruise}"
|
Cruise Line ship |
class Ship(models.Model): """Individual cruise ships""" SHIP_CATEGORIES = [ ('luxury', 'Luxury'), ('premium', 'Premium'), ('contemporary', 'Contemporary'), ('budget', 'Budget'), ('expedition', 'Expedition'), ('river', 'River Cruise'), ]
name = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True) cruise_line = models.ForeignKey(CruiseLine, on_delete=models.CASCADE, related_name='ships') category = models.CharField(max_length=20, choices=SHIP_CATEGORIES, default='contemporary') year_built = models.PositiveIntegerField() year_refurbished = models.PositiveIntegerField(null=True, blank=True) capacity = models.PositiveIntegerField(help_text="Maximum passenger capacity") crew_size = models.PositiveIntegerField(help_text="Number of crew members") gross_tonnage = models.PositiveIntegerField() length = models.DecimalField(max_digits=6, decimal_places=2, help_text="Length in meters") beam = models.DecimalField(max_digits=5, decimal_places=2, help_text="Width in meters") decks = models.PositiveIntegerField() description = models.TextField(blank=True) description_vi = models.TextField(blank=True) main_image = models.ImageField(upload_to='ships/', blank=True, null=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: verbose_name = _("Cruise Ship") verbose_name_plural = _("Cruise Ships") ordering = ['cruise_line__name', 'name']
def __str__(self): return f"{self.cruise_line.name} - {self.name}"
@property def passenger_to_crew_ratio(self): if self.crew_size and self.capacity and self.crew_size > 0: return round(self.capacity / self.crew_size, 1) return 0
|
Ship Amenity |
class ShipAmenity(models.Model): """Ship facilities and amenities""" AMENITY_CATEGORIES = [ ('dining', 'Dining'), ('entertainment', 'Entertainment'), ('activities', 'Activities'), ('spa', 'Spa & Wellness'), ('sports', 'Sports & Recreation'), ('kids', 'Kids & Family'), ('shopping', 'Shopping'), ('accessibility', 'Accessibility'), ]
name = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True) category = models.CharField(max_length=20, choices=AMENITY_CATEGORIES) description = models.TextField(blank=True) description_vi = models.TextField(blank=True) icon = models.ImageField(upload_to='amenities/', blank=True, null=True) ships = models.ManyToManyField(Ship, related_name='amenities', blank=True)
class Meta: ordering = ['category', 'name'] verbose_name_plural = 'Ship Amenities'
def __str__(self): return self.name
|
Ship Camin |
class CabinCategory(models.Model): """Different types of cabins available on ships""" CABIN_TYPES = [ ('interior', 'Interior'), ('ocean_view', 'Ocean View'), ('balcony', 'Balcony'), ('suite', 'Suite'), ]
ship = models.ForeignKey(Ship, on_delete=models.CASCADE, related_name='cabin_categories') name = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True) cabin_type = models.CharField(max_length=15, choices=CABIN_TYPES) deck_location = models.CharField(max_length=100, blank=True, help_text="Which decks this category is on") size_sqft = models.PositiveIntegerField(help_text="Size in square feet") max_occupancy = models.PositiveIntegerField(default=2) description = models.TextField(blank=True) description_vi = models.TextField(blank=True) amenities = models.TextField(blank=True, help_text="List of cabin amenities") amenities_vi = models.TextField(blank=True) image = models.ImageField(upload_to='cabins/', blank=True, null=True) is_active = models.BooleanField(default=True)
class Meta: ordering = ['ship', 'cabin_type', 'name'] verbose_name_plural = 'Cabin Categories'
def __str__(self): return f"{self.ship.name} - {self.name}"
|
Nhom |
Notes |
|
from django.db import models from django.urls import reverse from django.contrib.auth import get_user_model from django.utils.translation import gettext_lazy as _ from decimal import Decimal from datetime import datetime, timedelta from apps.geo.models import Location
User = get_user_model()
|
Booking |
class CruiseBooking(models.Model): """Cruise bookings made through the platform""" STATUS_CHOICES = [ ('pending', 'Pending'), ('confirmed', 'Confirmed'), ('cancelled', 'Cancelled'), ('completed', 'Completed'), ]
booking_number = models.CharField(max_length=20, unique=True) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cruise_bookings') cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='bookings') cabin_category = models.ForeignKey(CabinCategory, on_delete=models.CASCADE)
# Guest information primary_guest_name = models.CharField(max_length=200) primary_guest_email = models.EmailField() primary_guest_phone = models.CharField(max_length=20) number_of_guests = models.PositiveIntegerField(default=2) special_requests = models.TextField(blank=True)
# Pricing base_price = models.DecimalField(max_digits=10, decimal_places=2) taxes_and_fees = models.DecimalField(max_digits=10, decimal_places=2) total_price = models.DecimalField(max_digits=10, decimal_places=2) commission_rate = models.DecimalField(max_digits=5, decimal_places=2, default=10) commission_amount = models.DecimalField(max_digits=10, decimal_places=2)
# Status and dates status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='pending') booking_date = models.DateTimeField(auto_now_add=True) confirmation_date = models.DateTimeField(null=True, blank=True) cancellation_date = models.DateTimeField(null=True, blank=True)
# Payment tracking deposit_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0) deposit_paid = models.BooleanField(default=False) final_payment_due = models.DateField(null=True, blank=True) final_payment_made = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: ordering = ['-created_at']
def save(self, *args, **kwargs): if not self.booking_number: # Generate unique booking number import uuid self.booking_number = f"CR{uuid.uuid4().hex[:8].upper()}" super().save(*args, **kwargs)
def __str__(self): return f"Booking {self.booking_number} - {self.cruise}"
|
Cruise Line |
class CruiseLine(models.Model): """Major cruise companies like Royal Caribbean, Norwegian, etc.""" name = models.CharField(max_length=100, verbose_name=_("Name")) name_vi = models.CharField(max_length=100, blank=True, verbose_name=_("Vietnamese Name")) code = models.CharField(max_length=10, unique=True, help_text=_("Short code like 'RCL', 'NCL'"), verbose_name=_("Code")) description = models.TextField(blank=True, verbose_name=_("Description")) description_vi = models.TextField(blank=True, verbose_name=_("Vietnamese Description")) logo = models.ImageField(upload_to='cruise_lines/', blank=True, null=True, verbose_name=_("Logo")) website = models.URLField(blank=True, verbose_name=_("Website")) star_rating = models.DecimalField(max_digits=2, decimal_places=1, default=3.0, help_text=_("1-5 star rating"), verbose_name=_("Star Rating")) is_active = models.BooleanField(default=True, verbose_name=_("Is Active")) owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cruise_lines', verbose_name=_("Owner")) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
class Meta: ordering = ['name'] verbose_name = _("Cruise Line") verbose_name_plural = _("Cruise Lines")
def __str__(self): return self.name
@property def ship_count(self): return self.ships.filter(is_active=True).count()
|
Cruise Line Cruise |
class Cruise(models.Model): """Specific cruise departures""" STATUS_CHOICES = [ ('active', 'Active'), ('sold_out', 'Sold Out'), ('cancelled', 'Cancelled'), ('completed', 'Completed'), ]
DEAL_TYPES = [ ('early_bird', 'Early Bird'), ('last_minute', 'Last Minute'), ('group', 'Group Discount'), ('senior', 'Senior Discount'), ('military', 'Military Discount'), ('repeat', 'Repeat Guest'), ('two_for_one', 'Two for One'), ]
ship = models.ForeignKey(Ship, on_delete=models.CASCADE, related_name='cruises') itinerary = models.ForeignKey(CruiseItinerary, on_delete=models.CASCADE, related_name='cruises') departure_date = models.DateField() return_date = models.DateField() status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='active')
# Pricing and deals is_deal = models.BooleanField(default=False) deal_type = models.CharField(max_length=15, choices=DEAL_TYPES, blank=True) deal_discount_percent = models.DecimalField(max_digits=5, decimal_places=2, default=0) deal_description = models.CharField(max_length=200, blank=True) deal_description_vi = models.CharField(max_length=200, blank=True) deal_expires = models.DateTimeField(null=True, blank=True)
# Additional offerings airfare_included = models.BooleanField(default=False) shore_excursions_included = models.BooleanField(default=False) wifi_included = models.BooleanField(default=False) drinks_package_included = models.BooleanField(default=False) gratuities_included = models.BooleanField(default=False)
# Internal tracking booking_deadline = models.DateField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: verbose_name = _("Cruise") verbose_name_plural = _("Cruises") ordering = ['departure_date']
def __str__(self): return f"{self.ship.name} - {self.departure_date}"
@property def days_until_departure(self): if self.departure_date is None: return None from datetime import date return (self.departure_date - date.today()).days
@property def is_last_minute(self): days = self.days_until_departure if days is None: return False return days <= 90
def get_absolute_url(self): return reverse('cruise:detail', kwargs={'pk': self.pk})
|
Cruise Line Cruise Image |
class CruiseImage(models.Model): """Images for cruises, ships, and itineraries""" IMAGE_TYPES = [ ('ship_exterior', 'Ship Exterior'), ('ship_interior', 'Ship Interior'), ('cabin', 'Cabin'), ('dining', 'Dining'), ('entertainment', 'Entertainment'), ('destination', 'Destination'), ('amenity', 'Amenity'), ]
cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='images', null=True, blank=True) ship = models.ForeignKey(Ship, on_delete=models.CASCADE, related_name='images', null=True, blank=True) image = models.ImageField(upload_to='cruise_images/') image_type = models.CharField(max_length=20, choices=IMAGE_TYPES) caption = models.CharField(max_length=200, blank=True) caption_vi = models.CharField(max_length=200, blank=True) is_featured = models.BooleanField(default=False) order = models.PositiveIntegerField(default=0)
class Meta: ordering = ['order']
def __str__(self): if self.cruise: return f"{self.cruise} - {self.image_type}" elif self.ship: return f"{self.ship} - {self.image_type}" return f"Image {self.pk}"
|
Cruise Line Cruise itinerary |
class CruiseItinerary(models.Model): """Cruise routes and destinations""" ITINERARY_TYPES = [ ('round_trip', 'Round Trip'), ('one_way', 'One Way'), ('open_jaw', 'Open Jaw'), ]
name = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True) cruise_line = models.ForeignKey(CruiseLine, on_delete=models.CASCADE, related_name='itineraries') duration_days = models.PositiveIntegerField() itinerary_type = models.CharField(max_length=15, choices=ITINERARY_TYPES, default='round_trip') departure_port = models.ForeignKey(Location, on_delete=models.CASCADE, related_name='departure_cruises') arrival_port = models.ForeignKey(Location, on_delete=models.CASCADE, related_name='arrival_cruises') description = models.TextField(blank=True) description_vi = models.TextField(blank=True) highlights = models.TextField(blank=True, help_text="Key highlights of this itinerary") highlights_vi = models.TextField(blank=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: ordering = ['duration_days', 'name'] verbose_name_plural = 'Cruise Itineraries'
def __str__(self): return f"{self.duration_days}-day {self.name}"
@property def port_count(self): return self.ports.count()
|
Cruise Line Cruise itinerary port |
class ItineraryPort(models.Model): """Ports visited during a cruise itinerary""" itinerary = models.ForeignKey(CruiseItinerary, on_delete=models.CASCADE, related_name='ports') port = models.ForeignKey(Location, on_delete=models.CASCADE, related_name='itinerary_visits') day_number = models.PositiveIntegerField() arrival_time = models.TimeField(null=True, blank=True) departure_time = models.TimeField(null=True, blank=True) is_sea_day = models.BooleanField(default=False) notes = models.TextField(blank=True, help_text="Special notes about this port visit") notes_vi = models.TextField(blank=True)
class Meta: ordering = ['itinerary', 'day_number'] constraints = [ models.UniqueConstraint(fields=['itinerary', 'day_number'], name='unique_itinerary_day') ]
def __str__(self): if self.is_sea_day: return f"Day {self.day_number}: Sea Day" return f"Day {self.day_number}: {self.port.name}"
|
Cruise Line Cruise Price |
class CruisePrice(models.Model): """Pricing for different cabin categories on specific cruises""" cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='prices') cabin_category = models.ForeignKey(CabinCategory, on_delete=models.CASCADE) price_per_person = models.DecimalField(max_digits=10, decimal_places=2) single_supplement = models.DecimalField(max_digits=10, decimal_places=2, default=0) third_guest_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) fourth_guest_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
# Availability total_cabins = models.PositiveIntegerField(default=0) available_cabins = models.PositiveIntegerField(default=0)
# Taxes and fees port_charges = models.DecimalField(max_digits=8, decimal_places=2, default=0) government_fees = models.DecimalField(max_digits=8, decimal_places=2, default=0)
created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: constraints = [ models.UniqueConstraint(fields=['cruise', 'cabin_category'], name='unique_cruise_cabin_category') ] ordering = ['cruise', 'cabin_category__cabin_type']
def __str__(self): return f"{self.cruise} - {self.cabin_category.name}: ${self.price_per_person}"
@property def total_price_per_person(self): price = self.price_per_person or 0 port_charges = self.port_charges or 0 government_fees = self.government_fees or 0 return price + port_charges + government_fees
@property def discounted_price(self): price = self.price_per_person or 0 if self.cruise.is_deal and self.cruise.deal_discount_percent > 0: discount = (price * self.cruise.deal_discount_percent) / 100 return price - discount return price
@property def savings_amount(self): if self.cruise.is_deal: price = self.price_per_person or 0 return price - self.discounted_price return Decimal('0.00')
@property def is_available(self): return self.available_cabins > 0
|
Cruise Line Cruise Review |
class CruiseReview(models.Model): """Customer reviews for cruises""" RATING_CHOICES = [(i, str(i)) for i in range(1, 6)]
cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='reviews') user = models.ForeignKey(User, on_delete=models.CASCADE) overall_rating = models.IntegerField(choices=RATING_CHOICES) ship_rating = models.IntegerField(choices=RATING_CHOICES) service_rating = models.IntegerField(choices=RATING_CHOICES) dining_rating = models.IntegerField(choices=RATING_CHOICES) entertainment_rating = models.IntegerField(choices=RATING_CHOICES) value_rating = models.IntegerField(choices=RATING_CHOICES)
title = models.CharField(max_length=200) review_text = models.TextField() pros = models.TextField(blank=True) cons = models.TextField(blank=True)
travel_date = models.DateField() cabin_category = models.ForeignKey(CabinCategory, on_delete=models.SET_NULL, null=True, blank=True) traveler_type = models.CharField(max_length=50, blank=True, help_text="e.g., Couple, Family, Solo")
is_verified = models.BooleanField(default=False) helpful_votes = models.PositiveIntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: ordering = ['-created_at'] constraints = [ models.UniqueConstraint(fields=['cruise', 'user'], name='unique_cruise_user_review') ]
def __str__(self): return f"{self.cruise} - {self.title} ({self.overall_rating}/5)"
|
Cruise Line Cruise Wishlist |
class CruiseWishlist(models.Model): """User wishlist for cruises""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cruise_wishlist') cruise = models.ForeignKey(Cruise, on_delete=models.CASCADE, related_name='wishlisted_by') created_at = models.DateTimeField(auto_now_add=True)
class Meta: constraints = [ models.UniqueConstraint(fields=['user', 'cruise'], name='unique_user_cruise_wishlist') ] ordering = ['-created_at']
def __str__(self): return f"{self.user.username} - {self.cruise}"
|
Cruise Line ship |
class Ship(models.Model): """Individual cruise ships""" SHIP_CATEGORIES = [ ('luxury', 'Luxury'), ('premium', 'Premium'), ('contemporary', 'Contemporary'), ('budget', 'Budget'), ('expedition', 'Expedition'), ('river', 'River Cruise'), ]
name = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True) cruise_line = models.ForeignKey(CruiseLine, on_delete=models.CASCADE, related_name='ships') category = models.CharField(max_length=20, choices=SHIP_CATEGORIES, default='contemporary') year_built = models.PositiveIntegerField() year_refurbished = models.PositiveIntegerField(null=True, blank=True) capacity = models.PositiveIntegerField(help_text="Maximum passenger capacity") crew_size = models.PositiveIntegerField(help_text="Number of crew members") gross_tonnage = models.PositiveIntegerField() length = models.DecimalField(max_digits=6, decimal_places=2, help_text="Length in meters") beam = models.DecimalField(max_digits=5, decimal_places=2, help_text="Width in meters") decks = models.PositiveIntegerField() description = models.TextField(blank=True) description_vi = models.TextField(blank=True) main_image = models.ImageField(upload_to='ships/', blank=True, null=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: verbose_name = _("Cruise Ship") verbose_name_plural = _("Cruise Ships") ordering = ['cruise_line__name', 'name']
def __str__(self): return f"{self.cruise_line.name} - {self.name}"
@property def passenger_to_crew_ratio(self): if self.crew_size and self.capacity and self.crew_size > 0: return round(self.capacity / self.crew_size, 1) return 0
|
Ship Amenity |
class ShipAmenity(models.Model): """Ship facilities and amenities""" AMENITY_CATEGORIES = [ ('dining', 'Dining'), ('entertainment', 'Entertainment'), ('activities', 'Activities'), ('spa', 'Spa & Wellness'), ('sports', 'Sports & Recreation'), ('kids', 'Kids & Family'), ('shopping', 'Shopping'), ('accessibility', 'Accessibility'), ]
name = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True) category = models.CharField(max_length=20, choices=AMENITY_CATEGORIES) description = models.TextField(blank=True) description_vi = models.TextField(blank=True) icon = models.ImageField(upload_to='amenities/', blank=True, null=True) ships = models.ManyToManyField(Ship, related_name='amenities', blank=True)
class Meta: ordering = ['category', 'name'] verbose_name_plural = 'Ship Amenities'
def __str__(self): return self.name
|
Ship Camin |
class CabinCategory(models.Model): """Different types of cabins available on ships""" CABIN_TYPES = [ ('interior', 'Interior'), ('ocean_view', 'Ocean View'), ('balcony', 'Balcony'), ('suite', 'Suite'), ]
ship = models.ForeignKey(Ship, on_delete=models.CASCADE, related_name='cabin_categories') name = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True) cabin_type = models.CharField(max_length=15, choices=CABIN_TYPES) deck_location = models.CharField(max_length=100, blank=True, help_text="Which decks this category is on") size_sqft = models.PositiveIntegerField(help_text="Size in square feet") max_occupancy = models.PositiveIntegerField(default=2) description = models.TextField(blank=True) description_vi = models.TextField(blank=True) amenities = models.TextField(blank=True, help_text="List of cabin amenities") amenities_vi = models.TextField(blank=True) image = models.ImageField(upload_to='cabins/', blank=True, null=True) is_active = models.BooleanField(default=True)
class Meta: ordering = ['ship', 'cabin_type', 'name'] verbose_name_plural = 'Cabin Categories'
def __str__(self): return f"{self.ship.name} - {self.name}"
|
Attached Files
You are viewing this article in public mode. Some features may be limited.