예제 #1
0
 def __new__(mcs, name, bases, attrs):
     """
     This method creates and registers new class, if it's not already
     in the register.
     """
     # Do not register the base classes, which actual classes inherit.
     if mcs.inherit_fields:
         for base in bases:
             for field, value in mcs.get_fields_from_base(base):
                 if field not in attrs:
                     attrs[field] = value
     if not attrs.get('__abstract__', False):
         missing = mcs.get_missing_fields(mcs.required_fields, attrs)
         if missing:
             raise RestEasyException(
                 'The following mandatory fields are missing from {} class definition: {}'.format(
                     name,
                     ', '.join(missing)
                 )
             )
         name, bases, attrs = mcs.pre_register(name, bases, attrs)
         slug = mcs.get_name(name, bases, attrs)
         cls = super(RegisteredCreator, mcs).__new__(mcs, name, bases, attrs)
         mcs.register.register(slug, cls)
         mcs.post_register(cls, name, bases, attrs)
     else:
         cls = super(RegisteredCreator, mcs).__new__(mcs, name, bases, attrs)
     return cls
예제 #2
0
    def __init__(self, qs_or_obj, parent_field='pk', related_field=None, raise_404=False, allow_none=False,
                 get_object_handle='', parent=None):
        """
        Sets instance properties, infers sane defaults and ensures qs_or_obj is correct.

        :param qs_or_obj: This can be a queryset or a Django model or explicit None (for particular subclasses)
        :param parent_field: the field to filter by in the parent queryset (qs_or_obj), by default 'id'.
        :param related_field: the field to filter by in the view queryset, by default model_name.
        :param raise_404: whether 404 should be raised if parent object cannot be found.
        :param allow_none: if filtering view queryset by object=None should be allowed. If it's false, resulting
         queryset is guaranteed to be empty if parent object can't be found and 404 is not raised.
        :param get_object_handle: the name under which the object should be available in view. ie.
         view.get_scoped_object(get_object_handle) or view.get_{get_object_handle}. If None, the object will
         not be available from view level. By default will be infered to qs_or_obj's model_name.
        :param parent: if this object's queryset should be filtered by another parameter, parent attribute should be
         an instance of ScopeQuerySet. This allows for ScopeQuerySetChaining (ie. for messages we might have
         UrlKwargScopeQuerySet(User, parent=UrlKwargScopeQuerySet(Account))  for scoping by user and limiting users
         to an account.
        """
        if isinstance(qs_or_obj, QuerySet):
            self.queryset = qs_or_obj
        elif isinstance(qs_or_obj, type) and issubclass(qs_or_obj, Model):
            self.queryset = qs_or_obj.objects.all()
        elif qs_or_obj is None:
            self.queryset = None
        else:
            raise RestEasyException('Queryset parameter must be an instance of QuerySet or a Model subclass.')

        if related_field is None:
            try:
                related_field = '{}'.format(self.queryset.model._meta.model_name)  # pylint: disable=protected-access
            except AttributeError:
                raise RestEasyException('Either related_field or qs_or_obj must be given.')
        self.parent_field = parent_field
        self.related_field = related_field
        self.raise_404 = raise_404
        self.parent = ([parent] if isinstance(parent, ScopeQuerySet) else parent) or []
        self.allow_none = allow_none
        self.get_object_handle = get_object_handle
        if self.get_object_handle == '':
            try:
                self.get_object_handle = self.queryset.model._meta.model_name  # pylint: disable=protected-access
            except AttributeError:
                raise RestEasyException('Either qs_or_obj or explicit get_object_handle (can be None) must be given.')
예제 #3
0
def get_serializer(data):
    """
    Get correct serializer for dict-like data.

    This introspects model and schema fields of the data and passes them to
    :class:`rest_easy.registers.SerializerRegister`.
    :param data: dict-like object.
    :return: serializer class.
    """
    if 'model' not in data or 'schema' not in data:
        raise RestEasyException(
            'Both model and schema must be provided in data~.')
    serializer = serializer_register.lookup(
        serializer_register.get_name(data['model'], data['schema']))
    if not serializer:
        raise RestEasyException(
            'No serializer found for model {} schema {}'.format(
                data['model'], data['schema']))
    return serializer
예제 #4
0
 def register(self, name, ref):
     """
     Register an entry, shall we?
     :param name: entry name.
     :param ref: entry value (probably class).
     :returns: True if model was added just now, False if it was already in the register.
     """
     if not self.lookup(name) or self.get_conflict_policy() == 'allow':
         self._entries[name] = ref
         return True
     raise RestEasyException('Entry named {} is already registered.'.format(name))
예제 #5
0
 def serialize(self, schema=None):
     """
     Serialize the model using given or default schema.
     :param schema: schema to be used for serialization or self.default_schema
     :return: serialized data (a dict).
     """
     serializer = self.get_serializer(schema or self.default_schema)
     if not serializer:
         raise RestEasyException(
             'No serializer found for model {} schema {}'.format(
                 self.__class__, schema))
     return serializer(self).data
예제 #6
0
 def contribute_to_class(self, view):
     """
     Put self.get_object_handle into view's available handles dict to allow easy access to the scope's get_object()
     method in case the object needs to be reused (ie. in child object creation).
     :param view: View the scope is added to.
     """
     if self.get_object_handle:
         if self.get_object_handle in view.rest_easy_available_object_handles:
             raise RestEasyException(
                 'ImproperlyConfigured: multiple scopes with {} get_object handle!'.format(self.get_object_handle)
             )
         view.rest_easy_available_object_handles[self.get_object_handle] = self
     for scope in self.parent:
         scope.contribute_to_class(view)
예제 #7
0
    def __init__(self, *args, **kwargs):
        """
        Adds is_object and request_attr  to :class:`rest_easy.views.ScopeQuerySet` init parameters.

        :param args: same as :class:`rest_easy.views.ScopeQuerySet`.
        :param request_attr: name of property to be obtained from view.request.
        :param is_object: if request's property will be an object or a value to filter by. True by default.
        :param kwargs: same as :class:`rest_easy.views.ScopeQuerySet`.
        """
        self.request_attr = kwargs.pop('request_attr', None)
        if self.request_attr is None:
            raise RestEasyException('request_attr must be set explicitly on an {} init.'.format(
                self.__class__.__name__))
        self.is_object = kwargs.pop('is_object', True)
        super(RequestAttrScopeQuerySet, self).__init__(*args, **kwargs)
예제 #8
0
    def __init__(self, *args, **kwargs):
        """
        Adds url_kwarg to :class:`rest_easy.views.ScopeQuerySet` init parameters.

        :param args: same as :class:`rest_easy.views.ScopeQuerySet`.
        :param url_kwarg: name of url field to be obtained from view's kwargs. By default it will be inferred as
         model_name_pk.
        :param kwargs: same as :class:`rest_easy.views.ScopeQuerySet`.
        """
        self.url_kwarg = kwargs.pop('url_kwarg', None)
        super(UrlKwargScopeQuerySet, self).__init__(*args, **kwargs)
        if not self.url_kwarg:
            try:
                self.url_kwarg = '{}_pk'.format(self.queryset.model._meta.model_name)  # pylint: disable=protected-access
            except AttributeError:
                raise RestEasyException('Either related_field or qs_or_obj must be given.')
예제 #9
0
 def get_name(model, schema):
     """
     Constructs serializer registration name using model's app label, model name and schema.
     :param model: a Django model, a ct-like app-model string (app_label.modelname) or explicit None.
     :param schema: schema to be used.
     :return: constructed serializer registration name.
     """
     if model is None:
         return schema
     if isinstance(model, six.string_types):
         return '{}.{}'.format(model, schema)
     try:
         return '{}.{}.{}'.format(model._meta.app_label,
                                  model._meta.model_name, schema)  # pylint: disable=protected-access
     except AttributeError:
         raise RestEasyException(
             'Model must be either None, a ct-like model string or Django model class.'
         )
예제 #10
0
    def get_serializer_name(self, verb=None):
        """
        Obtains registered serializer name for this view.

        Leverages :class:`rest_easy.registers.SerializerRegister`. Works when either of or both model
        and schema properties are available on this view.

        :return: registered serializer key.
        """
        model = getattr(self, 'model', None)
        schema = None
        if not model and not hasattr(self, 'schema') and (
                verb and verb not in self.serializer_schema_for_verb):
            raise RestEasyException(
                'Either model or schema fields need to be set on a model-based GenericAPIView.'
            )
        if verb:
            schema = self.serializer_schema_for_verb.get(verb, None)
        if schema is None:
            schema = getattr(self, 'schema', 'default')
        return serializer_register.get_name(model, schema)
예제 #11
0
    def get_serializer_class(self):
        """
        Gets serializer appropriate for this view.

        Leverages :class:`rest_easy.registers.SerializerRegister`. Works when either of or both model
        and schema properties are available on this view.

        :return: serializer class.
        """

        if hasattr(self, 'serializer_class') and self.serializer_class:
            return self.serializer_class

        serializer = serializer_register.lookup(
            self.get_serializer_name(verb=self.get_drf_verb()))
        if serializer:
            return serializer

        raise RestEasyException(
            u'Serializer for model {} and schema {} cannot be found.'.format(
                getattr(self, 'model', '[no model]'),
                getattr(self, 'schema', '[no schema]')))