Classified ads Models
Classified ads Models (English fallback)
Aug. 16, 2025
Posted by admin
Nhom |
Notes |
|
from django.db import models from django.contrib.auth.models import User from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ from ckeditor_uploader.fields import RichTextUploadingField
|
|
|
|
class AdStatus(models.TextChoices): DRAFT = 'draft', 'Draft' ACTIVE = 'active', 'Active' SOLD = 'sold', 'Sold' EXPIRED = 'expired', 'Expired' SUSPENDED = 'suspended', 'Suspended'
class AdType(models.TextChoices): SELL = 'sell', 'For Sale' BUY = 'buy', 'Looking to Buy' RENT = 'rent', 'For Rent' SERVICE = 'service', 'Service' JOB = 'job', 'Job'
|
|
class Location(models.Model): """Predefined locations for classified ads""" name = models.CharField(max_length=100) slug = models.SlugField(unique=True) state_province = models.CharField(max_length=50, blank=True) country = models.CharField(max_length=50, default='Vietnam') is_popular = models.BooleanField(default=False, help_text="Show in popular locations") is_active = models.BooleanField(default=True) order = models.PositiveIntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True)
class Meta: verbose_name = _("Ad Location") verbose_name_plural = _("Ad Locations") ordering = ['order', 'name'] verbose_name_plural = "Locations"
def __str__(self): if self.state_province: return f"{self.name}, {self.state_province}" return self.name
def get_ads_count(self): """Get count of active ads in this location""" return ClassifiedAd.objects.filter( status=AdStatus.ACTIVE, location__icontains=self.name ).count()
|
Ads |
class ClassifiedAd(models.Model): """Main classified ad model""" TRANSLATION_STATUS_CHOICES = [ ('pending', 'Pending Translation'), ('translated', 'Translated'), ('reviewed', 'Reviewed'), ]
title = models.CharField(max_length=200) title_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Title") description = RichTextUploadingField() description_vi = RichTextUploadingField(blank=True, verbose_name="Vietnamese Description") category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='ads') ad_type = models.CharField(max_length=20, choices=AdType.choices, default=AdType.SELL)
# User information user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='classified_ads') contact_name = models.CharField(max_length=100, blank=True) contact_email = models.EmailField(blank=True) contact_phone = models.CharField(max_length=20, blank=True)
# Location location = models.CharField(max_length=200, blank=True)
# Pricing price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) price_negotiable = models.BooleanField(default=False)
# Status and timing status = models.CharField(max_length=20, choices=AdStatus.choices, default=AdStatus.DRAFT) featured = models.BooleanField(default=False) expires_at = models.DateTimeField(null=True, blank=True)
# Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) published_at = models.DateTimeField(null=True, blank=True)
# Vietnamese translation fields translation_status = models.CharField( max_length=20, choices=TRANSLATION_STATUS_CHOICES, default='pending', help_text="Status of Vietnamese translation" ) translated_by = models.CharField(max_length=100, blank=True, help_text="Name of translator") translated_at = models.DateTimeField(null=True, blank=True, help_text="When translation was completed")
# Counters view_count = models.PositiveIntegerField(default=0)
class Meta: ordering = ['-created_at'] indexes = [ models.Index(fields=['status']), models.Index(fields=['category']), models.Index(fields=['created_at']), models.Index(fields=['featured']), ]
def __str__(self): return self.title
def get_display_title(self, language='en'): """Get ad title in specified language""" if language == 'vi' and self.title_vi: return self.title_vi return self.title
def get_display_description(self, language='en'): """Get ad description in specified language""" if language == 'vi' and self.description_vi: return self.description_vi return self.description
def is_translated(self): """Check if ad has Vietnamese translation""" return bool(self.title_vi and self.description_vi)
def save(self, *args, **kwargs): if self.status == AdStatus.ACTIVE and not self.published_at: self.published_at = timezone.now() super().save(*args, **kwargs)
def get_absolute_url(self): return reverse('classified_ads:detail', kwargs={'pk': self.pk})
def is_expired(self): if self.expires_at and timezone.now() > self.expires_at: return True return False
@property def days_since_published(self): if self.published_at: return (timezone.now() - self.published_at).days return 0
|
Ads Category |
class Category(models.Model): """Categories for classified ads""" TRANSLATION_STATUS_CHOICES = [ ('pending', 'Pending Translation'), ('translated', 'Translated'), ('reviewed', 'Reviewed'), ]
name = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name") description = models.TextField(blank=True) description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description") parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children') icon = models.CharField(max_length=50, blank=True, help_text="Font Awesome icon class (e.g., fas fa-car)") slug = models.SlugField(max_length=255, unique=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True)
# Vietnamese translation fields translation_status = models.CharField( max_length=20, choices=TRANSLATION_STATUS_CHOICES, default='pending', help_text="Status of Vietnamese translation" ) translated_by = models.CharField(max_length=100, blank=True, help_text="Name of translator") translated_at = models.DateTimeField(null=True, blank=True, help_text="When translation was completed")
class Meta: verbose_name = _("Ad Category") verbose_name_plural = _("Ad Categories") verbose_name_plural = "Categories" ordering = ['name']
def __str__(self): return self.name
def get_display_name(self, language='en'): """Get category name in specified language""" if language == 'vi' and self.name_vi: return self.name_vi return self.name
def get_display_description(self, language='en'): """Get category description in specified language""" if language == 'vi' and self.description_vi: return self.description_vi return self.description
def is_translated(self): """Check if category has Vietnamese translation""" return bool(self.name_vi and self.description_vi)
def get_absolute_url(self): return reverse('classified_ads:category', kwargs={'slug': self.slug})
|
Ads favorite |
class Favorite(models.Model): """User favorites""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='favorites') ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='favorited_by') created_at = models.DateTimeField(auto_now_add=True)
class Meta: verbose_name = _("Favorite Ad") verbose_name_plural = _("Favorite Ads") unique_together = ['user', 'ad'] ordering = ['-created_at']
def __str__(self): return f"{self.user.username} - {self.ad.title}"
|
Ads Image |
class AdImage(models.Model): """Images for classified ads""" ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='images') image = models.ImageField(upload_to='classified_ads/images/%Y/%m/') caption = models.CharField(max_length=200, blank=True) is_primary = models.BooleanField(default=False) order = models.PositiveIntegerField(default=0) uploaded_at = models.DateTimeField(auto_now_add=True)
class Meta: ordering = ['order', 'uploaded_at']
def __str__(self): return f"Image for {self.ad.title}"
|
Ads inquiry |
class AdInquiry(models.Model): """Inquiries about ads""" ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='inquiries') name = models.CharField(max_length=100) email = models.EmailField() phone = models.CharField(max_length=20, blank=True) message = models.TextField() created_at = models.DateTimeField(auto_now_add=True) is_read = models.BooleanField(default=False)
class Meta: ordering = ['-created_at'] verbose_name_plural = "Ad Inquiries"
def __str__(self): return f"Inquiry for {self.ad.title} from {self.name}"
|
Ads Promotion |
class AdPromotion(models.Model): """Paid promotions for ads""" PROMOTION_TYPES = ( ('featured', 'Featured Ad - $5/week'), ('top_listing', 'Top Listing - $10/week'), ('homepage_banner', 'Homepage Banner - $25/week'), ('category_banner', 'Category Banner - $15/week'), ('urgent', 'Urgent Ad - $3/week'), ('highlight', 'Highlighted Ad - $2/week'), )
PAYMENT_STATUS = ( ('pending', 'Pending'), ('completed', 'Completed'), ('failed', 'Failed'), ('refunded', 'Refunded'), )
ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='promotions') promotion_type = models.CharField(max_length=20, choices=PROMOTION_TYPES) amount = models.DecimalField(max_digits=10, decimal_places=2) duration_days = models.PositiveIntegerField(default=7)
payment_status = models.CharField(max_length=20, choices=PAYMENT_STATUS, default='pending') payment_method = models.CharField(max_length=50, blank=True) payment_id = models.CharField(max_length=100, blank=True)
start_date = models.DateTimeField() end_date = models.DateTimeField() is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: ordering = ['-created_at']
def __str__(self): return f"{self.ad.title} - {self.get_promotion_type_display()}"
def save(self, *args, **kwargs): # Auto-calculate end_date if not self.end_date and self.start_date: from datetime import timedelta self.end_date = self.start_date + timedelta(days=self.duration_days) super().save(*args, **kwargs)
|
Ads Revenue |
class PlatformRevenue(models.Model): """Track platform revenue streams""" REVENUE_TYPES = ( ('subscription', 'User Subscription'), ('promotion', 'Ad Promotion'), ('commission', 'Transaction Commission'), ('featured_listing', 'Featured Listing Fee'), ('banner_ad', 'Banner Advertisement'), ('premium_placement', 'Premium Placement'), )
revenue_type = models.CharField(max_length=20, choices=REVENUE_TYPES) amount = models.DecimalField(max_digits=10, decimal_places=2) currency = models.CharField(max_length=3, default='USD')
# Related objects user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) ad = models.ForeignKey(ClassifiedAd, on_delete=models.SET_NULL, null=True, blank=True) promotion = models.ForeignKey(AdPromotion, on_delete=models.SET_NULL, null=True, blank=True)
# Payment details payment_method = models.CharField(max_length=50) payment_id = models.CharField(max_length=100, blank=True) transaction_fee = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) net_amount = models.DecimalField(max_digits=10, decimal_places=2)
description = models.TextField(blank=True) date_created = models.DateTimeField(auto_now_add=True)
class Meta: ordering = ['-date_created']
def __str__(self): return f"{self.get_revenue_type_display()} - ${self.amount}"
def save(self, *args, **kwargs): # Calculate net amount after transaction fees if not self.net_amount: self.net_amount = self.amount - self.transaction_fee super().save(*args, **kwargs)
|
Ads Statistic |
class AdStatistics(models.Model): """Track ad performance for analytics""" ad = models.OneToOneField(ClassifiedAd, on_delete=models.CASCADE, related_name='statistics')
# View statistics total_views = models.PositiveIntegerField(default=0) unique_views = models.PositiveIntegerField(default=0) views_today = models.PositiveIntegerField(default=0) views_this_week = models.PositiveIntegerField(default=0) views_this_month = models.PositiveIntegerField(default=0)
# Contact statistics total_contacts = models.PositiveIntegerField(default=0) phone_clicks = models.PositiveIntegerField(default=0) email_clicks = models.PositiveIntegerField(default=0) inquiries_received = models.PositiveIntegerField(default=0)
# Social sharing shares_count = models.PositiveIntegerField(default=0) favorites_count = models.PositiveIntegerField(default=0)
# Revenue tracking revenue_generated = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) promotion_spent = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
last_updated = models.DateTimeField(auto_now=True)
def __str__(self): return f"Stats for {self.ad.title}"
|
Ads View |
class AdView(models.Model): """Track ad views""" ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='ad_views') ip_address = models.GenericIPAddressField() user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) viewed_at = models.DateTimeField(auto_now_add=True)
class Meta: unique_together = ['ad', 'ip_address'] ordering = ['-viewed_at']
|
Nhom |
Notes |
|
from django.db import models from django.contrib.auth.models import User from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ from ckeditor_uploader.fields import RichTextUploadingField
|
|
|
|
class AdStatus(models.TextChoices): DRAFT = 'draft', 'Draft' ACTIVE = 'active', 'Active' SOLD = 'sold', 'Sold' EXPIRED = 'expired', 'Expired' SUSPENDED = 'suspended', 'Suspended'
class AdType(models.TextChoices): SELL = 'sell', 'For Sale' BUY = 'buy', 'Looking to Buy' RENT = 'rent', 'For Rent' SERVICE = 'service', 'Service' JOB = 'job', 'Job'
|
|
class Location(models.Model): """Predefined locations for classified ads""" name = models.CharField(max_length=100) slug = models.SlugField(unique=True) state_province = models.CharField(max_length=50, blank=True) country = models.CharField(max_length=50, default='Vietnam') is_popular = models.BooleanField(default=False, help_text="Show in popular locations") is_active = models.BooleanField(default=True) order = models.PositiveIntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True)
class Meta: verbose_name = _("Ad Location") verbose_name_plural = _("Ad Locations") ordering = ['order', 'name'] verbose_name_plural = "Locations"
def __str__(self): if self.state_province: return f"{self.name}, {self.state_province}" return self.name
def get_ads_count(self): """Get count of active ads in this location""" return ClassifiedAd.objects.filter( status=AdStatus.ACTIVE, location__icontains=self.name ).count()
|
Ads |
class ClassifiedAd(models.Model): """Main classified ad model""" TRANSLATION_STATUS_CHOICES = [ ('pending', 'Pending Translation'), ('translated', 'Translated'), ('reviewed', 'Reviewed'), ]
title = models.CharField(max_length=200) title_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Title") description = RichTextUploadingField() description_vi = RichTextUploadingField(blank=True, verbose_name="Vietnamese Description") category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='ads') ad_type = models.CharField(max_length=20, choices=AdType.choices, default=AdType.SELL)
# User information user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='classified_ads') contact_name = models.CharField(max_length=100, blank=True) contact_email = models.EmailField(blank=True) contact_phone = models.CharField(max_length=20, blank=True)
# Location location = models.CharField(max_length=200, blank=True)
# Pricing price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) price_negotiable = models.BooleanField(default=False)
# Status and timing status = models.CharField(max_length=20, choices=AdStatus.choices, default=AdStatus.DRAFT) featured = models.BooleanField(default=False) expires_at = models.DateTimeField(null=True, blank=True)
# Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) published_at = models.DateTimeField(null=True, blank=True)
# Vietnamese translation fields translation_status = models.CharField( max_length=20, choices=TRANSLATION_STATUS_CHOICES, default='pending', help_text="Status of Vietnamese translation" ) translated_by = models.CharField(max_length=100, blank=True, help_text="Name of translator") translated_at = models.DateTimeField(null=True, blank=True, help_text="When translation was completed")
# Counters view_count = models.PositiveIntegerField(default=0)
class Meta: ordering = ['-created_at'] indexes = [ models.Index(fields=['status']), models.Index(fields=['category']), models.Index(fields=['created_at']), models.Index(fields=['featured']), ]
def __str__(self): return self.title
def get_display_title(self, language='en'): """Get ad title in specified language""" if language == 'vi' and self.title_vi: return self.title_vi return self.title
def get_display_description(self, language='en'): """Get ad description in specified language""" if language == 'vi' and self.description_vi: return self.description_vi return self.description
def is_translated(self): """Check if ad has Vietnamese translation""" return bool(self.title_vi and self.description_vi)
def save(self, *args, **kwargs): if self.status == AdStatus.ACTIVE and not self.published_at: self.published_at = timezone.now() super().save(*args, **kwargs)
def get_absolute_url(self): return reverse('classified_ads:detail', kwargs={'pk': self.pk})
def is_expired(self): if self.expires_at and timezone.now() > self.expires_at: return True return False
@property def days_since_published(self): if self.published_at: return (timezone.now() - self.published_at).days return 0
|
Ads Category |
class Category(models.Model): """Categories for classified ads""" TRANSLATION_STATUS_CHOICES = [ ('pending', 'Pending Translation'), ('translated', 'Translated'), ('reviewed', 'Reviewed'), ]
name = models.CharField(max_length=100) name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name") description = models.TextField(blank=True) description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description") parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children') icon = models.CharField(max_length=50, blank=True, help_text="Font Awesome icon class (e.g., fas fa-car)") slug = models.SlugField(max_length=255, unique=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True)
# Vietnamese translation fields translation_status = models.CharField( max_length=20, choices=TRANSLATION_STATUS_CHOICES, default='pending', help_text="Status of Vietnamese translation" ) translated_by = models.CharField(max_length=100, blank=True, help_text="Name of translator") translated_at = models.DateTimeField(null=True, blank=True, help_text="When translation was completed")
class Meta: verbose_name = _("Ad Category") verbose_name_plural = _("Ad Categories") verbose_name_plural = "Categories" ordering = ['name']
def __str__(self): return self.name
def get_display_name(self, language='en'): """Get category name in specified language""" if language == 'vi' and self.name_vi: return self.name_vi return self.name
def get_display_description(self, language='en'): """Get category description in specified language""" if language == 'vi' and self.description_vi: return self.description_vi return self.description
def is_translated(self): """Check if category has Vietnamese translation""" return bool(self.name_vi and self.description_vi)
def get_absolute_url(self): return reverse('classified_ads:category', kwargs={'slug': self.slug})
|
Ads favorite |
class Favorite(models.Model): """User favorites""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='favorites') ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='favorited_by') created_at = models.DateTimeField(auto_now_add=True)
class Meta: verbose_name = _("Favorite Ad") verbose_name_plural = _("Favorite Ads") unique_together = ['user', 'ad'] ordering = ['-created_at']
def __str__(self): return f"{self.user.username} - {self.ad.title}"
|
Ads Image |
class AdImage(models.Model): """Images for classified ads""" ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='images') image = models.ImageField(upload_to='classified_ads/images/%Y/%m/') caption = models.CharField(max_length=200, blank=True) is_primary = models.BooleanField(default=False) order = models.PositiveIntegerField(default=0) uploaded_at = models.DateTimeField(auto_now_add=True)
class Meta: ordering = ['order', 'uploaded_at']
def __str__(self): return f"Image for {self.ad.title}"
|
Ads inquiry |
class AdInquiry(models.Model): """Inquiries about ads""" ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='inquiries') name = models.CharField(max_length=100) email = models.EmailField() phone = models.CharField(max_length=20, blank=True) message = models.TextField() created_at = models.DateTimeField(auto_now_add=True) is_read = models.BooleanField(default=False)
class Meta: ordering = ['-created_at'] verbose_name_plural = "Ad Inquiries"
def __str__(self): return f"Inquiry for {self.ad.title} from {self.name}"
|
Ads Promotion |
class AdPromotion(models.Model): """Paid promotions for ads""" PROMOTION_TYPES = ( ('featured', 'Featured Ad - $5/week'), ('top_listing', 'Top Listing - $10/week'), ('homepage_banner', 'Homepage Banner - $25/week'), ('category_banner', 'Category Banner - $15/week'), ('urgent', 'Urgent Ad - $3/week'), ('highlight', 'Highlighted Ad - $2/week'), )
PAYMENT_STATUS = ( ('pending', 'Pending'), ('completed', 'Completed'), ('failed', 'Failed'), ('refunded', 'Refunded'), )
ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='promotions') promotion_type = models.CharField(max_length=20, choices=PROMOTION_TYPES) amount = models.DecimalField(max_digits=10, decimal_places=2) duration_days = models.PositiveIntegerField(default=7)
payment_status = models.CharField(max_length=20, choices=PAYMENT_STATUS, default='pending') payment_method = models.CharField(max_length=50, blank=True) payment_id = models.CharField(max_length=100, blank=True)
start_date = models.DateTimeField() end_date = models.DateTimeField() is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: ordering = ['-created_at']
def __str__(self): return f"{self.ad.title} - {self.get_promotion_type_display()}"
def save(self, *args, **kwargs): # Auto-calculate end_date if not self.end_date and self.start_date: from datetime import timedelta self.end_date = self.start_date + timedelta(days=self.duration_days) super().save(*args, **kwargs)
|
Ads Revenue |
class PlatformRevenue(models.Model): """Track platform revenue streams""" REVENUE_TYPES = ( ('subscription', 'User Subscription'), ('promotion', 'Ad Promotion'), ('commission', 'Transaction Commission'), ('featured_listing', 'Featured Listing Fee'), ('banner_ad', 'Banner Advertisement'), ('premium_placement', 'Premium Placement'), )
revenue_type = models.CharField(max_length=20, choices=REVENUE_TYPES) amount = models.DecimalField(max_digits=10, decimal_places=2) currency = models.CharField(max_length=3, default='USD')
# Related objects user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) ad = models.ForeignKey(ClassifiedAd, on_delete=models.SET_NULL, null=True, blank=True) promotion = models.ForeignKey(AdPromotion, on_delete=models.SET_NULL, null=True, blank=True)
# Payment details payment_method = models.CharField(max_length=50) payment_id = models.CharField(max_length=100, blank=True) transaction_fee = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) net_amount = models.DecimalField(max_digits=10, decimal_places=2)
description = models.TextField(blank=True) date_created = models.DateTimeField(auto_now_add=True)
class Meta: ordering = ['-date_created']
def __str__(self): return f"{self.get_revenue_type_display()} - ${self.amount}"
def save(self, *args, **kwargs): # Calculate net amount after transaction fees if not self.net_amount: self.net_amount = self.amount - self.transaction_fee super().save(*args, **kwargs)
|
Ads Statistic |
class AdStatistics(models.Model): """Track ad performance for analytics""" ad = models.OneToOneField(ClassifiedAd, on_delete=models.CASCADE, related_name='statistics')
# View statistics total_views = models.PositiveIntegerField(default=0) unique_views = models.PositiveIntegerField(default=0) views_today = models.PositiveIntegerField(default=0) views_this_week = models.PositiveIntegerField(default=0) views_this_month = models.PositiveIntegerField(default=0)
# Contact statistics total_contacts = models.PositiveIntegerField(default=0) phone_clicks = models.PositiveIntegerField(default=0) email_clicks = models.PositiveIntegerField(default=0) inquiries_received = models.PositiveIntegerField(default=0)
# Social sharing shares_count = models.PositiveIntegerField(default=0) favorites_count = models.PositiveIntegerField(default=0)
# Revenue tracking revenue_generated = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) promotion_spent = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
last_updated = models.DateTimeField(auto_now=True)
def __str__(self): return f"Stats for {self.ad.title}"
|
Ads View |
class AdView(models.Model): """Track ad views""" ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='ad_views') ip_address = models.GenericIPAddressField() user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) viewed_at = models.DateTimeField(auto_now_add=True)
class Meta: unique_together = ['ad', 'ip_address'] ordering = ['-viewed_at']
|
Attached Files
You are viewing this article in public mode. Some features may be limited.