import django from django.contrib.auth.models import Group from django.db import models from django.db.models.fields.related_descriptors import ReverseOneToOneDescriptor from django.db.transaction import atomic from django.utils.translation import ugettext_lazy as _ from django.conf import settings class UserSAMLIdentifier(models.Model): user = models.ForeignKey( verbose_name=_('user'), to=settings.AUTH_USER_MODEL, related_name='saml_identifiers') issuer = models.TextField( verbose_name=_('Issuer')) name_id = models.TextField( verbose_name=_('SAML identifier')) created = models.DateTimeField( verbose_name=_('created'), auto_now_add=True) class Meta: verbose_name = _('user SAML identifier') verbose_name_plural = _('users SAML identifiers') unique_together = (('issuer', 'name_id'),) class AutoSingleRelatedObjectDescriptor(ReverseOneToOneDescriptor): """From https://github.com/skorokithakis/django-annoying Temporary, we will write a migration after testing""" @atomic def __get__(self, instance, instance_type=None): model = getattr(self.related, 'related_model', self.related.model) try: return ( super(AutoSingleRelatedObjectDescriptor, self) .__get__(instance, instance_type) ) except model.DoesNotExist: # Using get_or_create instead() of save() or create() as it better handles race conditions obj, _ = model.objects.get_or_create(**{self.related.field.name: instance}) # Update Django's cache, otherwise first 2 calls to obj.relobj # will return 2 different in-memory objects if django.VERSION >= (2, 0): self.related.set_cached_value(instance, obj) self.related.field.set_cached_value(obj, instance) else: setattr(instance, self.cache_name, obj) setattr(obj, self.related.field.get_cache_name(), instance) return obj class AutoOneToOneField(models.OneToOneField): """From https://github.com/skorokithakis/django-annoying""" def contribute_to_related_class(self, cls, related): setattr( cls, related.get_accessor_name(), AutoSingleRelatedObjectDescriptor(related) ) class AuthenticationLevel(models.Model): group = AutoOneToOneField(Group, related_name='auth_level') # ou plutot laisser blanc pour qu'il y ait une erreur si on # a pas recu les niveaux ? value = models.PositiveSmallIntegerField(default=1)