Guest User

Untitled

a guest
Jan 22nd, 2018
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.21 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. # module pyparsing.py
  3. #
  4. # Copyright (c) 2011 Nathan Duthoit
  5. #
  6. # Permission is hereby granted, free of charge, to any person obtaining
  7. # a copy of this software and associated documentation files (the
  8. # "Software"), to deal in the Software without restriction, including
  9. # without limitation the rights to use, copy, modify, merge, publish,
  10. # distribute, sublicense, and/or sell copies of the Software, and to
  11. # permit persons to whom the Software is furnished to do so, subject to
  12. # the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be
  15. # included in all copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  20. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  21. # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  22. # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  23. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  24. #
  25. #
  26.  
  27. from django.db import models
  28. from django.db.models.signals import post_init, pre_save, post_save
  29. from django.dispatch.dispatcher import receiver
  30.  
  31. __author__ = 'nduthoit (Nathan Duthoit)'
  32.  
  33. """
  34. Sample usage (using the spy_on class decorator for models)
  35.  
  36. def log_change(book, old_title, new_title):
  37. print "Changed book title (old: %s) (new: %s)" % (old_title, new_title)
  38.  
  39. @spy_on([Agent('title', log_change)])
  40. class Book(models.Model):
  41. title = models.CharField(max_length=20)
  42. author = models.CharField(max_length=20)
  43.  
  44. The spy_on class decorator takes a list of Agent definitions. An Agent definition consists of:
  45. - name of the field to be watched/spied on
  46. - callback function to be called when the value of the field changes
  47. - optional switch to have the callback called before (default) or after the instance is saved
  48. Note that the callback function you pass in the agent definition will be passed exactly 3 arguments when called by spy: instance, old field value and new field value
  49.  
  50. Known limitation: using the update method on a queryset will prevent django-spy from detecting the field value change (as the save method and related signals are not called)
  51.  
  52. """
  53.  
  54. def _watch_name(field_name):
  55. return "_bug_%s" % field_name
  56.  
  57. class WatchException(Exception):
  58. pass
  59.  
  60. class Agent(object):
  61. def __init__(self, field_name, on_change, pre_save=True):
  62. self.field_name = field_name
  63. self.on_change = on_change
  64. self.pre_save = pre_save
  65.  
  66. def __unicode__(self):
  67. return u"%s - %s" % (self.field_name, self.on_change)
  68.  
  69. def spy_on_model(model, agents):
  70. if not issubclass(model, models.Model):
  71. raise WatchException("'%s' is not a subclass of django.db.models.Model" % (model.__name__, ))
  72. model_fields = [field.name for field in model._meta.fields]
  73. pre_save_agents = []
  74. post_save_agents = []
  75. for agent in agents:
  76. field_name = agent.field_name
  77. is_pre_save = agent.pre_save
  78. if not field_name in model_fields:
  79. raise WatchException("'%s' is not a field of '%s'" % (field_name, model.__name__))
  80. if is_pre_save:
  81. pre_save_agents.append(agent)
  82. else:
  83. post_save_agents.append(agent)
  84.  
  85.  
  86. # Add an attribute for each agent (a 'bug') to keep track of the field values
  87. def bug_instance(instance):
  88. for agent in agents:
  89. field_name = agent.field_name
  90. agent_name = _watch_name(field_name)
  91. current_value = getattr(instance, field_name)
  92. setattr(instance, agent_name, current_value)
  93.  
  94. dispatch_uid = 'spy_post_init_%s' % model.__name__
  95. @receiver(post_init, sender=model, dispatch_uid=dispatch_uid)
  96. def init_bugs(sender, instance, **kwargs):
  97. bug_instance(instance)
  98.  
  99. def get_detect_change(pre_save=True):
  100. if pre_save:
  101. event_agents = pre_save_agents
  102. else:
  103. event_agents = post_save_agents
  104. def detect_change_and_reset_bugs(sender, instance, **kwargs):
  105. for agent in event_agents:
  106. field_name = agent.field_name
  107. agent_name = _watch_name(field_name)
  108. old_value = getattr(instance, agent_name)
  109. new_value = getattr(instance, field_name)
  110. # If the value has changed, call the on_change method of the agent
  111. if old_value != new_value:
  112. agent.on_change(instance, old_value, new_value)
  113. # Reset the bugs for this instance
  114. bug_instance(instance)
  115. return detect_change_and_reset_bugs
  116.  
  117. detect_changes_pre_save = get_detect_change(True)
  118. detect_changes_post_save = get_detect_change(False)
  119.  
  120. if pre_save_agents:
  121. dispatch_uid = 'spy_pre_save_%s' % model.__name__
  122. pre_save.connect(detect_changes_pre_save, sender=model, dispatch_uid=dispatch_uid)
  123. if post_save_agents:
  124. dispatch_uid = 'spy_post_save_%s' % model.__name__
  125. post_save.connect(detect_changes_post_save, sender=model, dispatch_uid=dispatch_uid)
  126.  
  127. return model
  128.  
  129. def spy_on(agents):
  130. def _spy_on_model(model):
  131. return spy_on_model(model, agents)
  132. return _spy_on_model
Add Comment
Please, Sign In to add comment