Exemple #1
0
def get_default_namespaces():
    """
    Whether owner or owner-domain (org, dept, etc.), return the default Namespace instance
     as a singleton list. It is created by default if SMARTMODELS_NAMESPACE_MODEL model DoesNotExists
    Also make the sentinel owner belong to the sentinel org.
    """
    namespace, created = get_namespace_model()._objects.get_or_create(
        **{
            get_setting('NAMESPACE_PK_FIELD'): get_setting('SENTINEL_UID'),
        })
    return list((namespace, ))
def is_sentinel(instance):
    """
    Whether instance is the sentinel for the user or namespace model.
    :param instance: instance of User or Namespace.
    :return: bool
    """
    return _are_services(instance, uid=get_setting('SENTINEL_UID'))
Exemple #3
0
def prepare_shared_smart_fields(sender, instance, **kwargs):
    """
    Ensuring Resource subclasses will be saved with (*)all smart fields correctly set.
    Whether to require the smart fields' values from the api user (if `MODELS_DEFAULT_REQUIRED=True`)?
     Or to preset them with the defaults when the api user has not supplied them?

    (*) Only set the shared smart fields. Regular smart fields assumed already set by homologue
    method `prepare_smart_fields` for SmartModel subclasses.
    (*) We'nt aware whether it's a update or create op!
          Which fields need persisting to db is left up to high level api.
    """
    if issubclass(sender, Resource):
        namespaces = getattr(instance, get_namespaces_manager_name())

        # require the setting of all Resource fields by the api user
        # if requested so (`MODELS_DEFAULT_REQUIRED=True) otherwise,
        # use our own defaults presets.
        if get_setting('DEFAULT_REQUIRED'):
            assert namespaces, (
                '{model_class}(SmartModel) instance missing "namespaces" attribute.'
                'To use the builtin defaults, set `SMARTMODELS_DEFAULT_REQUIRED=False`'
                'in  Django settings'.format(
                    model_class=instance.__class__.__name__))
            assert namespaces.exists(), (
                'The `namespaces` smart model field mustn\'t be empty since SMARTMODELS_DEFAULT_REQUIRED=True". '
                'Please supply  some {model_class} instances.'
                'To use the builtin defaults, set `SMARTMODELS_DEFAULT_REQUIRED=False`'
                'in  Django settings'.format(
                    model_class=type(namespaces).__name__))

    # forbid CRUDing namespace instances, superusers excepted.
    if issubclass(sender, Namespace):
        drop_perms(sender)
Exemple #4
0
    def delete(self, using=None, keep_parents=False):
        """
        Model.delete() override that also mark model as deleted and by whom.
        Requires the `deleted_by` field to be set by the caller, if SMARTMODELS_DEFAULT_REQUIRED=True.

        Owner of the model is changed to the sentinel owner by the ORM behind the scene
        through `on_delete=models.SET(get_sentinel_user)`. Cf the owner field definition.
        """

        # this is a guard to ensure `deleted_by` is set
        # so that we know we deletes an instance
        if get_setting('DEFAULT_REQUIRED'):
            assert self.deleted_by, (
                '{model_class}(SmartModel) instance missing "deleted_by" attribute.'
                'To use the builtin defaults, set `SMARTMODELS_DEFAULT_REQUIRED=False`'
                'in  Django settings'.format(
                    model_class=self.__class__.__name__))

        self.deleted_at = timezone.now()
        self.owner = get_sentinel_user()

        # calling save instead of regular `delete()` method,
        # will route to the `smartmodels.models.prepare_smart_fields()` pre_save signal handler
        # also, let's manually fire the `post_delete` signal to leave change to listeners to cope with the deletion.
        post_delete.send(sender=self.__class__,
                         instance=self,
                         deleted_by=self.deleted_by)
        self.save()
Exemple #5
0
def get_sentinel_user():
    """
    Sentinel instance, of the AUTH_USER_MODEL model.
    """

    # sentinel is hidden from the regular `objects` manager,
    # use the default manager
    user, created = get_user_model()._objects.get_or_create(
        **{get_owner_pk_field(): get_setting('SENTINEL_UID')})
    return user
    def active(self):
        """
        Hide 1) the deleted instances 2) those whose creator (owner) has been deleted
        and  3) those belonging to the sentinel owner.
        """
        # FIXME: implement 2-3).
        # FIXME: get_sentinel_owner() exception trying to get instance 'coz models not loaded yet
        qs = self
        owner_pk_field = get_owner_pk_field()
        sentinel_filter = {"owner__{}".format(owner_pk_field): get_setting('SENTINEL_UID')}
        services_filter = {"owner__{}__in".format(owner_pk_field): get_setting('SERVICE_UIDS')}

        # hide what should be hidden
        qs = qs.exclude(Q(**sentinel_filter) | Q(owner__is_active=False,))
        if get_setting('HIDE_DELETED'):
            qs = qs.exclude(Q(deleted_at__isnull=False))
        if get_setting('HIDE_SERVICE_OWNERS'):
            qs = qs.exclude(Q(**services_filter) | Q(deleted_at__isnull=False))

        return qs
Exemple #7
0
class Resource(SmartModel):
    """
    Resource instances can be `owned` by several namespaces at once.
    If no valid namespace is set on creation, a resource inherit by default
     the namespaces of the owner that creates it.
    """
    # defaults to `shared_model.Namespace` instances. cf. smartmodels.settings
    namespaces = models.ManyToManyField(
        get_setting('NAMESPACE_MODEL'),
        related_name='%(class)ss_owned',
        help_text=_("Visibility domain: org, district, domain, etc."))

    class Meta:
        abstract = True
 def delete(self, deleted_by=None, **kwargs):
     """
     Fake-delete an entire queryset.
     Hooked when call looks like: SmartX.objects.filter(**opts).delete()
     :return: Nothing
     """
     if get_setting('DEFAULT_REQUIRED'):
         assert deleted_by, (
             '{model_class}(SmartModel) manager method `delete()` missing "deleted_by" attribute.'
             'To use the builtin defaults, set `SMARTMODELS_DEFAULT_REQUIRED=False`'
             'in  Django settings'.format(
                 model_class=self.__class__.__name__
             )
         )
     return super(SmartQuerySet, self)\
         .update(deleted_at=timezone.now(), deleted_by=deleted_by, owner=get_sentinel_user(), **kwargs)
Exemple #9
0
def get_namespace_model():
    """
    Return the Owner model class (not string)
    that is active in this project.
    """

    model_string = get_setting('NAMESPACE_MODEL')
    try:
        return django_apps.get_model(model_string, require_ready=False)
    except ValueError:
        raise ImproperlyConfigured(
            "SMARTMODELS_NAMESPACE_MODEL must be of the form 'app_label.model_name'"
        )
    except LookupError:
        raise ImproperlyConfigured(
            "SMARTMODELS_NAMESPACE_MODEL refers to model '%s' that has not been installed"
            % model_string)
Exemple #10
0
def prepare_smart_fields(sender, instance, **kwargs):
    """
    Ensures SmartModel subclasses will be saved with (*)all smart fields correctly set.
    Will require the smart fields' values set from higher lever API if `MODELS_DEFAULT_REQUIRED=True`
    Set `MODELS_DEFAULT_REQUIRED=False` to emulate regular models (ignore the smart fields).

    (*) We are'nt aware whether it's a update or create op, nor what owner achieves it!
    (*) Values for `created_by`, `updated_by` `deleted_by` must be set by the higher level api.
    """
    # TODO: delete not handled yet !
    if issubclass(sender, SmartModel):

        # require the setting of all Resource fields by the api owner
        # if requested so (`MODELS_DEFAULT_REQUIRED=True) otherwise,
        # use our own defaults presets.
        excluded = is_sentinel(instance) or is_superuser(
            instance) or is_service(instance)
        if get_setting('DEFAULT_REQUIRED') and not excluded:
            assert instance.owner, (
                '{model_class}(SmartModel) instance missing "owner" attribute. '
                'To use the builtin defaults, set `SMARTMODELS_DEFAULT_REQUIRED=False`'
                'in  Django settings'.format(
                    model_class=instance.__class__.__name__))
            if not instance.pk:
                assert instance.created_by and instance.updated_by, (
                    '{model_class}(SmartModel) instance missing "created_by" or "updated_by" attribute. '
                    'To use the builtin defaults, set `SMARTMODELS_DEFAULT_REQUIRED=False`'
                    'in  Django settings'.format(
                        model_class=instance.__class__.__name__))
            else:
                assert instance.updated_by or instance.deleted_by, (
                    '{model_class}(SmartModel) instance missing "updated_by" or "deleted_by" attribute. '
                    'To use the builtin defaults, set `SMARTMODELS_DEFAULT_REQUIRED=False`'
                    'in  Django settings'.format(
                        model_class=instance.__class__.__name__))

        # use the builtin defaults,
        # because MODELS_DEFAULT_REQUIRED=True
        else:
            time = timezone.now()
            smart_fields = dict(updated_at=time)
            if not instance.pk:
                smart_fields.update(created_at=time)

            for attr, value in smart_fields.items():
                setattr(instance, attr, value)
Exemple #11
0
class AbstractNamespace(models.Model):
    """
    Top level domain (org, department, topic, etc.) which defines:
    1) the viewing scope of resources (Resource instances) created by users,
    2) the high level entity users are contributing to by creating resources.

    When picked up by Django settings (`SMARTMODELS_NAMESPACE_MODEL != AUTH_USER_MODEL),
     it means that the `distributed mode is active.
    """

    # TODO: Make the `name` field a configurable setting via custom metaclass
    slug = models.SlugField(
        _("Unique ID"),
        default='',
        max_length=get_setting('NAMESPACE_MAX_LENGTH'),
        help_text=
        _("Unique ID for this namespace. Configurable via the SMARTMODELS_OWNER_PK_FIELD setting."
          ),
        unique=True,
        blank=False,
        null=False,
        editable=True,
        error_messages=dict(unique=_("This slug is not available")))
    users = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name=
        '%(class)ss',  # eg. user.namespaces, user.orgs, user.domains, etc.
        help_text=_("Contributors (users) to this org, department, etc."))

    objects = NamespaceManager()
    _objects = models.Manager()

    def __str__(self):
        return self.slug

    class Meta:
        abstract = True

    def clean(self):
        self.slug = slugify(self.slug, allow_unicode=True)
Exemple #12
0
 def active(self):
     return self.exclude(
         **{
             get_setting('NAMESPACE_PK_FIELD'): get_setting('SENTINEL_UID'),
         })
def is_service(instance):
    return _are_services(instance, uids=get_setting('SERVICE_UIDS'))
 def get_queryset(self):
     if get_setting('NAMESPACE_MODEL') == settings.AUTH_USER_MODEL:
         return get_default_namespaces()
     return super(NamespaceViewSet, self).get_queryset()