1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

# -*- coding: utf-8 -*- 

from django.core.urlresolvers import reverse 

from django.conf import settings 

from django.db import models 

from django.db.models import Count 

from django.utils.translation import ugettext_lazy as _ 

from polymorphic.manager import PolymorphicManager 

from polymorphic.polymorphic_model import PolymorphicModel 

from shop.util.fields import CurrencyField 

from shop.util.loader import load_class 

 

class ProductStatisticsManager(PolymorphicManager): 

    """ 

    A Manager for all the non-object manipulation needs, mostly statistics and 

    other "data-mining" toys. 

    """ 

 

    def top_selling_products(self, quantity): 

        """ 

        This method "mines" the previously passed orders, and gets a list of 

        products (of a size equal to the quantity parameter), ordered by how  

        many times they have been purchased. 

        """ 

        # Importing here is fugly, but it saves us from circular imports... 

        from shop.models.ordermodel import OrderItem 

        # Get an aggregate of product references and their respective counts 

        top_products_data = OrderItem.objects.values( 

                'product').annotate( 

                    product_count=Count('product') 

                ).order_by('product_count' 

            )[:quantity] 

 

        # The top_products_data result should be in the form: 

        # [{'product_reference': '<product_id>', 'product_count': <count>}, ...] 

 

        top_products_list = [] # The actual list of products 

        for values in top_products_data: 

            prod = values.get('product') 

            # We could eventually return the count easily here, if needed. 

            top_products_list.append(prod) 

 

        return top_products_list 

 

class ProductManager(PolymorphicManager): 

    """ 

    A more classic manager for Product filtering and manipulation. 

    """ 

    def active(self): 

        return self.filter(active=True) 

 

class Product(PolymorphicModel): 

    """ 

    A basic product for the shop 

    Most of the already existing fields here should be generic enough to reside 

    on the "base model" and not on an added property 

    """ 

 

    name = models.CharField(max_length=255, verbose_name=_('Name')) 

    slug = models.SlugField(verbose_name=_('Slug')) 

    active = models.BooleanField(default=False, verbose_name=_('Active')) 

 

    date_added = models.DateTimeField(auto_now_add=True, verbose_name=_('Date added')) 

    last_modified = models.DateTimeField(auto_now=True, verbose_name=_('Last modified')) 

 

    unit_price = CurrencyField(verbose_name=_('Unit price')) 

 

    # Managers 

    objects = ProductManager() 

    statistics = ProductStatisticsManager() 

 

    class Meta(object): 

        app_label = 'shop' 

        verbose_name = _('Product') 

        verbose_name_plural = _('Products') 

 

    def __unicode__(self): 

        return self.name 

 

    def get_absolute_url(self): 

        return reverse('product_detail', args=[self.slug]) 

 

    def get_price(self): 

        """ 

        Return the price for this item (provided for extensibility) 

        """ 

        return self.unit_price 

 

    def get_name(self): 

        """ 

        Return the name of this Product (provided for extensibility) 

        """ 

        return self.name 

 

#=============================================================================== 

# Extensibility 

#=============================================================================== 

""" 

This overrides the Product model with the class loaded from the SHOP_PRODUCT_MODEL 

setting if it exists. 

""" 

PRODUCT_MODEL = getattr(settings, 'SHOP_PRODUCT_MODEL', None) 

103if PRODUCT_MODEL: 

    Product = load_class(PRODUCT_MODEL, 'SHOP_PRODUCT_MODEL')