models.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  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 subscription.
  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. class Meta:
  56. verbose_name = 'configuration'
  57. @receiver(post_save, sender=IPSubnet)
  58. def subnet_save_event(sender, **kwargs):
  59. """Fires when a subnet is saved (created/modified). We tell the
  60. configuration backend to do whatever it needs to do with it.
  61. We should use a pre_save signal, so that if anything goes wrong in the
  62. backend (exception raised), nothing is actually saved in the database.
  63. But it has a big problem: the configuration backend will not see the
  64. change, since it has not been saved into the database yet.
  65. That's why we use a post_save signal instead. But surprisingly, all
  66. is well: if we raise an exception here, the IPSubnet object will not
  67. be saved in the database. But the backend *does* see the new state of
  68. the database. It looks like the database rollbacks if an exception is
  69. raised. Whatever the reason, this is not a documented feature of
  70. Django signals.
  71. """
  72. subnet = kwargs['instance']
  73. try:
  74. config = subnet.configuration
  75. config.save_subnet(subnet, kwargs['created'])
  76. except ObjectDoesNotExist:
  77. pass
  78. @receiver(post_delete, sender=IPSubnet)
  79. def subnet_delete_event(sender, **kwargs):
  80. """Fires when a subnet is deleted. We tell the configuration backend to
  81. do whatever it needs to do with it.
  82. """
  83. subnet = kwargs['instance']
  84. try:
  85. config = subnet.configuration
  86. config.delete_subnet(subnet)
  87. except ObjectDoesNotExist:
  88. pass