models.py 3.7 KB

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