models.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # -*- coding: utf-8 -*-
  2. from django.db import models
  3. from polymorphic import PolymorphicModel
  4. from coin.offers.models import OfferSubscription
  5. from django.db.models.signals import post_save, post_delete
  6. from django.core.exceptions import ObjectDoesNotExist
  7. from django.dispatch import receiver
  8. from coin.resources.models import IPSubnet
  9. """
  10. Implementation note : Configuration is a PolymorphicModel.
  11. The childs of Configuration are the differents models to store
  12. technical informations of a subscibtion.
  13. To add a new configuration backend, you have to create a new app with a model
  14. which inherit from Configuration.
  15. Your model can implement Meta verbose_name to have human readable name and a
  16. url_namespace variable to specify the url namespace used by this model.
  17. """
  18. class Configuration(PolymorphicModel):
  19. offersubscription = models.OneToOneField(OfferSubscription,
  20. related_name='configuration',
  21. verbose_name='abonnement')
  22. @staticmethod
  23. def get_configurations_choices_list():
  24. """
  25. Génère automatiquement la liste de choix possibles de configurations
  26. en fonction des classes enfants de Configuration
  27. """
  28. return tuple((x().__class__.__name__,x()._meta.verbose_name)
  29. for x in Configuration.__subclasses__())
  30. def model_name(self):
  31. return self.__class__.__name__
  32. model_name.short_description = 'Nom du modèle'
  33. def configuration_type_name(self):
  34. return self._meta.verbose_name
  35. configuration_type_name.short_description = 'Type'
  36. def get_absolute_url(self):
  37. """
  38. Renvoi l'URL d'accès à la page "details" de l'objet
  39. Une url doit être nommée "details"
  40. """
  41. from django.core.urlresolvers import reverse
  42. return reverse('%s:details' % self.get_url_namespace(),
  43. args=[str(self.id)])
  44. def get_url_namespace(self):
  45. """
  46. Renvoi le namespace utilisé par la configuration. Utilise en priorité
  47. celui définit dans la classe enfant dans url_namespace sinon
  48. par défaut utilise le nom de la classe en minuscule
  49. """
  50. if self.url_namespace:
  51. return self.url_namespace
  52. else:
  53. return self.model_name().lower()
  54. @receiver(post_save, sender=IPSubnet)
  55. def subnet_save_event(sender, **kwargs):
  56. """Fires when a subnet is saved (created/modified). We tell the
  57. configuration backend to do whatever it needs to do with it.
  58. We should use a pre_save signal, so that if anything goes wrong in the
  59. backend (exception raised), nothing is actually saved in the database.
  60. But it has a big problem: the configuration backend will not see the
  61. change, since it has not been saved into the database yet.
  62. That's why we use a post_save signal instead. But surprisingly, all
  63. is well: if we raise an exception here, the IPSubnet object will not
  64. be saved in the database. But the backend *does* see the new state of
  65. the database. It looks like the database rollbacks if an exception is
  66. raised. Whatever the reason, this is not a documented feature of
  67. Django signals.
  68. """
  69. subnet = kwargs['instance']
  70. try:
  71. config = subnet.configuration
  72. config.save_subnet(subnet, kwargs['created'])
  73. except ObjectDoesNotExist:
  74. pass
  75. @receiver(post_delete, sender=IPSubnet)
  76. def subnet_delete_event(sender, **kwargs):
  77. """Fires when a subnet is deleted. We tell the configuration backend to
  78. do whatever it needs to do with it.
  79. """
  80. subnet = kwargs['instance']
  81. try:
  82. config = subnet.configuration
  83. config.delete_subnet(subnet)
  84. except ObjectDoesNotExist:
  85. pass