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
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.')
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
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))
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
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)
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)
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.')
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.' )
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)
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]')))