models.py 3.6 KB

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