| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- from django.db import models
- from django.conf import settings
- from django.utils.translation import ugettext as _
- from django.core.urlresolvers import reverse
- from django.contrib.contenttypes.models import ContentType
- from django.contrib.contenttypes import generic
- from django.db.models import get_model
- from django.utils.translation import ugettext_lazy as _
-
- from oscar.models.fields import ExtendedURLField
-
- Product = get_model('catalogue', 'Product')
- Item = get_model('product', 'Item')
-
-
- # Linking models - these link promotions to content (eg pages, or keywords)
-
-
- class LinkedPromotion(models.Model):
-
- # We use generic foreign key to link to a promotion model
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
-
- position = models.CharField(_("Position"), max_length=100, help_text="Position on page")
- display_order = models.PositiveIntegerField(_("Display Order"), default=0)
- clicks = models.PositiveIntegerField(_("Clicks"), default=0)
- date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)
-
- class Meta:
- abstract = True
- ordering = ['-clicks']
- verbose_name = _("Linked Promotion")
- verbose_name_plural = _("Linked Promotions")
-
- def record_click(self):
- self.clicks += 1
- self.save()
-
-
- class PagePromotion(LinkedPromotion):
- """
- A promotion embedded on a particular page.
- """
- page_url = ExtendedURLField(_('Page URL'), max_length=128, db_index=True)
-
- def __unicode__(self):
- return u"%s on %s" % (self.content_object, self.page_url)
-
- def get_link(self):
- return reverse('promotions:page-click', kwargs={'page_promotion_id': self.id})
-
- class Meta:
- verbose_name = _("Page Promotion")
- verbose_name_plural = _("Page Promotions")
-
-
- class KeywordPromotion(LinkedPromotion):
- """
- A promotion linked to a specific keyword.
-
- This can be used on a search results page to show promotions
- linked to a particular keyword.
- """
-
- keyword = models.CharField(_("Keyword"), max_length=200)
-
- # We allow an additional filter which will let search query matches
- # be restricted to different parts of the site.
- filter = models.CharField(_("Filter"), max_length=200, blank=True, null=True)
-
- def get_link(self):
- return reverse('promotions:keyword-click', kwargs={'keyword_promotion_id': self.id})
-
- class Meta:
- verbose_name = _("Keyword Promotion")
- verbose_name_plural = _("Keyword Promotions")
-
- # Different model types for each type of promotion
-
-
- class AbstractPromotion(models.Model):
- """
- Abstract base promotion that defines the interface
- that subclasses must implement.
- """
- _type = 'Promotion'
- keywords = generic.GenericRelation(KeywordPromotion, verbose_name=_('Keywords'))
- pages = generic.GenericRelation(PagePromotion, verbose_name=_('Pages'))
-
- class Meta:
- abstract = True
- verbose_name = _("Promotion")
- verbose_name_plural = _("Promotions")
-
- @property
- def type(self):
- return _(self._type)
-
- @classmethod
- def classname(cls):
- return cls.__name__.lower()
-
- @property
- def code(self):
- return self.__class__.__name__.lower()
-
- def template_name(self):
- """
- Returns the template to use to render this promotion.
- """
- return 'promotions/%s.html' % self.code
-
- def template_context(self, request):
- return {}
-
- @property
- def content_type(self):
- return ContentType.objects.get_for_model(self)
-
- @property
- def num_times_used(self):
- ctype = self.content_type
- page_count = PagePromotion.objects.filter(content_type=ctype,
- object_id=self.id).count()
- keyword_count = KeywordPromotion.objects.filter(content_type=ctype,
- object_id=self.id).count()
- return page_count + keyword_count
-
-
- class RawHTML(AbstractPromotion):
- """
- Simple promotion - just raw HTML
- """
- _type = 'Raw HTML'
- name = models.CharField(_("Name"), max_length=128)
-
- # Used to determine how to render the promotion (eg
- # if a different width container is required). This isn't always
- # required.
- display_type = models.CharField(
- _("Display type"), max_length=128,
- blank=True, null=True,
- help_text=_("This can be used to have different types of HTML blocks (eg different widths)"))
- body = models.TextField(_("HTML"))
- date_created = models.DateTimeField(auto_now_add=True)
-
- class Meta:
- verbose_name = _('Raw HTML')
- verbose_name_plural = _('Raw HTML')
-
- def __unicode__(self):
- return self.name
-
-
- class Image(AbstractPromotion):
- """
- An image promotion is simply a named image which has an optional
- link to another part of the site (or another site).
-
- This can be used to model both banners and pods.
- """
- _type = 'Image'
- name = models.CharField(_("Name"), max_length=128)
- link_url = ExtendedURLField(_('Link URL'), blank=True, null=True, help_text=_("""This is
- where this promotion links to"""))
- image = models.ImageField(_('Image'), upload_to=settings.OSCAR_PROMOTION_FOLDER)
- date_created = models.DateTimeField(auto_now_add=True)
-
- def __unicode__(self):
- return self.name
-
- class Meta:
- verbose_name = _("Image")
- verbose_name_plural = _("Image")
-
-
- class MultiImage(AbstractPromotion):
- """
- A multi-image promotion is simply a collection of image promotions
- that are rendered in a specific way. This models things like
- rotating banners.
- """
- _type = 'Multi-image'
- name = models.CharField(_("Name"), max_length=128)
- images = models.ManyToManyField('promotions.Image', null=True, blank=True)
- date_created = models.DateTimeField(auto_now_add=True)
-
- def __unicode__(self):
- return self.name
-
- class Meta:
- verbose_name = _("Multi Image")
- verbose_name_plural = _("Multi Images")
-
-
- class SingleProduct(AbstractPromotion):
- _type = 'Single product'
- name = models.CharField(_("Name"), max_length=128)
- product = models.ForeignKey('catalogue.Product')
- description = models.TextField(_("Description"), null=True, blank=True)
- date_created = models.DateTimeField(auto_now_add=True)
-
- def __unicode__(self):
- return self.name
-
- def template_context(self, request):
- return {'product': self.product}
-
- class Meta:
- verbose_name = _("Single Product")
- verbose_name_plural = _("Single Product")
-
-
- class AbstractProductList(AbstractPromotion):
- """
- Abstract superclass for promotions which are essentially a list
- of products.
- """
- name = models.CharField(_("Title"), max_length=255)
- description = models.TextField(_("Description"), null=True, blank=True)
- link_url = ExtendedURLField(_('Link URL'), blank=True, null=True)
- link_text = models.CharField(_("Link text"), max_length=255, blank=True, null=True)
- date_created = models.DateTimeField(auto_now_add=True)
-
- class Meta:
- abstract = True
- verbose_name = _("Product List")
- verbose_name_plural = _("Product Lists")
-
- def __unicode__(self):
- return self.name
-
- def template_context(self, request):
- return {'products': self.get_products()}
-
-
- class HandPickedProductList(AbstractProductList):
- """
- A hand-picked product list is a list of manually selected
- products.
- """
- _type = 'Product list'
- products = models.ManyToManyField('catalogue.Product', through='OrderedProduct', blank=True, null=True,
- verbose_name=_("Products"))
-
- def get_queryset(self):
- return self.products.all().order_by('%s.display_order' % OrderedProduct._meta.db_table)
-
- def get_products(self):
- return self.get_queryset()
-
- class Meta:
- verbose_name = _("Hand Picked Product List")
- verbose_name_plural = _("Hand Picked Product Lists")
-
-
- class OrderedProduct(models.Model):
-
- list = models.ForeignKey('promotions.HandPickedProductList', verbose_name=_("List"))
- product = models.ForeignKey('catalogue.Product', verbose_name=_("Product"))
- display_order = models.PositiveIntegerField(_('Display Order'), default=0)
-
- class Meta:
- ordering = ('display_order',)
- verbose_name = _("Ordered Product")
- verbose_name_plural = _("Ordered Product")
-
-
- class AutomaticProductList(AbstractProductList):
-
- _type = 'Auto-product list'
- BESTSELLING, RECENTLY_ADDED = ('Bestselling', 'RecentlyAdded')
- METHOD_CHOICES = (
- (BESTSELLING, _("Bestselling products")),
- (RECENTLY_ADDED, _("Recently added products")),
- )
- method = models.CharField(_('Method'), max_length=128, choices=METHOD_CHOICES)
- num_products = models.PositiveSmallIntegerField(_('Number of Products'), default=4)
-
- def get_queryset(self):
- if self.method == self.BESTSELLING:
- return (Product.browsable.all()
- .select_related('stockrecord__partner')
- .prefetch_related('variants', 'images')
- .order_by('-score'))
- return (Product.browsable.all()
- .select_related('stockrecord__partner')
- .prefetch_related('variants', 'images')
- .order_by('-date_created'))
-
- def get_products(self):
- return self.get_queryset()[:self.num_products]
-
- class Meta:
- verbose_name = _("Automatic Product List")
- verbose_name_plural = _("Automatic Product Lists")
-
-
- class OrderedProductList(HandPickedProductList):
- tabbed_block = models.ForeignKey('promotions.TabbedBlock',
- related_name='tabs', verbose_name=_("Tabbed Block"))
- display_order = models.PositiveIntegerField(_('Display Order'), default=0)
-
- class Meta:
- ordering = ('display_order',)
- verbose_name = _("Ordered Product List")
- verbose_name_plural = _("Ordered Product Lists")
-
-
- class TabbedBlock(AbstractPromotion):
-
- _type = 'Tabbed block'
- name = models.CharField(_("Title"), max_length=255)
- date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)
-
- class Meta:
- verbose_name = _("Tabbed Block")
- verbose_name_plural = _("Tabbed Blocks")
|