diff options
| author | Tom Christie | 2011-06-02 12:58:10 +0100 | 
|---|---|---|
| committer | Tom Christie | 2011-06-02 12:58:10 +0100 | 
| commit | b50492853f537a2473bb0a9eea86c8b0ed6b8824 (patch) | |
| tree | d289d39aacf187a8a0696a4c1c863aabe1472c3a /djangorestframework/modelresource.py | |
| parent | 7ee9adbe5c03c29cd4a894dd476548f7fe73b5e4 (diff) | |
| parent | fc1640de75511006e89f033c9270ec91a9f1e4d4 (diff) | |
| download | django-rest-framework-b50492853f537a2473bb0a9eea86c8b0ed6b8824.tar.bz2 | |
pull in -dev as 0.2.0
Diffstat (limited to 'djangorestframework/modelresource.py')
| -rw-r--r-- | djangorestframework/modelresource.py | 431 | 
1 files changed, 0 insertions, 431 deletions
diff --git a/djangorestframework/modelresource.py b/djangorestframework/modelresource.py deleted file mode 100644 index 55a15d6a..00000000 --- a/djangorestframework/modelresource.py +++ /dev/null @@ -1,431 +0,0 @@ -from django.forms import ModelForm -from django.db.models import Model -from django.db.models.query import QuerySet -from django.db.models.fields.related import RelatedField - -from djangorestframework.response import Response, ResponseException -from djangorestframework.resource import Resource -from djangorestframework.validators import ModelFormValidatorMixin -from djangorestframework import status - -import decimal -import inspect -import re - - -class ModelResource(Resource, ModelFormValidatorMixin): -    """A specialized type of Resource, for resources that map directly to a Django Model. -    Useful things this provides: - -    0. Default input validation based on ModelForms. -    1. Nice serialization of returned Models and QuerySets. -    2. A default set of create/read/update/delete operations.""" -     -    # The model attribute refers to the Django Model which this Resource maps to. -    # (The Model's class, rather than an instance of the Model) -    model = None -     -    # By default the set of returned fields will be the set of: -    # -    # 0. All the fields on the model, excluding 'id'. -    # 1. All the properties on the model. -    # 2. The absolute_url of the model, if a get_absolute_url method exists for the model. -    # -    # If you wish to override this behaviour, -    # you should explicitly set the fields attribute on your class. -    fields = None -     -    # By default the form used with be a ModelForm for self.model -    # If you wish to override this behaviour or provide a sub-classed ModelForm -    # you should explicitly set the form attribute on your class. -    form = None -     -    # By default the set of input fields will be the same as the set of output fields -    # If you wish to override this behaviour you should explicitly set the -    # form_fields attribute on your class.  -    #form_fields = None - - -    #def get_form(self, content=None): -    #    """Return a form that may be used in validation and/or rendering an html emitter""" -    #    if self.form: -    #        return super(self.__class__, self).get_form(content) -    # -    #    elif self.model: -    # -    #        class NewModelForm(ModelForm): -    #            class Meta: -    #                model = self.model -    #                fields = self.form_fields if self.form_fields else None -    # -    #        if content and isinstance(content, Model): -    #            return NewModelForm(instance=content) -    #        elif content: -    #            return NewModelForm(content) -    #         -    #        return NewModelForm() -    # -    #    return None - - -    #def cleanup_request(self, data, form_instance): -    #    """Override cleanup_request to drop read-only fields from the input prior to validation. -    #    This ensures that we don't error out with 'non-existent field' when these fields are supplied, -    #    and allows for a pragmatic approach to resources which include read-only elements. -    # -    #    I would actually like to be strict and verify the value of correctness of the values in these fields, -    #    although that gets tricky as it involves validating at the point that we get the model instance. -    #     -    #    See here for another example of this approach: -    #    http://fedoraproject.org/wiki/Cloud_APIs_REST_Style_Guide -    #    https://www.redhat.com/archives/rest-practices/2010-April/thread.html#00041""" -    #    read_only_fields = set(self.fields) - set(self.form_instance.fields) -    #    input_fields = set(data.keys()) -    # -    #    clean_data = {} -    #    for key in input_fields - read_only_fields: -    #        clean_data[key] = data[key] -    # -    #    return super(ModelResource, self).cleanup_request(clean_data, form_instance) - - -    def cleanup_response(self, data): -        """A munging of Piston's pre-serialization.  Returns a dict""" - -        def _any(thing, fields=()): -            """ -            Dispatch, all types are routed through here. -            """ -            ret = None -             -            if isinstance(thing, QuerySet): -                ret = _qs(thing, fields=fields) -            elif isinstance(thing, (tuple, list)): -                ret = _list(thing) -            elif isinstance(thing, dict): -                ret = _dict(thing) -            elif isinstance(thing, int): -                ret = thing -            elif isinstance(thing, bool): -                ret = thing -            elif isinstance(thing, type(None)): -                ret = thing -            elif isinstance(thing, decimal.Decimal): -                ret = str(thing) -            elif isinstance(thing, Model): -                ret = _model(thing, fields=fields) -            #elif isinstance(thing, HttpResponse):    TRC -            #    raise HttpStatusCode(thing) -            elif inspect.isfunction(thing): -                if not inspect.getargspec(thing)[0]: -                    ret = _any(thing()) -            elif hasattr(thing, '__emittable__'): -                f = thing.__emittable__ -                if inspect.ismethod(f) and len(inspect.getargspec(f)[0]) == 1: -                    ret = _any(f()) -            else: -                ret = unicode(thing)  # TRC  TODO: Change this back! - -            return ret - -        def _fk(data, field): -            """ -            Foreign keys. -            """ -            return _any(getattr(data, field.name)) -         -        def _related(data, fields=()): -            """ -            Foreign keys. -            """ -            return [ _model(m, fields) for m in data.iterator() ] -         -        def _m2m(data, field, fields=()): -            """ -            Many to many (re-route to `_model`.) -            """ -            return [ _model(m, fields) for m in getattr(data, field.name).iterator() ] -         - -        def _method_fields(data, fields): -            if not data: -                return { } -     -            has = dir(data) -            ret = dict() -                 -            for field in fields: -                if field in has: -                    ret[field] = getattr(data, field) -             -            return ret - -        def _model(data, fields=()): -            """ -            Models. Will respect the `fields` and/or -            `exclude` on the handler (see `typemapper`.) -            """ -            ret = { } -            #handler = self.in_typemapper(type(data), self.anonymous)  # TRC -            handler = None                                             # TRC -            get_absolute_url = False -             -            if handler or fields: -                v = lambda f: getattr(data, f.attname) - -                if not fields: -                    """ -                    Fields was not specified, try to find teh correct -                    version in the typemapper we were sent. -                    """ -                    mapped = self.in_typemapper(type(data), self.anonymous) -                    get_fields = set(mapped.fields) -                    exclude_fields = set(mapped.exclude).difference(get_fields) -                 -                    if not get_fields: -                        get_fields = set([ f.attname.replace("_id", "", 1) -                            for f in data._meta.fields ]) -                 -                    # sets can be negated. -                    for exclude in exclude_fields: -                        if isinstance(exclude, basestring): -                            get_fields.discard(exclude) -                             -                        elif isinstance(exclude, re._pattern_type): -                            for field in get_fields.copy(): -                                if exclude.match(field): -                                    get_fields.discard(field) -                     -                    get_absolute_url = True - -                else: -                    get_fields = set(fields) -                    if 'absolute_url' in get_fields:   # MOVED (TRC) -                        get_absolute_url = True - -                met_fields = _method_fields(handler, get_fields)  # TRC - -                for f in data._meta.local_fields: -                    if f.serialize and not any([ p in met_fields for p in [ f.attname, f.name ]]): -                        if not f.rel: -                            if f.attname in get_fields: -                                ret[f.attname] = _any(v(f)) -                                get_fields.remove(f.attname) -                        else: -                            if f.attname[:-3] in get_fields: -                                ret[f.name] = _fk(data, f) -                                get_fields.remove(f.name) -                 -                for mf in data._meta.many_to_many: -                    if mf.serialize and mf.attname not in met_fields: -                        if mf.attname in get_fields: -                            ret[mf.name] = _m2m(data, mf) -                            get_fields.remove(mf.name) -                 -                # try to get the remainder of fields -                for maybe_field in get_fields: -                     -                    if isinstance(maybe_field, (list, tuple)): -                        model, fields = maybe_field -                        inst = getattr(data, model, None) - -                        if inst: -                            if hasattr(inst, 'all'): -                                ret[model] = _related(inst, fields) -                            elif callable(inst): -                                if len(inspect.getargspec(inst)[0]) == 1: -                                    ret[model] = _any(inst(), fields) -                            else: -                                ret[model] = _model(inst, fields) - -                    elif maybe_field in met_fields: -                        # Overriding normal field which has a "resource method" -                        # so you can alter the contents of certain fields without -                        # using different names. -                        ret[maybe_field] = _any(met_fields[maybe_field](data)) - -                    else:                     -                        maybe = getattr(data, maybe_field, None) -                        if maybe: -                            if callable(maybe): -                                if len(inspect.getargspec(maybe)[0]) == 1: -                                    ret[maybe_field] = _any(maybe()) -                            else: -                                ret[maybe_field] = _any(maybe) -                        else: -                            pass   # TRC -                            #handler_f = getattr(handler or self.handler, maybe_field, None) -                            # -                            #if handler_f: -                            #    ret[maybe_field] = _any(handler_f(data)) - -            else: -                # Add absolute_url if it exists -                get_absolute_url = True -                 -                # Add all the fields -                for f in data._meta.fields: -                    if f.attname != 'id': -                        ret[f.attname] = _any(getattr(data, f.attname)) -                 -                # Add all the propertiess -                klass = data.__class__ -                for attr in dir(klass): -                    if not attr.startswith('_') and not attr in ('pk','id') and isinstance(getattr(klass, attr, None), property): -                        #if attr.endswith('_url') or attr.endswith('_uri'): -                        #    ret[attr] = self.make_absolute(_any(getattr(data, attr))) -                        #else: -                        ret[attr] = _any(getattr(data, attr)) -                #fields = dir(data.__class__) + ret.keys() -                #add_ons = [k for k in dir(data) if k not in fields and not k.startswith('_')] -                #print add_ons -                ###print dir(data.__class__) -                #from django.db.models import Model -                #model_fields = dir(Model) - -                #for attr in dir(data): -                ##    #if attr.startswith('_'): -                ##    #    continue -                #    if (attr in fields) and not (attr in model_fields) and not attr.startswith('_'): -                #        print attr, type(getattr(data, attr, None)), attr in fields, attr in model_fields -                 -                #for k in add_ons: -                #    ret[k] = _any(getattr(data, k)) -             -            # TRC -            # resouce uri -            #if self.in_typemapper(type(data), self.anonymous): -            #    handler = self.in_typemapper(type(data), self.anonymous) -            #    if hasattr(handler, 'resource_uri'): -            #        url_id, fields = handler.resource_uri() -            #        ret['resource_uri'] = permalink( lambda: (url_id,  -            #            (getattr(data, f) for f in fields) ) )() -             -            # TRC -            #if hasattr(data, 'get_api_url') and 'resource_uri' not in ret: -            #    try: ret['resource_uri'] = data.get_api_url() -            #    except: pass -             -            # absolute uri -            if hasattr(data, 'get_absolute_url') and get_absolute_url: -                try: ret['absolute_url'] = data.get_absolute_url() -                except: pass -             -            #for key, val in ret.items(): -            #    if key.endswith('_url') or key.endswith('_uri'): -            #        ret[key] = self.add_domain(val) - -            return ret -         -        def _qs(data, fields=()): -            """ -            Querysets. -            """ -            return [ _any(v, fields) for v in data ] -                 -        def _list(data): -            """ -            Lists. -            """ -            return [ _any(v) for v in data ] -             -        def _dict(data): -            """ -            Dictionaries. -            """ -            return dict([ (k, _any(v)) for k, v in data.iteritems() ]) -             -        # Kickstart the seralizin'. -        return _any(data, self.fields) - - -    def post(self, request, auth, content, *args, **kwargs): -        # TODO: test creation on a non-existing resource url -         -        # translated related_field into related_field_id -        for related_name in [field.name for field in self.model._meta.fields if isinstance(field, RelatedField)]: -            if kwargs.has_key(related_name): -                kwargs[related_name + '_id'] = kwargs[related_name] -                del kwargs[related_name] - -        all_kw_args = dict(content.items() + kwargs.items()) -        if args: -            instance = self.model(pk=args[-1], **all_kw_args) -        else: -            instance = self.model(**all_kw_args) -        instance.save() -        headers = {} -        if hasattr(instance, 'get_absolute_url'): -            headers['Location'] = instance.get_absolute_url() -        return Response(status.HTTP_201_CREATED, instance, headers) - -    def get(self, request, auth, *args, **kwargs): -        try: -            if args: -                # If we have any none kwargs then assume the last represents the primrary key -                instance = self.model.objects.get(pk=args[-1], **kwargs) -            else: -                # Otherwise assume the kwargs uniquely identify the model -                instance = self.model.objects.get(**kwargs) -        except self.model.DoesNotExist: -            raise ResponseException(status.HTTP_404_NOT_FOUND) - -        return instance - -    def put(self, request, auth, content, *args, **kwargs): -        # TODO: update on the url of a non-existing resource url doesn't work correctly at the moment - will end up with a new url  -        try: -            if args: -                # If we have any none kwargs then assume the last represents the primrary key -                instance = self.model.objects.get(pk=args[-1], **kwargs) -            else: -                # Otherwise assume the kwargs uniquely identify the model -                instance = self.model.objects.get(**kwargs) - -            for (key, val) in content.items(): -                setattr(instance, key, val) -        except self.model.DoesNotExist: -            instance = self.model(**content) -            instance.save() - -        instance.save() -        return instance - -    def delete(self, request, auth, *args, **kwargs): -        try: -            if args: -                # If we have any none kwargs then assume the last represents the primrary key -                instance = self.model.objects.get(pk=args[-1], **kwargs) -            else: -                # Otherwise assume the kwargs uniquely identify the model -                instance = self.model.objects.get(**kwargs) -        except self.model.DoesNotExist: -            raise ResponseException(status.HTTP_404_NOT_FOUND, None, {}) - -        instance.delete() -        return -         - -class RootModelResource(ModelResource): -    """A Resource which provides default operations for list and create.""" -    allowed_methods = ('GET', 'POST') -    queryset = None - -    def get(self, request, auth, *args, **kwargs): -        queryset = self.queryset if self.queryset else self.model.objects.all() -        return queryset.filter(**kwargs) - - -class QueryModelResource(ModelResource): -    """Resource with default operations for list. -    TODO: provide filter/order/num_results/paging, and a create operation to create queries.""" -    allowed_methods = ('GET',) -    queryset = None - -    def get_form(self, data=None): -        return None - -    def get(self, request, auth, *args, **kwargs): -        queryset = self.queryset if self.queryset else self.model.objects.all() -        return queryset.filer(**kwargs) -  | 
