Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

receivers.py 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import logging
  2. from django.db.models import F
  3. from django.dispatch import receiver
  4. from django.db import IntegrityError
  5. from oscar.core.loading import get_class, get_classes
  6. from oscar.apps.search.signals import user_search
  7. UserSearch, UserRecord, ProductRecord, UserProductView = get_classes(
  8. 'analytics.models', ['UserSearch', 'UserRecord', 'ProductRecord',
  9. 'UserProductView'])
  10. product_viewed = get_classes('catalogue.signals', ['product_viewed'])
  11. basket_addition = get_class('basket.signals', 'basket_addition')
  12. order_placed = get_class('order.signals', 'order_placed')
  13. # Helpers
  14. logger = logging.getLogger('oscar.analytics')
  15. def _update_counter(model, field_name, filter_kwargs, increment=1):
  16. """
  17. Efficiently updates a counter field by a given increment. Uses Django's
  18. update() call to fetch and update in one query.
  19. :param model: The model class of the recording model
  20. :param field_name: The name of the field to update
  21. :param filter_kwargs: Parameters to the ORM's filter() function to get the
  22. correct instance
  23. """
  24. try:
  25. record = model.objects.filter(**filter_kwargs)
  26. affected = record.update(**{field_name: F(field_name) + increment})
  27. if not affected:
  28. filter_kwargs[field_name] = increment
  29. model.objects.create(**filter_kwargs)
  30. except IntegrityError:
  31. # get_or_create sometimes fails due to MySQL's weird transactions, fail
  32. # silently
  33. logger.error(
  34. "IntegrityError when updating analytics counter for %s", model)
  35. def _record_products_in_order(order):
  36. # surely there's a way to do this without causing a query for each line?
  37. for line in order.lines.all():
  38. _update_counter(
  39. ProductRecord, 'num_purchases',
  40. {'product': line.product}, line.quantity)
  41. def _record_user_order(user, order):
  42. try:
  43. record = UserRecord.objects.filter(user=user)
  44. affected = record.update(
  45. num_orders=F('num_orders') + 1,
  46. num_order_lines=F('num_order_lines') + order.num_lines,
  47. num_order_items=F('num_order_items') + order.num_items,
  48. total_spent=F('total_spent') + order.total_incl_tax,
  49. date_last_order=order.date_placed)
  50. if not affected:
  51. UserRecord.objects.create(
  52. user=user, num_orders=1, num_order_lines=order.num_lines,
  53. num_order_items=order.num_items,
  54. total_spent=order.total_incl_tax,
  55. date_last_order=order.date_placed)
  56. except IntegrityError:
  57. logger.error(
  58. "IntegrityError in analytics when recording a user order.")
  59. # Receivers
  60. @receiver(product_viewed)
  61. def receive_product_view(sender, product, user, **kwargs):
  62. if kwargs.get('raw', False):
  63. return
  64. _update_counter(ProductRecord, 'num_views', {'product': product})
  65. if user and user.is_authenticated():
  66. _update_counter(UserRecord, 'num_product_views', {'user': user})
  67. UserProductView.objects.create(product=product, user=user)
  68. @receiver(user_search)
  69. def receive_product_search(sender, query, user, **kwargs):
  70. if user and user.is_authenticated() and not kwargs.get('raw', False):
  71. UserSearch._default_manager.create(user=user, query=query)
  72. @receiver(basket_addition)
  73. def receive_basket_addition(sender, product, user, **kwargs):
  74. if kwargs.get('raw', False):
  75. return
  76. _update_counter(
  77. ProductRecord, 'num_basket_additions', {'product': product})
  78. if user and user.is_authenticated():
  79. _update_counter(UserRecord, 'num_basket_additions', {'user': user})
  80. @receiver(order_placed)
  81. def receive_order_placed(sender, order, user, **kwargs):
  82. if kwargs.get('raw', False):
  83. return
  84. _record_products_in_order(order)
  85. if user and user.is_authenticated():
  86. _record_user_order(user, order)