Destination Models
Destination Models (English fallback)
Aug. 17, 2025
Posted by admin
Destination Models
Nhom |
Notes |
|
|
|
from django.db import models from django.urls import reverse from django.contrib.auth.models import User from django.core.validators import MinValueValidator, MaxValueValidator from django.utils.translation import gettext_lazy as _ from decimal import Decimal from apps.geo.models import Location
|
1 |
class Country(models.Model): """Comprehensive country-level information for travel planning"""
# ================================ # 🏳️ COUNTRY OVERVIEW # ================================ name_en = models.CharField(max_length=100, unique=True, verbose_name="Country Name (English)") name_vi = models.CharField(max_length=100, blank=True, verbose_name="Country Name (Vietnamese)") official_name_en = models.CharField(max_length=150, blank=True, verbose_name="Official Name (English)") official_name_vi = models.CharField(max_length=150, blank=True, verbose_name="Official Name (Vietnamese)") country_code = models.CharField(max_length=3, unique=True, help_text="ISO 3166-1 alpha-3 code") continent = models.CharField(max_length=50) flag_image = models.ImageField(upload_to='countries/flags/', blank=True, null=True)
# Basic geographic info capital_en = models.CharField(max_length=100, blank=True, verbose_name="Capital (English)") capital_vi = models.CharField(max_length=100, blank=True, verbose_name="Capital (Vietnamese)") major_cities_en = models.TextField(blank=True, verbose_name="Major Cities (English)", help_text="Comma-separated list") major_cities_vi = models.TextField(blank=True, verbose_name="Major Cities (Vietnamese)", help_text="Comma-separated list") population = models.BigIntegerField(blank=True, null=True) area_km2 = models.BigIntegerField(blank=True, null=True, help_text="Area in square kilometers") gdp_per_capita = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
# Languages and communication official_languages_en = models.CharField(max_length=200, blank=True, verbose_name="Official Languages (English)") official_languages_vi = models.CharField(max_length=200, blank=True, verbose_name="Official Languages (Vietnamese)") spoken_languages_en = models.CharField(max_length=300, blank=True, verbose_name="Commonly Spoken Languages (English)") spoken_languages_vi = models.CharField(max_length=300, blank=True, verbose_name="Commonly Spoken Languages (Vietnamese)")
# Time and location time_zones = models.CharField(max_length=200, blank=True, verbose_name="Time Zones") calling_code = models.CharField(max_length=10, blank=True, verbose_name="Country Calling Code") internet_domain = models.CharField(max_length=10, blank=True, verbose_name="Internet Domain")
# Infrastructure basics driving_side = models.CharField(max_length=10, choices=[('left', 'Left'), ('right', 'Right')], blank=True) power_plugs = models.CharField(max_length=50, blank=True, help_text="e.g., Type A, B, C") voltage = models.CharField(max_length=20, blank=True, help_text="e.g., 110V, 220V") frequency = models.CharField(max_length=10, blank=True, help_text="e.g., 50Hz, 60Hz")
# ================================ # 💰 CURRENCY & FINANCIAL INFO # ================================ currency_name_en = models.CharField(max_length=50, blank=True, verbose_name="Currency Name (English)") currency_name_vi = models.CharField(max_length=50, blank=True, verbose_name="Currency Name (Vietnamese)") currency_code = models.CharField(max_length=10, blank=True, verbose_name="Currency Code (ISO)") currency_symbol = models.CharField(max_length=10, blank=True, verbose_name="Currency Symbol") currency_subdivisions_en = models.CharField(max_length=100, blank=True, verbose_name="Currency Subdivisions (English)") currency_subdivisions_vi = models.CharField(max_length=100, blank=True, verbose_name="Currency Subdivisions (Vietnamese)")
# Money exchange and usage usd_accepted = models.BooleanField(default=False, verbose_name="USD Widely Accepted") euro_accepted = models.BooleanField(default=False, verbose_name="EUR Widely Accepted") exchange_info_en = models.TextField(blank=True, verbose_name="Money Exchange Info (English)") exchange_info_vi = models.TextField(blank=True, verbose_name="Money Exchange Info (Vietnamese)") atm_availability_en = models.TextField(blank=True, verbose_name="ATM Availability (English)") atm_availability_vi = models.TextField(blank=True, verbose_name="ATM Availability (Vietnamese)") credit_cards_accepted = models.BooleanField(default=True, verbose_name="Credit Cards Widely Accepted")
# Tipping and costs tipping_culture_en = models.TextField(blank=True, verbose_name="Tipping Culture (English)") tipping_culture_vi = models.TextField(blank=True, verbose_name="Tipping Culture (Vietnamese)") daily_budget_backpacker = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True, verbose_name="Daily Budget - Backpacker (USD)") daily_budget_mid_range = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True, verbose_name="Daily Budget - Mid-range (USD)") daily_budget_luxury = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True, verbose_name="Daily Budget - Luxury (USD)")
# ================================ # 📜 HISTORY & CULTURE # ================================ historical_background_en = models.TextField(blank=True, verbose_name="Historical Background (English)") historical_background_vi = models.TextField(blank=True, verbose_name="Historical Background (Vietnamese)") cultural_highlights_en = models.TextField(blank=True, verbose_name="Cultural Highlights (English)") cultural_highlights_vi = models.TextField(blank=True, verbose_name="Cultural Highlights (Vietnamese)")
# Religion and festivals major_religions_en = models.CharField(max_length=200, blank=True, verbose_name="Major Religions (English)") major_religions_vi = models.CharField(max_length=200, blank=True, verbose_name="Major Religions (Vietnamese)") festivals_holidays_en = models.TextField(blank=True, verbose_name="Major Festivals & Holidays (English)") festivals_holidays_vi = models.TextField(blank=True, verbose_name="Major Festivals & Holidays (Vietnamese)")
# Social customs customs_traditions_en = models.TextField(blank=True, verbose_name="Customs & Traditions (English)") customs_traditions_vi = models.TextField(blank=True, verbose_name="Customs & Traditions (Vietnamese)") etiquette_tips_en = models.TextField(blank=True, verbose_name="Etiquette Tips (English)") etiquette_tips_vi = models.TextField(blank=True, verbose_name="Etiquette Tips (Vietnamese)") cultural_taboos_en = models.TextField(blank=True, verbose_name="Cultural Taboos (English)") cultural_taboos_vi = models.TextField(blank=True, verbose_name="Cultural Taboos (Vietnamese)")
# ================================ # ✈️ ENTRY & VISA REQUIREMENTS # ================================ visa_requirements_vietnamese_en = models.TextField(blank=True, verbose_name="Visa for Vietnamese Citizens (English)") visa_requirements_vietnamese_vi = models.TextField(blank=True, verbose_name="Visa for Vietnamese Citizens (Vietnamese)") visa_requirements_us_en = models.TextField(blank=True, verbose_name="Visa for US Citizens (English)") visa_requirements_us_vi = models.TextField(blank=True, verbose_name="Visa for US Citizens (Vietnamese)") visa_requirements_eu_en = models.TextField(blank=True, verbose_name="Visa for EU Citizens (English)") visa_requirements_eu_vi = models.TextField(blank=True, verbose_name="Visa for EU Citizens (Vietnamese)") visa_requirements_general_en = models.TextField(blank=True, verbose_name="General Visa Requirements (English)") visa_requirements_general_vi = models.TextField(blank=True, verbose_name="General Visa Requirements (Vietnamese)")
# Stay duration and documents max_stay_duration = models.CharField(max_length=100, blank=True, verbose_name="Maximum Stay Duration") required_documents_en = models.TextField(blank=True, verbose_name="Required Documents (English)") required_documents_vi = models.TextField(blank=True, verbose_name="Required Documents (Vietnamese)") vaccination_requirements_en = models.TextField(blank=True, verbose_name="Vaccination Requirements (English)") vaccination_requirements_vi = models.TextField(blank=True, verbose_name="Vaccination Requirements (Vietnamese)")
# Customs and entry customs_duty_free_en = models.TextField(blank=True, verbose_name="Duty-Free Allowances (English)") customs_duty_free_vi = models.TextField(blank=True, verbose_name="Duty-Free Allowances (Vietnamese)") customs_restrictions_en = models.TextField(blank=True, verbose_name="Customs Restrictions (English)") customs_restrictions_vi = models.TextField(blank=True, verbose_name="Customs Restrictions (Vietnamese)") entry_requirements_en = models.TextField(blank=True, verbose_name="Entry Requirements (English)") entry_requirements_vi = models.TextField(blank=True, verbose_name="Entry Requirements (Vietnamese)")
# ================================ # 📱 TECHNOLOGY & CONNECTIVITY # ================================ mobile_carriers_en = models.TextField(blank=True, verbose_name="Mobile Carriers (English)") mobile_carriers_vi = models.TextField(blank=True, verbose_name="Mobile Carriers (Vietnamese)") sim_card_info_en = models.TextField(blank=True, verbose_name="SIM Card Information (English)") sim_card_info_vi = models.TextField(blank=True, verbose_name="SIM Card Information (Vietnamese)") esim_available = models.BooleanField(default=False, verbose_name="eSIM Available") esim_providers_en = models.TextField(blank=True, verbose_name="eSIM Providers (English)") esim_providers_vi = models.TextField(blank=True, verbose_name="eSIM Providers (Vietnamese)")
# Internet and connectivity internet_coverage_en = models.TextField(blank=True, verbose_name="Internet Coverage (English)") internet_coverage_vi = models.TextField(blank=True, verbose_name="Internet Coverage (Vietnamese)") wifi_availability_en = models.TextField(blank=True, verbose_name="WiFi Availability (English)") wifi_availability_vi = models.TextField(blank=True, verbose_name="WiFi Availability (Vietnamese)") internet_speed_quality = models.CharField(max_length=20, choices=[ ('excellent', 'Excellent'), ('good', 'Good'), ('moderate', 'Moderate'), ('poor', 'Poor') ], blank=True, verbose_name="Internet Speed Quality")
# Useful apps maps_apps_en = models.TextField(blank=True, verbose_name="Recommended Maps Apps (English)") maps_apps_vi = models.TextField(blank=True, verbose_name="Recommended Maps Apps (Vietnamese)") transport_apps_en = models.TextField(blank=True, verbose_name="Transport Apps (English)") transport_apps_vi = models.TextField(blank=True, verbose_name="Transport Apps (Vietnamese)") translation_apps_en = models.TextField(blank=True, verbose_name="Translation Apps (English)") translation_apps_vi = models.TextField(blank=True, verbose_name="Translation Apps (Vietnamese)") food_delivery_apps_en = models.TextField(blank=True, verbose_name="Food Delivery Apps (English)") food_delivery_apps_vi = models.TextField(blank=True, verbose_name="Food Delivery Apps (Vietnamese)") other_useful_apps_en = models.TextField(blank=True, verbose_name="Other Useful Apps (English)") other_useful_apps_vi = models.TextField(blank=True, verbose_name="Other Useful Apps (Vietnamese)")
# ================================ # 🚌 TRANSPORTATION # ================================ airports_info_en = models.TextField(blank=True, verbose_name="Airport Information (English)") airports_info_vi = models.TextField(blank=True, verbose_name="Airport Information (Vietnamese)") airport_transfer_en = models.TextField(blank=True, verbose_name="Airport Transfer Options (English)") airport_transfer_vi = models.TextField(blank=True, verbose_name="Airport Transfer Options (Vietnamese)")
# Public transportation public_transport_overview_en = models.TextField(blank=True, verbose_name="Public Transport Overview (English)") public_transport_overview_vi = models.TextField(blank=True, verbose_name="Public Transport Overview (Vietnamese)") metro_system_en = models.TextField(blank=True, verbose_name="Metro/Subway System (English)") metro_system_vi = models.TextField(blank=True, verbose_name="Metro/Subway System (Vietnamese)") bus_system_en = models.TextField(blank=True, verbose_name="Bus System (English)") bus_system_vi = models.TextField(blank=True, verbose_name="Bus System (Vietnamese)") taxi_ridehailing_en = models.TextField(blank=True, verbose_name="Taxi & Ride-hailing (English)") taxi_ridehailing_vi = models.TextField(blank=True, verbose_name="Taxi & Ride-hailing (Vietnamese)")
# Vehicle rental and intercity travel car_rental_info_en = models.TextField(blank=True, verbose_name="Car Rental Information (English)") car_rental_info_vi = models.TextField(blank=True, verbose_name="Car Rental Information (Vietnamese)") scooter_rental_info_en = models.TextField(blank=True, verbose_name="Scooter/Bike Rental (English)") scooter_rental_info_vi = models.TextField(blank=True, verbose_name="Scooter/Bike Rental (Vietnamese)") train_system_en = models.TextField(blank=True, verbose_name="Train System (English)") train_system_vi = models.TextField(blank=True, verbose_name="Train System (Vietnamese)") intercity_travel_en = models.TextField(blank=True, verbose_name="Intercity Travel Options (English)") intercity_travel_vi = models.TextField(blank=True, verbose_name="Intercity Travel Options (Vietnamese)")
# ================================ # 🛟 SAFETY & HEALTH # ================================ safety_overview_en = models.TextField(blank=True, verbose_name="Safety Overview (English)") safety_overview_vi = models.TextField(blank=True, verbose_name="Safety Overview (Vietnamese)") safety_rating = models.CharField(max_length=20, choices=[ ('very_safe', 'Very Safe'), ('safe', 'Safe'), ('moderately_safe', 'Moderately Safe'), ('caution_advised', 'Caution Advised'), ('high_risk', 'High Risk') ], blank=True, verbose_name="Safety Rating")
# Common issues and scams common_scams_en = models.TextField(blank=True, verbose_name="Common Scams (English)") common_scams_vi = models.TextField(blank=True, verbose_name="Common Scams (Vietnamese)") safety_tips_en = models.TextField(blank=True, verbose_name="Safety Tips (English)") safety_tips_vi = models.TextField(blank=True, verbose_name="Safety Tips (Vietnamese)")
# Health and medical health_precautions_en = models.TextField(blank=True, verbose_name="Health Precautions (English)") health_precautions_vi = models.TextField(blank=True, verbose_name="Health Precautions (Vietnamese)") medical_facilities_en = models.TextField(blank=True, verbose_name="Medical Facilities (English)") medical_facilities_vi = models.TextField(blank=True, verbose_name="Medical Facilities (Vietnamese)") travel_insurance_tips_en = models.TextField(blank=True, verbose_name="Travel Insurance Tips (English)") travel_insurance_tips_vi = models.TextField(blank=True, verbose_name="Travel Insurance Tips (Vietnamese)")
# Emergency information emergency_numbers = models.TextField(blank=True, help_text="JSON format: {'police': '911', 'fire': '911', 'medical': '911'}") emergency_contacts_en = models.TextField(blank=True, verbose_name="Emergency Contacts (English)") emergency_contacts_vi = models.TextField(blank=True, verbose_name="Emergency Contacts (Vietnamese)")
# ================================ # 🌤️ CLIMATE & PACKING # ================================ climate_overview_en = models.TextField(blank=True, verbose_name="Climate Overview (English)") climate_overview_vi = models.TextField(blank=True, verbose_name="Climate Overview (Vietnamese)") seasons_description_en = models.TextField(blank=True, verbose_name="Seasons Description (English)") seasons_description_vi = models.TextField(blank=True, verbose_name="Seasons Description (Vietnamese)") best_time_to_visit_en = models.CharField(max_length=100, blank=True, verbose_name="Best Time to Visit (English)") best_time_to_visit_vi = models.CharField(max_length=100, blank=True, verbose_name="Best Time to Visit (Vietnamese)")
# Weather patterns rainy_season_en = models.CharField(max_length=100, blank=True, verbose_name="Rainy Season (English)") rainy_season_vi = models.CharField(max_length=100, blank=True, verbose_name="Rainy Season (Vietnamese)") dry_season_en = models.CharField(max_length=100, blank=True, verbose_name="Dry Season (English)") dry_season_vi = models.CharField(max_length=100, blank=True, verbose_name="Dry Season (Vietnamese)") temperature_range = models.CharField(max_length=50, blank=True, verbose_name="Temperature Range") humidity_levels = models.CharField(max_length=50, blank=True, verbose_name="Humidity Levels")
# Packing recommendations packing_essentials_en = models.TextField(blank=True, verbose_name="Packing Essentials (English)") packing_essentials_vi = models.TextField(blank=True, verbose_name="Packing Essentials (Vietnamese)") clothing_recommendations_en = models.TextField(blank=True, verbose_name="Clothing Recommendations (English)") clothing_recommendations_vi = models.TextField(blank=True, verbose_name="Clothing Recommendations (Vietnamese)") cultural_dress_code_en = models.TextField(blank=True, verbose_name="Cultural Dress Code (English)") cultural_dress_code_vi = models.TextField(blank=True, verbose_name="Cultural Dress Code (Vietnamese)") prohibited_items_en = models.TextField(blank=True, verbose_name="Prohibited Items (English)") prohibited_items_vi = models.TextField(blank=True, verbose_name="Prohibited Items (Vietnamese)")
# ================================ # 🎯 ADDITIONAL FEATURES # ================================ travel_style_recommendations_en = models.TextField(blank=True, verbose_name="Travel Style Recommendations (English)") travel_style_recommendations_vi = models.TextField(blank=True, verbose_name="Travel Style Recommendations (Vietnamese)") solo_travel_tips_en = models.TextField(blank=True, verbose_name="Solo Travel Tips (English)") solo_travel_tips_vi = models.TextField(blank=True, verbose_name="Solo Travel Tips (Vietnamese)") family_travel_tips_en = models.TextField(blank=True, verbose_name="Family Travel Tips (English)") family_travel_tips_vi = models.TextField(blank=True, verbose_name="Family Travel Tips (Vietnamese)") backpacker_tips_en = models.TextField(blank=True, verbose_name="Backpacker Tips (English)") backpacker_tips_vi = models.TextField(blank=True, verbose_name="Backpacker Tips (Vietnamese)")
# Country comparison and ranking tourism_ranking = models.PositiveIntegerField(blank=True, null=True, verbose_name="Global Tourism Ranking") ease_of_travel_score = models.DecimalField(max_digits=3, decimal_places=1, blank=True, null=True, validators=[MinValueValidator(0), MaxValueValidator(10)], verbose_name="Ease of Travel Score (1-10)") english_proficiency = models.CharField(max_length=20, choices=[ ('excellent', 'Excellent'), ('good', 'Good'), ('moderate', 'Moderate'), ('limited', 'Limited'), ('very_limited', 'Very Limited') ], blank=True, verbose_name="English Proficiency Level")
# Special notes and recommendations special_notes_en = models.TextField(blank=True, verbose_name="Special Notes (English)") special_notes_vi = models.TextField(blank=True, verbose_name="Special Notes (Vietnamese)") insider_tips_en = models.TextField(blank=True, verbose_name="Insider Tips (English)") insider_tips_vi = models.TextField(blank=True, verbose_name="Insider Tips (Vietnamese)")
# ================================ # 📊 ADMINISTRATIVE # ================================ created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True) is_popular = models.BooleanField(default=False, help_text="Mark as popular destination country") featured_order = models.PositiveIntegerField(default=0, help_text="Order for featured countries") is_complete_profile = models.BooleanField(default=False, verbose_name="Complete Profile", help_text="Mark if country profile is fully completed") profile_completion_percentage = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(100)], verbose_name="Profile Completion %")
class Meta: verbose_name = _("Country") verbose_name_plural = _("Countries") verbose_name_plural = "Countries" ordering = ['name_en']
def __str__(self): return self.name_en
def get_absolute_url(self): return reverse('destinations:country_detail', args=[str(self.id)])
|
2 |
class DestinationCategory(models.Model): """Categories for destinations (Beach, Mountain, City, etc.)""" name_en = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True) description_en = models.TextField(blank=True) description_vi = models.TextField(blank=True) icon = models.CharField(max_length=50, blank=True, help_text="Font Awesome icon class") color = models.CharField(max_length=7, default="#007bff", help_text="Hex color code")
def __str__(self): return self.name_en
class Meta: verbose_name_plural = "Destination Categories"
|
2.1 |
class Destination(models.Model): """Enhanced destination model with country relationship and TripAdvisor-style features"""
# Basic relationships location = models.OneToOneField(Location, on_delete=models.CASCADE, limit_choices_to={'type': 'city'}) country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='destinations', null=True, blank=True) categories = models.ManyToManyField(DestinationCategory, blank=True, help_text="Beach, Mountain, City, etc.")
# Basic info title_en = models.CharField(max_length=200) title_vi = models.CharField(max_length=200, blank=True) description_en = models.TextField() description_vi = models.TextField(blank=True) short_description_en = models.CharField(max_length=300, blank=True, help_text="For cards and previews") short_description_vi = models.CharField(max_length=300, blank=True)
# Images main_image = models.ImageField(upload_to='destinations/', blank=True, null=True) hero_image = models.ImageField(upload_to='destinations/hero/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs")
# TripAdvisor-style ratings and rankings overall_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) total_reviews = models.PositiveIntegerField(default=0) ranking_in_country = models.PositiveIntegerField(blank=True, null=True) ranking_worldwide = models.PositiveIntegerField(blank=True, null=True) traveler_choice_award = models.BooleanField(default=False)
# Visit statistics annual_visitors = models.PositiveIntegerField(blank=True, null=True, help_text="Annual visitor count") peak_season_months = models.CharField(max_length=100, blank=True, help_text="e.g., June-August") off_season_months = models.CharField(max_length=100, blank=True, help_text="e.g., December-February")
# Local destination-specific overrides (if different from country) local_currency_notes_en = models.TextField(blank=True, help_text="Destination-specific currency info") local_currency_notes_vi = models.TextField(blank=True) exchange_rate_note_en = models.TextField(blank=True) exchange_rate_note_vi = models.TextField(blank=True) payment_methods_en = models.CharField(max_length=200, blank=True) payment_methods_vi = models.CharField(max_length=200, blank=True) atm_info_en = models.TextField(blank=True) atm_info_vi = models.TextField(blank=True)
# Local cultural info local_customs_en = models.TextField(blank=True, help_text="City/region specific customs") local_customs_vi = models.TextField(blank=True) local_etiquette_en = models.TextField(blank=True) local_etiquette_vi = models.TextField(blank=True) local_language_notes_en = models.TextField(blank=True, help_text="Local dialects, language tips") local_language_notes_vi = models.TextField(blank=True)
# Destination-specific practical info tipping_culture_en = models.TextField(blank=True) tipping_culture_vi = models.TextField(blank=True) local_emergency_contacts = models.TextField(blank=True, help_text="JSON format for local emergency numbers") hospital_info_en = models.TextField(blank=True) hospital_info_vi = models.TextField(blank=True)
# Climate and timing climate_info_en = models.TextField(blank=True) climate_info_vi = models.TextField(blank=True) best_time_to_visit_en = models.CharField(max_length=100, blank=True) best_time_to_visit_vi = models.CharField(max_length=100, blank=True) weather_highlights_en = models.TextField(blank=True) weather_highlights_vi = models.TextField(blank=True)
# Technology and connectivity sim_info_en = models.TextField(blank=True) sim_info_vi = models.TextField(blank=True) wifi_availability_en = models.TextField(blank=True) wifi_availability_vi = models.TextField(blank=True) connectivity_info_en = models.TextField(blank=True) connectivity_info_vi = models.TextField(blank=True)
# Accessibility accessibility_info_en = models.TextField(blank=True) accessibility_info_vi = models.TextField(blank=True) wheelchair_friendly = models.BooleanField(default=False) accessibility_rating = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1), MaxValueValidator(5)])
# Transportation getting_there_en = models.TextField(blank=True, help_text="How to reach this destination") getting_there_vi = models.TextField(blank=True) getting_around_en = models.TextField(blank=True, help_text="Local transportation options") getting_around_vi = models.TextField(blank=True) airport_info_en = models.TextField(blank=True) airport_info_vi = models.TextField(blank=True)
# Budget information budget_range = models.CharField(max_length=20, choices=[ ('budget', 'Budget ($)'), ('moderate', 'Moderate ($$)'), ('expensive', 'Expensive ($$$)'), ('luxury', 'Luxury ($$$$)') ], blank=True) daily_budget_low = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) daily_budget_mid = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) daily_budget_high = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) budget_tips_en = models.TextField(blank=True) budget_tips_vi = models.TextField(blank=True)
# SEO and metadata meta_description_en = models.CharField(max_length=160, blank=True) meta_description_vi = models.CharField(max_length=160, blank=True) keywords_en = models.CharField(max_length=200, blank=True, help_text="Comma-separated keywords") keywords_vi = models.CharField(max_length=200, blank=True)
# Administrative is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) is_published = models.BooleanField(default=True) featured_order = models.PositiveIntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: verbose_name = _("Destination") verbose_name_plural = _("Destinations") ordering = ['-is_featured', '-overall_rating', 'title_en'] indexes = [ models.Index(fields=['country', 'is_published']), models.Index(fields=['overall_rating', 'total_reviews']), models.Index(fields=['is_featured', 'is_popular']), ]
def __str__(self): return self.title_en
def get_absolute_url(self): """Return the URL to the destination detail page.""" return reverse('destinations:destination_detail', args=[str(self.id)])
|
|
|
3 |
class WhatToSee(models.Model): """Enhanced attractions model with TripAdvisor-style features""" TYPE_CHOICES = [ ('museum', 'Museum'), ('park', 'Park'), ('landmark', 'Landmark'), ('shopping', 'Shopping'), ('nature', 'Nature'), ('religious', 'Religious Site'), ('entertainment', 'Entertainment'), ('beach', 'Beach'), ('viewpoint', 'Viewpoint'), ('historical', 'Historical Site'), ('cultural', 'Cultural Site'), ('adventure', 'Adventure Activity'), ('other', 'Other'), ]
PRICE_RANGE_CHOICES = [ ('free', 'Free'), ('budget', 'Budget ($)'), ('moderate', 'Moderate ($$)'), ('expensive', 'Expensive ($$$)'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='attractions') name_en = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=30, choices=TYPE_CHOICES, default='other') description_en = models.TextField() description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")
# TripAdvisor-style ratings rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) review_count = models.PositiveIntegerField(default=0) ranking = models.PositiveIntegerField(blank=True, null=True, help_text="Ranking in destination")
# Practical information address_en = models.CharField(max_length=300, blank=True) address_vi = models.CharField(max_length=300, blank=True) phone = models.CharField(max_length=50, blank=True) website = models.URLField(blank=True) email = models.EmailField(blank=True)
# Pricing and duration price_range = models.CharField(max_length=20, choices=PRICE_RANGE_CHOICES, blank=True) entry_fee_notes_en = models.TextField(blank=True) entry_fee_notes_vi = models.TextField(blank=True) suggested_duration_hours = models.DecimalField(max_digits=4, decimal_places=1, blank=True, null=True)
# Operating information opening_hours = models.TextField(blank=True, help_text="JSON format for operating hours") best_time_to_visit_en = models.CharField(max_length=200, blank=True) best_time_to_visit_vi = models.CharField(max_length=200, blank=True)
# Features and amenities accessibility_features = models.TextField(blank=True, help_text="JSON array of accessibility features") amenities = models.TextField(blank=True, help_text="JSON array of amenities") languages_spoken = models.CharField(max_length=200, blank=True)
# Media image = models.ImageField(upload_to='attractions/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs") virtual_tour_url = models.URLField(blank=True)
# Tips and notes insider_tips_en = models.TextField(blank=True) insider_tips_vi = models.TextField(blank=True) what_to_bring_en = models.TextField(blank=True) what_to_bring_vi = models.TextField(blank=True)
# Administrative is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-rating', 'name_en'] verbose_name = "Attraction" verbose_name_plural = "Attractions"
def __str__(self): return self.name_en
def get_absolute_url(self): """Return the URL to the destination detail page, scrolled to this attraction.""" return self.destination.get_absolute_url() + f"#attraction-{self.id}"
|
4 |
class WhereToEat(models.Model): """Enhanced restaurant model with TripAdvisor-style features""" TYPE_CHOICES = [ ('restaurant', 'Restaurant'), ('cafe', 'Cafe'), ('street_food', 'Street Food'), ('bar', 'Bar'), ('fast_food', 'Fast Food'), ('fine_dining', 'Fine Dining'), ('food_court', 'Food Court'), ('bakery', 'Bakery'), ('ice_cream', 'Ice Cream'), ('other', 'Other'), ]
CUISINE_CHOICES = [ ('vietnamese', 'Vietnamese'), ('italian', 'Italian'), ('french', 'French'), ('chinese', 'Chinese'), ('japanese', 'Japanese'), ('korean', 'Korean'), ('thai', 'Thai'), ('indian', 'Indian'), ('american', 'American'), ('mediterranean', 'Mediterranean'), ('mexican', 'Mexican'), ('seafood', 'Seafood'), ('vegetarian', 'Vegetarian'), ('vegan', 'Vegan'), ('international', 'International'), ('fusion', 'Fusion'), ('other', 'Other'), ]
PRICE_RANGE_CHOICES = [ ('budget', 'Budget ($)'), ('moderate', 'Moderate ($$)'), ('expensive', 'Expensive ($$$)'), ('luxury', 'Luxury ($$$$)'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='restaurants') name_en = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=30, choices=TYPE_CHOICES, default='restaurant') cuisine = models.CharField(max_length=30, choices=CUISINE_CHOICES, default='other') description_en = models.TextField() description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")
# TripAdvisor-style ratings rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) review_count = models.PositiveIntegerField(default=0) ranking = models.PositiveIntegerField(blank=True, null=True, help_text="Ranking in destination")
# Detailed ratings food_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) service_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) value_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) atmosphere_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)])
# Contact and location address_en = models.CharField(max_length=300, blank=True) address_vi = models.CharField(max_length=300, blank=True) phone = models.CharField(max_length=50, blank=True) website = models.URLField(blank=True) email = models.EmailField(blank=True)
# Pricing and details price_range = models.CharField(max_length=20, choices=PRICE_RANGE_CHOICES, blank=True) average_cost_for_two = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) currency = models.CharField(max_length=10, blank=True)
# Operating information opening_hours = models.TextField(blank=True, help_text="JSON format for operating hours") reservation_required = models.BooleanField(default=False) delivery_available = models.BooleanField(default=False) takeaway_available = models.BooleanField(default=False)
# Features and amenities features = models.TextField(blank=True, help_text="JSON array: outdoor seating, wifi, parking, etc.") dietary_options = models.TextField(blank=True, help_text="JSON array: vegetarian, vegan, gluten-free, etc.") payment_methods = models.CharField(max_length=200, blank=True) languages_spoken = models.CharField(max_length=200, blank=True)
# Special features happy_hour = models.BooleanField(default=False) live_music = models.BooleanField(default=False) outdoor_seating = models.BooleanField(default=False) wifi_available = models.BooleanField(default=False) parking_available = models.BooleanField(default=False) wheelchair_accessible = models.BooleanField(default=False)
# Media image = models.ImageField(upload_to='restaurants/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs") menu_url = models.URLField(blank=True)
# Recommendations signature_dishes_en = models.TextField(blank=True) signature_dishes_vi = models.TextField(blank=True) chef_recommendations_en = models.TextField(blank=True) chef_recommendations_vi = models.TextField(blank=True)
# Administrative is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) traveler_choice = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-rating', 'name_en'] verbose_name = "Restaurant" verbose_name_plural = "Restaurants"
def __str__(self): return self.name_en
def get_absolute_url(self): """Return the URL to the destination detail page, scrolled to this restaurant.""" return self.destination.get_absolute_url() + f"#restaurant-{self.id}"
|
5 |
class WhereToStay(models.Model): """Enhanced accommodation model with TripAdvisor-style features""" TYPE_CHOICES = [ ('hotel', 'Hotel'), ('resort', 'Resort'), ('hostel', 'Hostel'), ('guesthouse', 'Guesthouse'), ('villa', 'Villa'), ('apartment', 'Apartment'), ('homestay', 'Homestay'), ('boutique', 'Boutique Hotel'), ('luxury', 'Luxury Hotel'), ('budget', 'Budget Hotel'), ('spa', 'Spa Resort'), ('eco', 'Eco Lodge'), ('beach', 'Beach Resort'), ('city', 'City Hotel'), ('other', 'Other'), ]
STAR_RATING_CHOICES = [ ('1', '1 Star'), ('2', '2 Stars'), ('3', '3 Stars'), ('4', '4 Stars'), ('5', '5 Stars'), ('unrated', 'Unrated'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='accommodations') name_en = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=30, choices=TYPE_CHOICES, default='hotel') star_rating = models.CharField(max_length=20, choices=STAR_RATING_CHOICES, blank=True, default='unrated') description_en = models.TextField() description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")
# TripAdvisor-style ratings rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) review_count = models.PositiveIntegerField(default=0) ranking = models.PositiveIntegerField(blank=True, null=True, help_text="Ranking in destination")
# Detailed ratings cleanliness_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) service_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) value_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) location_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) comfort_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)])
# Contact and location address_en = models.CharField(max_length=300, blank=True) address_vi = models.CharField(max_length=300, blank=True) phone = models.CharField(max_length=50, blank=True) website = models.URLField(blank=True) email = models.EmailField(blank=True)
# Pricing information price_per_night_min = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) price_per_night_max = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) currency = models.CharField(max_length=10, blank=True) includes_breakfast = models.BooleanField(default=False) includes_wifi = models.BooleanField(default=False) includes_parking = models.BooleanField(default=False)
# Property details total_rooms = models.PositiveIntegerField(blank=True, null=True) check_in_time = models.TimeField(blank=True, null=True) check_out_time = models.TimeField(blank=True, null=True)
# Amenities and facilities amenities = models.TextField(blank=True, help_text="JSON array: pool, spa, gym, restaurant, etc.") room_features = models.TextField(blank=True, help_text="JSON array: AC, TV, minibar, balcony, etc.") services = models.TextField(blank=True, help_text="JSON array: room service, concierge, laundry, etc.")
# Accessibility and policies wheelchair_accessible = models.BooleanField(default=False) pet_friendly = models.BooleanField(default=False) family_friendly = models.BooleanField(default=False) smoking_allowed = models.BooleanField(default=False) age_restriction = models.CharField(max_length=100, blank=True)
# Hotel facilities has_restaurant = models.BooleanField(default=False) has_bar = models.BooleanField(default=False) has_pool = models.BooleanField(default=False) has_spa = models.BooleanField(default=False) has_gym = models.BooleanField(default=False) has_beach_access = models.BooleanField(default=False) has_airport_shuttle = models.BooleanField(default=False)
# Connectivity wifi_available = models.BooleanField(default=False) wifi_free = models.BooleanField(default=False) business_center = models.BooleanField(default=False)
# Transportation distance_to_city_center = models.CharField(max_length=50, blank=True) distance_to_airport = models.CharField(max_length=50, blank=True) parking_available = models.BooleanField(default=False) parking_free = models.BooleanField(default=False)
# Media image = models.ImageField(upload_to='accommodations/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs") virtual_tour_url = models.URLField(blank=True)
# Awards and certifications awards = models.TextField(blank=True, help_text="JSON array of awards and certifications") eco_certified = models.BooleanField(default=False) covid_safety_measures = models.TextField(blank=True)
# Booking information booking_url = models.URLField(blank=True) instant_booking = models.BooleanField(default=False) free_cancellation = models.BooleanField(default=False) cancellation_policy_en = models.TextField(blank=True) cancellation_policy_vi = models.TextField(blank=True)
# Special features is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) traveler_choice = models.BooleanField(default=False) best_value = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-rating', 'name_en'] verbose_name = "Accommodation" verbose_name_plural = "Accommodations"
def __str__(self): return self.name_en
def get_absolute_url(self): """Return the URL to the destination detail page, scrolled to this accommodation.""" return self.destination.get_absolute_url() + f"#accommodation-{self.id}"
|
6 |
class Event(models.Model): """Enhanced event model with TripAdvisor-style features""" TYPE_CHOICES = [ ('festival', 'Festival'), ('concert', 'Concert'), ('exhibition', 'Exhibition'), ('show', 'Show'), ('sports', 'Sports Event'), ('cultural', 'Cultural Event'), ('seasonal', 'Seasonal Event'), ('food', 'Food & Wine'), ('nightlife', 'Nightlife'), ('outdoor', 'Outdoor Activity'), ('family', 'Family Event'), ('business', 'Business Event'), ('religious', 'Religious Event'), ('market', 'Market/Fair'), ('other', 'Other'), ]
FREQUENCY_CHOICES = [ ('one_time', 'One-time Event'), ('daily', 'Daily'), ('weekly', 'Weekly'), ('monthly', 'Monthly'), ('seasonal', 'Seasonal'), ('annual', 'Annual'), ('irregular', 'Irregular'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='events') name_en = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=30, choices=TYPE_CHOICES, default='other') description_en = models.TextField() description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")
# TripAdvisor-style ratings rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) review_count = models.PositiveIntegerField(default=0)
# Event scheduling start_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True) start_time = models.TimeField(blank=True, null=True) end_time = models.TimeField(blank=True, null=True) frequency = models.CharField(max_length=20, choices=FREQUENCY_CHOICES, default='one_time') duration = models.CharField(max_length=100, blank=True, help_text="e.g., 2 hours, 3 days")
# Location details venue_name_en = models.CharField(max_length=200, blank=True) venue_name_vi = models.CharField(max_length=200, blank=True) address_en = models.CharField(max_length=300, blank=True) address_vi = models.CharField(max_length=300, blank=True)
# Contact and booking website = models.URLField(blank=True) phone = models.CharField(max_length=50, blank=True) email = models.EmailField(blank=True) booking_required = models.BooleanField(default=False) booking_url = models.URLField(blank=True)
# Pricing is_free = models.BooleanField(default=False) price_min = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) price_max = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) currency = models.CharField(max_length=10, blank=True) price_details_en = models.TextField(blank=True) price_details_vi = models.TextField(blank=True)
# Event features age_restrictions = models.CharField(max_length=100, blank=True) dress_code = models.CharField(max_length=100, blank=True) languages = models.CharField(max_length=200, blank=True, help_text="Languages supported") accessibility_info_en = models.TextField(blank=True) accessibility_info_vi = models.TextField(blank=True)
# Event amenities food_available = models.BooleanField(default=False) drinks_available = models.BooleanField(default=False) parking_available = models.BooleanField(default=False) photography_allowed = models.BooleanField(default=True) wheelchair_accessible = models.BooleanField(default=False)
# Weather dependency indoor_event = models.BooleanField(default=False) weather_dependent = models.BooleanField(default=False) rain_alternative_en = models.TextField(blank=True) rain_alternative_vi = models.TextField(blank=True)
# Media image = models.ImageField(upload_to='events/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs")
# Event highlights highlights_en = models.TextField(blank=True) highlights_vi = models.TextField(blank=True) what_to_expect_en = models.TextField(blank=True) what_to_expect_vi = models.TextField(blank=True) what_to_bring_en = models.TextField(blank=True) what_to_bring_vi = models.TextField(blank=True)
# Popularity indicators is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) traveler_choice = models.BooleanField(default=False) sold_out = models.BooleanField(default=False)
# Administrative created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-start_date', '-rating', 'name_en'] verbose_name = "Event" verbose_name_plural = "Events"
def __str__(self): return self.name_en
def get_absolute_url(self): """Return the URL to the destination detail page, scrolled to this event.""" return self.destination.get_absolute_url() + f"#event-{self.id}"
@property def is_upcoming(self): """Check if event is upcoming""" if self.start_date: from django.utils import timezone return self.start_date >= timezone.now().date() return False
@property def is_ongoing(self): """Check if event is currently ongoing""" if self.start_date and self.end_date: from django.utils import timezone today = timezone.now().date() return self.start_date <= today <= self.end_date return False
|
7 |
class SafetyTip(models.Model): destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='safety_tips') tip_en = models.TextField() tip_vi = models.TextField(blank=True, verbose_name="Vietnamese Safety Tip")
def __str__(self): return self.tip_en[:50]
|
8 |
class UserReview(models.Model): """User reviews for destinations and related items""" REVIEW_TYPE_CHOICES = [ ('destination', 'Destination'), ('attraction', 'Attraction'), ('restaurant', 'Restaurant'), ('accommodation', 'Accommodation'), ('event', 'Event'), ]
TRAVELER_TYPE_CHOICES = [ ('family', 'Family'), ('couple', 'Couple'), ('solo', 'Solo Traveler'), ('business', 'Business'), ('friends', 'Friends'), ('other', 'Other'), ]
VISIT_TYPE_CHOICES = [ ('first_time', 'First Time'), ('repeat', 'Repeat Visitor'), ]
# Review target (polymorphic relationship) review_type = models.CharField(max_length=20, choices=REVIEW_TYPE_CHOICES) destination = models.ForeignKey(Destination, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews') attraction = models.ForeignKey(WhatToSee, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews') restaurant = models.ForeignKey(WhereToEat, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews') accommodation = models.ForeignKey(WhereToStay, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews') event = models.ForeignKey(Event, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews')
# Reviewer information user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='destination_reviews') reviewer_name = models.CharField(max_length=100) reviewer_location = models.CharField(max_length=100, blank=True) traveler_type = models.CharField(max_length=20, choices=TRAVELER_TYPE_CHOICES, blank=True) visit_type = models.CharField(max_length=20, choices=VISIT_TYPE_CHOICES, blank=True)
# Review content title = models.CharField(max_length=200) review_text = models.TextField()
# Ratings overall_rating = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
# Specific ratings (for different review types) service_rating = models.PositiveIntegerField(null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)]) value_rating = models.PositiveIntegerField(null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)]) location_rating = models.PositiveIntegerField(null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)]) cleanliness_rating = models.PositiveIntegerField(null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)])
# Visit details visit_date = models.DateField(null=True, blank=True) stay_duration = models.CharField(max_length=50, blank=True, help_text="e.g., 3 days, 1 week")
# Review metadata helpful_votes = models.PositiveIntegerField(default=0) total_votes = models.PositiveIntegerField(default=0) is_verified = models.BooleanField(default=False) is_featured = models.BooleanField(default=False)
# Administrative created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True) is_published = models.BooleanField(default=True)
class Meta: ordering = ['-created_at'] unique_together = [['user', 'review_type', 'destination'], ['user', 'review_type', 'attraction'], ['user', 'review_type', 'restaurant'], ['user', 'review_type', 'accommodation'], ['user', 'review_type', 'event']] verbose_name = "User Review" verbose_name_plural = "User Reviews"
def __str__(self): return f"{self.title} by {self.reviewer_name}"
@property def helpful_percentage(self): """Calculate helpful vote percentage""" if self.total_votes > 0: return round((self.helpful_votes / self.total_votes) * 100, 1) return 0
|
9 |
class TravelTip(models.Model): """Travel tips and insider advice for destinations""" TIP_CATEGORY_CHOICES = [ ('general', 'General Tips'), ('transportation', 'Transportation'), ('accommodation', 'Accommodation'), ('food', 'Food & Dining'), ('shopping', 'Shopping'), ('culture', 'Culture & Customs'), ('safety', 'Safety'), ('money', 'Money & Costs'), ('weather', 'Weather & Climate'), ('activities', 'Activities'), ('photography', 'Photography'), ('health', 'Health & Medical'), ('packing', 'Packing'), ('communication', 'Communication'), ('etiquette', 'Local Etiquette'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='travel_tips') category = models.CharField(max_length=30, choices=TIP_CATEGORY_CHOICES) title_en = models.CharField(max_length=200) title_vi = models.CharField(max_length=200, blank=True) tip_en = models.TextField() tip_vi = models.TextField(blank=True)
# Tip metadata is_insider_tip = models.BooleanField(default=False) is_featured = models.BooleanField(default=False) helpful_votes = models.PositiveIntegerField(default=0)
# Author information author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True) author_name = models.CharField(max_length=100, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-helpful_votes', '-created_at'] verbose_name = "Travel Tip" verbose_name_plural = "Travel Tips"
def __str__(self): return f"{self.title_en} ({self.get_category_display()})"
|
10 |
class UsefulPhrase(models.Model): destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='phrases') phrase_en = models.CharField(max_length=200, default='', verbose_name="English Phrase") phrase_vi = models.CharField(max_length=200, default='', verbose_name="Vietnamese Phrase") translation = models.CharField(max_length=200, blank=True, verbose_name="Pronunciation Guide") note_en = models.CharField(max_length=200, blank=True, verbose_name="English Note") note_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Note")
def __str__(self): return f"{self.phrase_en} - {self.phrase_vi}"
|
Destination Models
Nhom |
Notes |
|
|
|
from django.db import models from django.urls import reverse from django.contrib.auth.models import User from django.core.validators import MinValueValidator, MaxValueValidator from django.utils.translation import gettext_lazy as _ from decimal import Decimal from apps.geo.models import Location
|
1 |
class Country(models.Model): """Comprehensive country-level information for travel planning"""
# ================================ # 🏳️ COUNTRY OVERVIEW # ================================ name_en = models.CharField(max_length=100, unique=True, verbose_name="Country Name (English)") name_vi = models.CharField(max_length=100, blank=True, verbose_name="Country Name (Vietnamese)") official_name_en = models.CharField(max_length=150, blank=True, verbose_name="Official Name (English)") official_name_vi = models.CharField(max_length=150, blank=True, verbose_name="Official Name (Vietnamese)") country_code = models.CharField(max_length=3, unique=True, help_text="ISO 3166-1 alpha-3 code") continent = models.CharField(max_length=50) flag_image = models.ImageField(upload_to='countries/flags/', blank=True, null=True)
# Basic geographic info capital_en = models.CharField(max_length=100, blank=True, verbose_name="Capital (English)") capital_vi = models.CharField(max_length=100, blank=True, verbose_name="Capital (Vietnamese)") major_cities_en = models.TextField(blank=True, verbose_name="Major Cities (English)", help_text="Comma-separated list") major_cities_vi = models.TextField(blank=True, verbose_name="Major Cities (Vietnamese)", help_text="Comma-separated list") population = models.BigIntegerField(blank=True, null=True) area_km2 = models.BigIntegerField(blank=True, null=True, help_text="Area in square kilometers") gdp_per_capita = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
# Languages and communication official_languages_en = models.CharField(max_length=200, blank=True, verbose_name="Official Languages (English)") official_languages_vi = models.CharField(max_length=200, blank=True, verbose_name="Official Languages (Vietnamese)") spoken_languages_en = models.CharField(max_length=300, blank=True, verbose_name="Commonly Spoken Languages (English)") spoken_languages_vi = models.CharField(max_length=300, blank=True, verbose_name="Commonly Spoken Languages (Vietnamese)")
# Time and location time_zones = models.CharField(max_length=200, blank=True, verbose_name="Time Zones") calling_code = models.CharField(max_length=10, blank=True, verbose_name="Country Calling Code") internet_domain = models.CharField(max_length=10, blank=True, verbose_name="Internet Domain")
# Infrastructure basics driving_side = models.CharField(max_length=10, choices=[('left', 'Left'), ('right', 'Right')], blank=True) power_plugs = models.CharField(max_length=50, blank=True, help_text="e.g., Type A, B, C") voltage = models.CharField(max_length=20, blank=True, help_text="e.g., 110V, 220V") frequency = models.CharField(max_length=10, blank=True, help_text="e.g., 50Hz, 60Hz")
# ================================ # 💰 CURRENCY & FINANCIAL INFO # ================================ currency_name_en = models.CharField(max_length=50, blank=True, verbose_name="Currency Name (English)") currency_name_vi = models.CharField(max_length=50, blank=True, verbose_name="Currency Name (Vietnamese)") currency_code = models.CharField(max_length=10, blank=True, verbose_name="Currency Code (ISO)") currency_symbol = models.CharField(max_length=10, blank=True, verbose_name="Currency Symbol") currency_subdivisions_en = models.CharField(max_length=100, blank=True, verbose_name="Currency Subdivisions (English)") currency_subdivisions_vi = models.CharField(max_length=100, blank=True, verbose_name="Currency Subdivisions (Vietnamese)")
# Money exchange and usage usd_accepted = models.BooleanField(default=False, verbose_name="USD Widely Accepted") euro_accepted = models.BooleanField(default=False, verbose_name="EUR Widely Accepted") exchange_info_en = models.TextField(blank=True, verbose_name="Money Exchange Info (English)") exchange_info_vi = models.TextField(blank=True, verbose_name="Money Exchange Info (Vietnamese)") atm_availability_en = models.TextField(blank=True, verbose_name="ATM Availability (English)") atm_availability_vi = models.TextField(blank=True, verbose_name="ATM Availability (Vietnamese)") credit_cards_accepted = models.BooleanField(default=True, verbose_name="Credit Cards Widely Accepted")
# Tipping and costs tipping_culture_en = models.TextField(blank=True, verbose_name="Tipping Culture (English)") tipping_culture_vi = models.TextField(blank=True, verbose_name="Tipping Culture (Vietnamese)") daily_budget_backpacker = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True, verbose_name="Daily Budget - Backpacker (USD)") daily_budget_mid_range = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True, verbose_name="Daily Budget - Mid-range (USD)") daily_budget_luxury = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True, verbose_name="Daily Budget - Luxury (USD)")
# ================================ # 📜 HISTORY & CULTURE # ================================ historical_background_en = models.TextField(blank=True, verbose_name="Historical Background (English)") historical_background_vi = models.TextField(blank=True, verbose_name="Historical Background (Vietnamese)") cultural_highlights_en = models.TextField(blank=True, verbose_name="Cultural Highlights (English)") cultural_highlights_vi = models.TextField(blank=True, verbose_name="Cultural Highlights (Vietnamese)")
# Religion and festivals major_religions_en = models.CharField(max_length=200, blank=True, verbose_name="Major Religions (English)") major_religions_vi = models.CharField(max_length=200, blank=True, verbose_name="Major Religions (Vietnamese)") festivals_holidays_en = models.TextField(blank=True, verbose_name="Major Festivals & Holidays (English)") festivals_holidays_vi = models.TextField(blank=True, verbose_name="Major Festivals & Holidays (Vietnamese)")
# Social customs customs_traditions_en = models.TextField(blank=True, verbose_name="Customs & Traditions (English)") customs_traditions_vi = models.TextField(blank=True, verbose_name="Customs & Traditions (Vietnamese)") etiquette_tips_en = models.TextField(blank=True, verbose_name="Etiquette Tips (English)") etiquette_tips_vi = models.TextField(blank=True, verbose_name="Etiquette Tips (Vietnamese)") cultural_taboos_en = models.TextField(blank=True, verbose_name="Cultural Taboos (English)") cultural_taboos_vi = models.TextField(blank=True, verbose_name="Cultural Taboos (Vietnamese)")
# ================================ # ✈️ ENTRY & VISA REQUIREMENTS # ================================ visa_requirements_vietnamese_en = models.TextField(blank=True, verbose_name="Visa for Vietnamese Citizens (English)") visa_requirements_vietnamese_vi = models.TextField(blank=True, verbose_name="Visa for Vietnamese Citizens (Vietnamese)") visa_requirements_us_en = models.TextField(blank=True, verbose_name="Visa for US Citizens (English)") visa_requirements_us_vi = models.TextField(blank=True, verbose_name="Visa for US Citizens (Vietnamese)") visa_requirements_eu_en = models.TextField(blank=True, verbose_name="Visa for EU Citizens (English)") visa_requirements_eu_vi = models.TextField(blank=True, verbose_name="Visa for EU Citizens (Vietnamese)") visa_requirements_general_en = models.TextField(blank=True, verbose_name="General Visa Requirements (English)") visa_requirements_general_vi = models.TextField(blank=True, verbose_name="General Visa Requirements (Vietnamese)")
# Stay duration and documents max_stay_duration = models.CharField(max_length=100, blank=True, verbose_name="Maximum Stay Duration") required_documents_en = models.TextField(blank=True, verbose_name="Required Documents (English)") required_documents_vi = models.TextField(blank=True, verbose_name="Required Documents (Vietnamese)") vaccination_requirements_en = models.TextField(blank=True, verbose_name="Vaccination Requirements (English)") vaccination_requirements_vi = models.TextField(blank=True, verbose_name="Vaccination Requirements (Vietnamese)")
# Customs and entry customs_duty_free_en = models.TextField(blank=True, verbose_name="Duty-Free Allowances (English)") customs_duty_free_vi = models.TextField(blank=True, verbose_name="Duty-Free Allowances (Vietnamese)") customs_restrictions_en = models.TextField(blank=True, verbose_name="Customs Restrictions (English)") customs_restrictions_vi = models.TextField(blank=True, verbose_name="Customs Restrictions (Vietnamese)") entry_requirements_en = models.TextField(blank=True, verbose_name="Entry Requirements (English)") entry_requirements_vi = models.TextField(blank=True, verbose_name="Entry Requirements (Vietnamese)")
# ================================ # 📱 TECHNOLOGY & CONNECTIVITY # ================================ mobile_carriers_en = models.TextField(blank=True, verbose_name="Mobile Carriers (English)") mobile_carriers_vi = models.TextField(blank=True, verbose_name="Mobile Carriers (Vietnamese)") sim_card_info_en = models.TextField(blank=True, verbose_name="SIM Card Information (English)") sim_card_info_vi = models.TextField(blank=True, verbose_name="SIM Card Information (Vietnamese)") esim_available = models.BooleanField(default=False, verbose_name="eSIM Available") esim_providers_en = models.TextField(blank=True, verbose_name="eSIM Providers (English)") esim_providers_vi = models.TextField(blank=True, verbose_name="eSIM Providers (Vietnamese)")
# Internet and connectivity internet_coverage_en = models.TextField(blank=True, verbose_name="Internet Coverage (English)") internet_coverage_vi = models.TextField(blank=True, verbose_name="Internet Coverage (Vietnamese)") wifi_availability_en = models.TextField(blank=True, verbose_name="WiFi Availability (English)") wifi_availability_vi = models.TextField(blank=True, verbose_name="WiFi Availability (Vietnamese)") internet_speed_quality = models.CharField(max_length=20, choices=[ ('excellent', 'Excellent'), ('good', 'Good'), ('moderate', 'Moderate'), ('poor', 'Poor') ], blank=True, verbose_name="Internet Speed Quality")
# Useful apps maps_apps_en = models.TextField(blank=True, verbose_name="Recommended Maps Apps (English)") maps_apps_vi = models.TextField(blank=True, verbose_name="Recommended Maps Apps (Vietnamese)") transport_apps_en = models.TextField(blank=True, verbose_name="Transport Apps (English)") transport_apps_vi = models.TextField(blank=True, verbose_name="Transport Apps (Vietnamese)") translation_apps_en = models.TextField(blank=True, verbose_name="Translation Apps (English)") translation_apps_vi = models.TextField(blank=True, verbose_name="Translation Apps (Vietnamese)") food_delivery_apps_en = models.TextField(blank=True, verbose_name="Food Delivery Apps (English)") food_delivery_apps_vi = models.TextField(blank=True, verbose_name="Food Delivery Apps (Vietnamese)") other_useful_apps_en = models.TextField(blank=True, verbose_name="Other Useful Apps (English)") other_useful_apps_vi = models.TextField(blank=True, verbose_name="Other Useful Apps (Vietnamese)")
# ================================ # 🚌 TRANSPORTATION # ================================ airports_info_en = models.TextField(blank=True, verbose_name="Airport Information (English)") airports_info_vi = models.TextField(blank=True, verbose_name="Airport Information (Vietnamese)") airport_transfer_en = models.TextField(blank=True, verbose_name="Airport Transfer Options (English)") airport_transfer_vi = models.TextField(blank=True, verbose_name="Airport Transfer Options (Vietnamese)")
# Public transportation public_transport_overview_en = models.TextField(blank=True, verbose_name="Public Transport Overview (English)") public_transport_overview_vi = models.TextField(blank=True, verbose_name="Public Transport Overview (Vietnamese)") metro_system_en = models.TextField(blank=True, verbose_name="Metro/Subway System (English)") metro_system_vi = models.TextField(blank=True, verbose_name="Metro/Subway System (Vietnamese)") bus_system_en = models.TextField(blank=True, verbose_name="Bus System (English)") bus_system_vi = models.TextField(blank=True, verbose_name="Bus System (Vietnamese)") taxi_ridehailing_en = models.TextField(blank=True, verbose_name="Taxi & Ride-hailing (English)") taxi_ridehailing_vi = models.TextField(blank=True, verbose_name="Taxi & Ride-hailing (Vietnamese)")
# Vehicle rental and intercity travel car_rental_info_en = models.TextField(blank=True, verbose_name="Car Rental Information (English)") car_rental_info_vi = models.TextField(blank=True, verbose_name="Car Rental Information (Vietnamese)") scooter_rental_info_en = models.TextField(blank=True, verbose_name="Scooter/Bike Rental (English)") scooter_rental_info_vi = models.TextField(blank=True, verbose_name="Scooter/Bike Rental (Vietnamese)") train_system_en = models.TextField(blank=True, verbose_name="Train System (English)") train_system_vi = models.TextField(blank=True, verbose_name="Train System (Vietnamese)") intercity_travel_en = models.TextField(blank=True, verbose_name="Intercity Travel Options (English)") intercity_travel_vi = models.TextField(blank=True, verbose_name="Intercity Travel Options (Vietnamese)")
# ================================ # 🛟 SAFETY & HEALTH # ================================ safety_overview_en = models.TextField(blank=True, verbose_name="Safety Overview (English)") safety_overview_vi = models.TextField(blank=True, verbose_name="Safety Overview (Vietnamese)") safety_rating = models.CharField(max_length=20, choices=[ ('very_safe', 'Very Safe'), ('safe', 'Safe'), ('moderately_safe', 'Moderately Safe'), ('caution_advised', 'Caution Advised'), ('high_risk', 'High Risk') ], blank=True, verbose_name="Safety Rating")
# Common issues and scams common_scams_en = models.TextField(blank=True, verbose_name="Common Scams (English)") common_scams_vi = models.TextField(blank=True, verbose_name="Common Scams (Vietnamese)") safety_tips_en = models.TextField(blank=True, verbose_name="Safety Tips (English)") safety_tips_vi = models.TextField(blank=True, verbose_name="Safety Tips (Vietnamese)")
# Health and medical health_precautions_en = models.TextField(blank=True, verbose_name="Health Precautions (English)") health_precautions_vi = models.TextField(blank=True, verbose_name="Health Precautions (Vietnamese)") medical_facilities_en = models.TextField(blank=True, verbose_name="Medical Facilities (English)") medical_facilities_vi = models.TextField(blank=True, verbose_name="Medical Facilities (Vietnamese)") travel_insurance_tips_en = models.TextField(blank=True, verbose_name="Travel Insurance Tips (English)") travel_insurance_tips_vi = models.TextField(blank=True, verbose_name="Travel Insurance Tips (Vietnamese)")
# Emergency information emergency_numbers = models.TextField(blank=True, help_text="JSON format: {'police': '911', 'fire': '911', 'medical': '911'}") emergency_contacts_en = models.TextField(blank=True, verbose_name="Emergency Contacts (English)") emergency_contacts_vi = models.TextField(blank=True, verbose_name="Emergency Contacts (Vietnamese)")
# ================================ # 🌤️ CLIMATE & PACKING # ================================ climate_overview_en = models.TextField(blank=True, verbose_name="Climate Overview (English)") climate_overview_vi = models.TextField(blank=True, verbose_name="Climate Overview (Vietnamese)") seasons_description_en = models.TextField(blank=True, verbose_name="Seasons Description (English)") seasons_description_vi = models.TextField(blank=True, verbose_name="Seasons Description (Vietnamese)") best_time_to_visit_en = models.CharField(max_length=100, blank=True, verbose_name="Best Time to Visit (English)") best_time_to_visit_vi = models.CharField(max_length=100, blank=True, verbose_name="Best Time to Visit (Vietnamese)")
# Weather patterns rainy_season_en = models.CharField(max_length=100, blank=True, verbose_name="Rainy Season (English)") rainy_season_vi = models.CharField(max_length=100, blank=True, verbose_name="Rainy Season (Vietnamese)") dry_season_en = models.CharField(max_length=100, blank=True, verbose_name="Dry Season (English)") dry_season_vi = models.CharField(max_length=100, blank=True, verbose_name="Dry Season (Vietnamese)") temperature_range = models.CharField(max_length=50, blank=True, verbose_name="Temperature Range") humidity_levels = models.CharField(max_length=50, blank=True, verbose_name="Humidity Levels")
# Packing recommendations packing_essentials_en = models.TextField(blank=True, verbose_name="Packing Essentials (English)") packing_essentials_vi = models.TextField(blank=True, verbose_name="Packing Essentials (Vietnamese)") clothing_recommendations_en = models.TextField(blank=True, verbose_name="Clothing Recommendations (English)") clothing_recommendations_vi = models.TextField(blank=True, verbose_name="Clothing Recommendations (Vietnamese)") cultural_dress_code_en = models.TextField(blank=True, verbose_name="Cultural Dress Code (English)") cultural_dress_code_vi = models.TextField(blank=True, verbose_name="Cultural Dress Code (Vietnamese)") prohibited_items_en = models.TextField(blank=True, verbose_name="Prohibited Items (English)") prohibited_items_vi = models.TextField(blank=True, verbose_name="Prohibited Items (Vietnamese)")
# ================================ # 🎯 ADDITIONAL FEATURES # ================================ travel_style_recommendations_en = models.TextField(blank=True, verbose_name="Travel Style Recommendations (English)") travel_style_recommendations_vi = models.TextField(blank=True, verbose_name="Travel Style Recommendations (Vietnamese)") solo_travel_tips_en = models.TextField(blank=True, verbose_name="Solo Travel Tips (English)") solo_travel_tips_vi = models.TextField(blank=True, verbose_name="Solo Travel Tips (Vietnamese)") family_travel_tips_en = models.TextField(blank=True, verbose_name="Family Travel Tips (English)") family_travel_tips_vi = models.TextField(blank=True, verbose_name="Family Travel Tips (Vietnamese)") backpacker_tips_en = models.TextField(blank=True, verbose_name="Backpacker Tips (English)") backpacker_tips_vi = models.TextField(blank=True, verbose_name="Backpacker Tips (Vietnamese)")
# Country comparison and ranking tourism_ranking = models.PositiveIntegerField(blank=True, null=True, verbose_name="Global Tourism Ranking") ease_of_travel_score = models.DecimalField(max_digits=3, decimal_places=1, blank=True, null=True, validators=[MinValueValidator(0), MaxValueValidator(10)], verbose_name="Ease of Travel Score (1-10)") english_proficiency = models.CharField(max_length=20, choices=[ ('excellent', 'Excellent'), ('good', 'Good'), ('moderate', 'Moderate'), ('limited', 'Limited'), ('very_limited', 'Very Limited') ], blank=True, verbose_name="English Proficiency Level")
# Special notes and recommendations special_notes_en = models.TextField(blank=True, verbose_name="Special Notes (English)") special_notes_vi = models.TextField(blank=True, verbose_name="Special Notes (Vietnamese)") insider_tips_en = models.TextField(blank=True, verbose_name="Insider Tips (English)") insider_tips_vi = models.TextField(blank=True, verbose_name="Insider Tips (Vietnamese)")
# ================================ # 📊 ADMINISTRATIVE # ================================ created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True) is_popular = models.BooleanField(default=False, help_text="Mark as popular destination country") featured_order = models.PositiveIntegerField(default=0, help_text="Order for featured countries") is_complete_profile = models.BooleanField(default=False, verbose_name="Complete Profile", help_text="Mark if country profile is fully completed") profile_completion_percentage = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(100)], verbose_name="Profile Completion %")
class Meta: verbose_name = _("Country") verbose_name_plural = _("Countries") verbose_name_plural = "Countries" ordering = ['name_en']
def __str__(self): return self.name_en
def get_absolute_url(self): return reverse('destinations:country_detail', args=[str(self.id)])
|
2 |
class DestinationCategory(models.Model): """Categories for destinations (Beach, Mountain, City, etc.)""" name_en = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True) description_en = models.TextField(blank=True) description_vi = models.TextField(blank=True) icon = models.CharField(max_length=50, blank=True, help_text="Font Awesome icon class") color = models.CharField(max_length=7, default="#007bff", help_text="Hex color code")
def __str__(self): return self.name_en
class Meta: verbose_name_plural = "Destination Categories"
|
2.1 |
class Destination(models.Model): """Enhanced destination model with country relationship and TripAdvisor-style features"""
# Basic relationships location = models.OneToOneField(Location, on_delete=models.CASCADE, limit_choices_to={'type': 'city'}) country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='destinations', null=True, blank=True) categories = models.ManyToManyField(DestinationCategory, blank=True, help_text="Beach, Mountain, City, etc.")
# Basic info title_en = models.CharField(max_length=200) title_vi = models.CharField(max_length=200, blank=True) description_en = models.TextField() description_vi = models.TextField(blank=True) short_description_en = models.CharField(max_length=300, blank=True, help_text="For cards and previews") short_description_vi = models.CharField(max_length=300, blank=True)
# Images main_image = models.ImageField(upload_to='destinations/', blank=True, null=True) hero_image = models.ImageField(upload_to='destinations/hero/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs")
# TripAdvisor-style ratings and rankings overall_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) total_reviews = models.PositiveIntegerField(default=0) ranking_in_country = models.PositiveIntegerField(blank=True, null=True) ranking_worldwide = models.PositiveIntegerField(blank=True, null=True) traveler_choice_award = models.BooleanField(default=False)
# Visit statistics annual_visitors = models.PositiveIntegerField(blank=True, null=True, help_text="Annual visitor count") peak_season_months = models.CharField(max_length=100, blank=True, help_text="e.g., June-August") off_season_months = models.CharField(max_length=100, blank=True, help_text="e.g., December-February")
# Local destination-specific overrides (if different from country) local_currency_notes_en = models.TextField(blank=True, help_text="Destination-specific currency info") local_currency_notes_vi = models.TextField(blank=True) exchange_rate_note_en = models.TextField(blank=True) exchange_rate_note_vi = models.TextField(blank=True) payment_methods_en = models.CharField(max_length=200, blank=True) payment_methods_vi = models.CharField(max_length=200, blank=True) atm_info_en = models.TextField(blank=True) atm_info_vi = models.TextField(blank=True)
# Local cultural info local_customs_en = models.TextField(blank=True, help_text="City/region specific customs") local_customs_vi = models.TextField(blank=True) local_etiquette_en = models.TextField(blank=True) local_etiquette_vi = models.TextField(blank=True) local_language_notes_en = models.TextField(blank=True, help_text="Local dialects, language tips") local_language_notes_vi = models.TextField(blank=True)
# Destination-specific practical info tipping_culture_en = models.TextField(blank=True) tipping_culture_vi = models.TextField(blank=True) local_emergency_contacts = models.TextField(blank=True, help_text="JSON format for local emergency numbers") hospital_info_en = models.TextField(blank=True) hospital_info_vi = models.TextField(blank=True)
# Climate and timing climate_info_en = models.TextField(blank=True) climate_info_vi = models.TextField(blank=True) best_time_to_visit_en = models.CharField(max_length=100, blank=True) best_time_to_visit_vi = models.CharField(max_length=100, blank=True) weather_highlights_en = models.TextField(blank=True) weather_highlights_vi = models.TextField(blank=True)
# Technology and connectivity sim_info_en = models.TextField(blank=True) sim_info_vi = models.TextField(blank=True) wifi_availability_en = models.TextField(blank=True) wifi_availability_vi = models.TextField(blank=True) connectivity_info_en = models.TextField(blank=True) connectivity_info_vi = models.TextField(blank=True)
# Accessibility accessibility_info_en = models.TextField(blank=True) accessibility_info_vi = models.TextField(blank=True) wheelchair_friendly = models.BooleanField(default=False) accessibility_rating = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1), MaxValueValidator(5)])
# Transportation getting_there_en = models.TextField(blank=True, help_text="How to reach this destination") getting_there_vi = models.TextField(blank=True) getting_around_en = models.TextField(blank=True, help_text="Local transportation options") getting_around_vi = models.TextField(blank=True) airport_info_en = models.TextField(blank=True) airport_info_vi = models.TextField(blank=True)
# Budget information budget_range = models.CharField(max_length=20, choices=[ ('budget', 'Budget ($)'), ('moderate', 'Moderate ($$)'), ('expensive', 'Expensive ($$$)'), ('luxury', 'Luxury ($$$$)') ], blank=True) daily_budget_low = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) daily_budget_mid = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) daily_budget_high = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) budget_tips_en = models.TextField(blank=True) budget_tips_vi = models.TextField(blank=True)
# SEO and metadata meta_description_en = models.CharField(max_length=160, blank=True) meta_description_vi = models.CharField(max_length=160, blank=True) keywords_en = models.CharField(max_length=200, blank=True, help_text="Comma-separated keywords") keywords_vi = models.CharField(max_length=200, blank=True)
# Administrative is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) is_published = models.BooleanField(default=True) featured_order = models.PositiveIntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: verbose_name = _("Destination") verbose_name_plural = _("Destinations") ordering = ['-is_featured', '-overall_rating', 'title_en'] indexes = [ models.Index(fields=['country', 'is_published']), models.Index(fields=['overall_rating', 'total_reviews']), models.Index(fields=['is_featured', 'is_popular']), ]
def __str__(self): return self.title_en
def get_absolute_url(self): """Return the URL to the destination detail page.""" return reverse('destinations:destination_detail', args=[str(self.id)])
|
|
|
3 |
class WhatToSee(models.Model): """Enhanced attractions model with TripAdvisor-style features""" TYPE_CHOICES = [ ('museum', 'Museum'), ('park', 'Park'), ('landmark', 'Landmark'), ('shopping', 'Shopping'), ('nature', 'Nature'), ('religious', 'Religious Site'), ('entertainment', 'Entertainment'), ('beach', 'Beach'), ('viewpoint', 'Viewpoint'), ('historical', 'Historical Site'), ('cultural', 'Cultural Site'), ('adventure', 'Adventure Activity'), ('other', 'Other'), ]
PRICE_RANGE_CHOICES = [ ('free', 'Free'), ('budget', 'Budget ($)'), ('moderate', 'Moderate ($$)'), ('expensive', 'Expensive ($$$)'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='attractions') name_en = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=30, choices=TYPE_CHOICES, default='other') description_en = models.TextField() description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")
# TripAdvisor-style ratings rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) review_count = models.PositiveIntegerField(default=0) ranking = models.PositiveIntegerField(blank=True, null=True, help_text="Ranking in destination")
# Practical information address_en = models.CharField(max_length=300, blank=True) address_vi = models.CharField(max_length=300, blank=True) phone = models.CharField(max_length=50, blank=True) website = models.URLField(blank=True) email = models.EmailField(blank=True)
# Pricing and duration price_range = models.CharField(max_length=20, choices=PRICE_RANGE_CHOICES, blank=True) entry_fee_notes_en = models.TextField(blank=True) entry_fee_notes_vi = models.TextField(blank=True) suggested_duration_hours = models.DecimalField(max_digits=4, decimal_places=1, blank=True, null=True)
# Operating information opening_hours = models.TextField(blank=True, help_text="JSON format for operating hours") best_time_to_visit_en = models.CharField(max_length=200, blank=True) best_time_to_visit_vi = models.CharField(max_length=200, blank=True)
# Features and amenities accessibility_features = models.TextField(blank=True, help_text="JSON array of accessibility features") amenities = models.TextField(blank=True, help_text="JSON array of amenities") languages_spoken = models.CharField(max_length=200, blank=True)
# Media image = models.ImageField(upload_to='attractions/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs") virtual_tour_url = models.URLField(blank=True)
# Tips and notes insider_tips_en = models.TextField(blank=True) insider_tips_vi = models.TextField(blank=True) what_to_bring_en = models.TextField(blank=True) what_to_bring_vi = models.TextField(blank=True)
# Administrative is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-rating', 'name_en'] verbose_name = "Attraction" verbose_name_plural = "Attractions"
def __str__(self): return self.name_en
def get_absolute_url(self): """Return the URL to the destination detail page, scrolled to this attraction.""" return self.destination.get_absolute_url() + f"#attraction-{self.id}"
|
4 |
class WhereToEat(models.Model): """Enhanced restaurant model with TripAdvisor-style features""" TYPE_CHOICES = [ ('restaurant', 'Restaurant'), ('cafe', 'Cafe'), ('street_food', 'Street Food'), ('bar', 'Bar'), ('fast_food', 'Fast Food'), ('fine_dining', 'Fine Dining'), ('food_court', 'Food Court'), ('bakery', 'Bakery'), ('ice_cream', 'Ice Cream'), ('other', 'Other'), ]
CUISINE_CHOICES = [ ('vietnamese', 'Vietnamese'), ('italian', 'Italian'), ('french', 'French'), ('chinese', 'Chinese'), ('japanese', 'Japanese'), ('korean', 'Korean'), ('thai', 'Thai'), ('indian', 'Indian'), ('american', 'American'), ('mediterranean', 'Mediterranean'), ('mexican', 'Mexican'), ('seafood', 'Seafood'), ('vegetarian', 'Vegetarian'), ('vegan', 'Vegan'), ('international', 'International'), ('fusion', 'Fusion'), ('other', 'Other'), ]
PRICE_RANGE_CHOICES = [ ('budget', 'Budget ($)'), ('moderate', 'Moderate ($$)'), ('expensive', 'Expensive ($$$)'), ('luxury', 'Luxury ($$$$)'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='restaurants') name_en = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=30, choices=TYPE_CHOICES, default='restaurant') cuisine = models.CharField(max_length=30, choices=CUISINE_CHOICES, default='other') description_en = models.TextField() description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")
# TripAdvisor-style ratings rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) review_count = models.PositiveIntegerField(default=0) ranking = models.PositiveIntegerField(blank=True, null=True, help_text="Ranking in destination")
# Detailed ratings food_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) service_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) value_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) atmosphere_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)])
# Contact and location address_en = models.CharField(max_length=300, blank=True) address_vi = models.CharField(max_length=300, blank=True) phone = models.CharField(max_length=50, blank=True) website = models.URLField(blank=True) email = models.EmailField(blank=True)
# Pricing and details price_range = models.CharField(max_length=20, choices=PRICE_RANGE_CHOICES, blank=True) average_cost_for_two = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) currency = models.CharField(max_length=10, blank=True)
# Operating information opening_hours = models.TextField(blank=True, help_text="JSON format for operating hours") reservation_required = models.BooleanField(default=False) delivery_available = models.BooleanField(default=False) takeaway_available = models.BooleanField(default=False)
# Features and amenities features = models.TextField(blank=True, help_text="JSON array: outdoor seating, wifi, parking, etc.") dietary_options = models.TextField(blank=True, help_text="JSON array: vegetarian, vegan, gluten-free, etc.") payment_methods = models.CharField(max_length=200, blank=True) languages_spoken = models.CharField(max_length=200, blank=True)
# Special features happy_hour = models.BooleanField(default=False) live_music = models.BooleanField(default=False) outdoor_seating = models.BooleanField(default=False) wifi_available = models.BooleanField(default=False) parking_available = models.BooleanField(default=False) wheelchair_accessible = models.BooleanField(default=False)
# Media image = models.ImageField(upload_to='restaurants/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs") menu_url = models.URLField(blank=True)
# Recommendations signature_dishes_en = models.TextField(blank=True) signature_dishes_vi = models.TextField(blank=True) chef_recommendations_en = models.TextField(blank=True) chef_recommendations_vi = models.TextField(blank=True)
# Administrative is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) traveler_choice = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-rating', 'name_en'] verbose_name = "Restaurant" verbose_name_plural = "Restaurants"
def __str__(self): return self.name_en
def get_absolute_url(self): """Return the URL to the destination detail page, scrolled to this restaurant.""" return self.destination.get_absolute_url() + f"#restaurant-{self.id}"
|
5 |
class WhereToStay(models.Model): """Enhanced accommodation model with TripAdvisor-style features""" TYPE_CHOICES = [ ('hotel', 'Hotel'), ('resort', 'Resort'), ('hostel', 'Hostel'), ('guesthouse', 'Guesthouse'), ('villa', 'Villa'), ('apartment', 'Apartment'), ('homestay', 'Homestay'), ('boutique', 'Boutique Hotel'), ('luxury', 'Luxury Hotel'), ('budget', 'Budget Hotel'), ('spa', 'Spa Resort'), ('eco', 'Eco Lodge'), ('beach', 'Beach Resort'), ('city', 'City Hotel'), ('other', 'Other'), ]
STAR_RATING_CHOICES = [ ('1', '1 Star'), ('2', '2 Stars'), ('3', '3 Stars'), ('4', '4 Stars'), ('5', '5 Stars'), ('unrated', 'Unrated'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='accommodations') name_en = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=30, choices=TYPE_CHOICES, default='hotel') star_rating = models.CharField(max_length=20, choices=STAR_RATING_CHOICES, blank=True, default='unrated') description_en = models.TextField() description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")
# TripAdvisor-style ratings rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) review_count = models.PositiveIntegerField(default=0) ranking = models.PositiveIntegerField(blank=True, null=True, help_text="Ranking in destination")
# Detailed ratings cleanliness_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) service_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) value_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) location_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) comfort_rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)])
# Contact and location address_en = models.CharField(max_length=300, blank=True) address_vi = models.CharField(max_length=300, blank=True) phone = models.CharField(max_length=50, blank=True) website = models.URLField(blank=True) email = models.EmailField(blank=True)
# Pricing information price_per_night_min = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) price_per_night_max = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) currency = models.CharField(max_length=10, blank=True) includes_breakfast = models.BooleanField(default=False) includes_wifi = models.BooleanField(default=False) includes_parking = models.BooleanField(default=False)
# Property details total_rooms = models.PositiveIntegerField(blank=True, null=True) check_in_time = models.TimeField(blank=True, null=True) check_out_time = models.TimeField(blank=True, null=True)
# Amenities and facilities amenities = models.TextField(blank=True, help_text="JSON array: pool, spa, gym, restaurant, etc.") room_features = models.TextField(blank=True, help_text="JSON array: AC, TV, minibar, balcony, etc.") services = models.TextField(blank=True, help_text="JSON array: room service, concierge, laundry, etc.")
# Accessibility and policies wheelchair_accessible = models.BooleanField(default=False) pet_friendly = models.BooleanField(default=False) family_friendly = models.BooleanField(default=False) smoking_allowed = models.BooleanField(default=False) age_restriction = models.CharField(max_length=100, blank=True)
# Hotel facilities has_restaurant = models.BooleanField(default=False) has_bar = models.BooleanField(default=False) has_pool = models.BooleanField(default=False) has_spa = models.BooleanField(default=False) has_gym = models.BooleanField(default=False) has_beach_access = models.BooleanField(default=False) has_airport_shuttle = models.BooleanField(default=False)
# Connectivity wifi_available = models.BooleanField(default=False) wifi_free = models.BooleanField(default=False) business_center = models.BooleanField(default=False)
# Transportation distance_to_city_center = models.CharField(max_length=50, blank=True) distance_to_airport = models.CharField(max_length=50, blank=True) parking_available = models.BooleanField(default=False) parking_free = models.BooleanField(default=False)
# Media image = models.ImageField(upload_to='accommodations/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs") virtual_tour_url = models.URLField(blank=True)
# Awards and certifications awards = models.TextField(blank=True, help_text="JSON array of awards and certifications") eco_certified = models.BooleanField(default=False) covid_safety_measures = models.TextField(blank=True)
# Booking information booking_url = models.URLField(blank=True) instant_booking = models.BooleanField(default=False) free_cancellation = models.BooleanField(default=False) cancellation_policy_en = models.TextField(blank=True) cancellation_policy_vi = models.TextField(blank=True)
# Special features is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) traveler_choice = models.BooleanField(default=False) best_value = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-rating', 'name_en'] verbose_name = "Accommodation" verbose_name_plural = "Accommodations"
def __str__(self): return self.name_en
def get_absolute_url(self): """Return the URL to the destination detail page, scrolled to this accommodation.""" return self.destination.get_absolute_url() + f"#accommodation-{self.id}"
|
6 |
class Event(models.Model): """Enhanced event model with TripAdvisor-style features""" TYPE_CHOICES = [ ('festival', 'Festival'), ('concert', 'Concert'), ('exhibition', 'Exhibition'), ('show', 'Show'), ('sports', 'Sports Event'), ('cultural', 'Cultural Event'), ('seasonal', 'Seasonal Event'), ('food', 'Food & Wine'), ('nightlife', 'Nightlife'), ('outdoor', 'Outdoor Activity'), ('family', 'Family Event'), ('business', 'Business Event'), ('religious', 'Religious Event'), ('market', 'Market/Fair'), ('other', 'Other'), ]
FREQUENCY_CHOICES = [ ('one_time', 'One-time Event'), ('daily', 'Daily'), ('weekly', 'Weekly'), ('monthly', 'Monthly'), ('seasonal', 'Seasonal'), ('annual', 'Annual'), ('irregular', 'Irregular'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='events') name_en = models.CharField(max_length=200) name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=30, choices=TYPE_CHOICES, default='other') description_en = models.TextField() description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")
# TripAdvisor-style ratings rating = models.DecimalField(max_digits=3, decimal_places=2, default=0.0, validators=[MinValueValidator(0), MaxValueValidator(5)]) review_count = models.PositiveIntegerField(default=0)
# Event scheduling start_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True) start_time = models.TimeField(blank=True, null=True) end_time = models.TimeField(blank=True, null=True) frequency = models.CharField(max_length=20, choices=FREQUENCY_CHOICES, default='one_time') duration = models.CharField(max_length=100, blank=True, help_text="e.g., 2 hours, 3 days")
# Location details venue_name_en = models.CharField(max_length=200, blank=True) venue_name_vi = models.CharField(max_length=200, blank=True) address_en = models.CharField(max_length=300, blank=True) address_vi = models.CharField(max_length=300, blank=True)
# Contact and booking website = models.URLField(blank=True) phone = models.CharField(max_length=50, blank=True) email = models.EmailField(blank=True) booking_required = models.BooleanField(default=False) booking_url = models.URLField(blank=True)
# Pricing is_free = models.BooleanField(default=False) price_min = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) price_max = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) currency = models.CharField(max_length=10, blank=True) price_details_en = models.TextField(blank=True) price_details_vi = models.TextField(blank=True)
# Event features age_restrictions = models.CharField(max_length=100, blank=True) dress_code = models.CharField(max_length=100, blank=True) languages = models.CharField(max_length=200, blank=True, help_text="Languages supported") accessibility_info_en = models.TextField(blank=True) accessibility_info_vi = models.TextField(blank=True)
# Event amenities food_available = models.BooleanField(default=False) drinks_available = models.BooleanField(default=False) parking_available = models.BooleanField(default=False) photography_allowed = models.BooleanField(default=True) wheelchair_accessible = models.BooleanField(default=False)
# Weather dependency indoor_event = models.BooleanField(default=False) weather_dependent = models.BooleanField(default=False) rain_alternative_en = models.TextField(blank=True) rain_alternative_vi = models.TextField(blank=True)
# Media image = models.ImageField(upload_to='events/', blank=True, null=True) gallery_images = models.TextField(blank=True, help_text="JSON array of image URLs")
# Event highlights highlights_en = models.TextField(blank=True) highlights_vi = models.TextField(blank=True) what_to_expect_en = models.TextField(blank=True) what_to_expect_vi = models.TextField(blank=True) what_to_bring_en = models.TextField(blank=True) what_to_bring_vi = models.TextField(blank=True)
# Popularity indicators is_featured = models.BooleanField(default=False) is_popular = models.BooleanField(default=False) traveler_choice = models.BooleanField(default=False) sold_out = models.BooleanField(default=False)
# Administrative created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-start_date', '-rating', 'name_en'] verbose_name = "Event" verbose_name_plural = "Events"
def __str__(self): return self.name_en
def get_absolute_url(self): """Return the URL to the destination detail page, scrolled to this event.""" return self.destination.get_absolute_url() + f"#event-{self.id}"
@property def is_upcoming(self): """Check if event is upcoming""" if self.start_date: from django.utils import timezone return self.start_date >= timezone.now().date() return False
@property def is_ongoing(self): """Check if event is currently ongoing""" if self.start_date and self.end_date: from django.utils import timezone today = timezone.now().date() return self.start_date <= today <= self.end_date return False
|
7 |
class SafetyTip(models.Model): destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='safety_tips') tip_en = models.TextField() tip_vi = models.TextField(blank=True, verbose_name="Vietnamese Safety Tip")
def __str__(self): return self.tip_en[:50]
|
8 |
class UserReview(models.Model): """User reviews for destinations and related items""" REVIEW_TYPE_CHOICES = [ ('destination', 'Destination'), ('attraction', 'Attraction'), ('restaurant', 'Restaurant'), ('accommodation', 'Accommodation'), ('event', 'Event'), ]
TRAVELER_TYPE_CHOICES = [ ('family', 'Family'), ('couple', 'Couple'), ('solo', 'Solo Traveler'), ('business', 'Business'), ('friends', 'Friends'), ('other', 'Other'), ]
VISIT_TYPE_CHOICES = [ ('first_time', 'First Time'), ('repeat', 'Repeat Visitor'), ]
# Review target (polymorphic relationship) review_type = models.CharField(max_length=20, choices=REVIEW_TYPE_CHOICES) destination = models.ForeignKey(Destination, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews') attraction = models.ForeignKey(WhatToSee, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews') restaurant = models.ForeignKey(WhereToEat, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews') accommodation = models.ForeignKey(WhereToStay, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews') event = models.ForeignKey(Event, on_delete=models.CASCADE, null=True, blank=True, related_name='user_reviews')
# Reviewer information user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='destination_reviews') reviewer_name = models.CharField(max_length=100) reviewer_location = models.CharField(max_length=100, blank=True) traveler_type = models.CharField(max_length=20, choices=TRAVELER_TYPE_CHOICES, blank=True) visit_type = models.CharField(max_length=20, choices=VISIT_TYPE_CHOICES, blank=True)
# Review content title = models.CharField(max_length=200) review_text = models.TextField()
# Ratings overall_rating = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
# Specific ratings (for different review types) service_rating = models.PositiveIntegerField(null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)]) value_rating = models.PositiveIntegerField(null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)]) location_rating = models.PositiveIntegerField(null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)]) cleanliness_rating = models.PositiveIntegerField(null=True, blank=True, validators=[MinValueValidator(1), MaxValueValidator(5)])
# Visit details visit_date = models.DateField(null=True, blank=True) stay_duration = models.CharField(max_length=50, blank=True, help_text="e.g., 3 days, 1 week")
# Review metadata helpful_votes = models.PositiveIntegerField(default=0) total_votes = models.PositiveIntegerField(default=0) is_verified = models.BooleanField(default=False) is_featured = models.BooleanField(default=False)
# Administrative created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True) is_published = models.BooleanField(default=True)
class Meta: ordering = ['-created_at'] unique_together = [['user', 'review_type', 'destination'], ['user', 'review_type', 'attraction'], ['user', 'review_type', 'restaurant'], ['user', 'review_type', 'accommodation'], ['user', 'review_type', 'event']] verbose_name = "User Review" verbose_name_plural = "User Reviews"
def __str__(self): return f"{self.title} by {self.reviewer_name}"
@property def helpful_percentage(self): """Calculate helpful vote percentage""" if self.total_votes > 0: return round((self.helpful_votes / self.total_votes) * 100, 1) return 0
|
9 |
class TravelTip(models.Model): """Travel tips and insider advice for destinations""" TIP_CATEGORY_CHOICES = [ ('general', 'General Tips'), ('transportation', 'Transportation'), ('accommodation', 'Accommodation'), ('food', 'Food & Dining'), ('shopping', 'Shopping'), ('culture', 'Culture & Customs'), ('safety', 'Safety'), ('money', 'Money & Costs'), ('weather', 'Weather & Climate'), ('activities', 'Activities'), ('photography', 'Photography'), ('health', 'Health & Medical'), ('packing', 'Packing'), ('communication', 'Communication'), ('etiquette', 'Local Etiquette'), ]
destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='travel_tips') category = models.CharField(max_length=30, choices=TIP_CATEGORY_CHOICES) title_en = models.CharField(max_length=200) title_vi = models.CharField(max_length=200, blank=True) tip_en = models.TextField() tip_vi = models.TextField(blank=True)
# Tip metadata is_insider_tip = models.BooleanField(default=False) is_featured = models.BooleanField(default=False) helpful_votes = models.PositiveIntegerField(default=0)
# Author information author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True) author_name = models.CharField(max_length=100, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True) updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta: ordering = ['-is_featured', '-helpful_votes', '-created_at'] verbose_name = "Travel Tip" verbose_name_plural = "Travel Tips"
def __str__(self): return f"{self.title_en} ({self.get_category_display()})"
|
10 |
class UsefulPhrase(models.Model): destination = models.ForeignKey(Destination, on_delete=models.CASCADE, related_name='phrases') phrase_en = models.CharField(max_length=200, default='', verbose_name="English Phrase") phrase_vi = models.CharField(max_length=200, default='', verbose_name="Vietnamese Phrase") translation = models.CharField(max_length=200, blank=True, verbose_name="Pronunciation Guide") note_en = models.CharField(max_length=200, blank=True, verbose_name="English Note") note_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Note")
def __str__(self): return f"{self.phrase_en} - {self.phrase_vi}"
|
Attached Files
You are viewing this article in public mode. Some features may be limited.