mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 16:07:38 +03:00 
			
		
		
		
	Sample example working
This commit is contained in:
		
							parent
							
								
									95ac2396d6
								
							
						
					
					
						commit
						42825e44e1
					
				|  | @ -18,7 +18,7 @@ class TemplatedEmitter(BaseEmitter): | |||
|     template = None | ||||
| 
 | ||||
|     def emit(self, output): | ||||
|         content = json.dumps(output, indent=4) | ||||
|         content = json.dumps(output, indent=4, sort_keys=True) | ||||
|         template = loader.get_template(self.template) | ||||
|         context = RequestContext(self.request, { | ||||
|             'content': content, | ||||
|  |  | |||
|  | @ -137,15 +137,20 @@ class Resource(object): | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     def determine_form(self, data=None): | ||||
|     def determine_form(self, input_data=None, return_data=None): | ||||
|         """Optionally return a Django Form instance, which may be used for validation | ||||
|         and/or rendered by an HTML/XHTML emitter. | ||||
|          | ||||
|         The data argument will be non Null if the form is required to be bound to some deserialized | ||||
|         input data, or Null if the form is required to be unbound.  | ||||
|         The input_data or return_data arguments can be used to bind the form either to the deserialized input, | ||||
|         or to a return object.  | ||||
|         """ | ||||
|         if self.form: | ||||
|             return self.form(data) | ||||
|             if input_data: | ||||
|                 return self.form(input_data) | ||||
|             elif return_data: | ||||
|                 return self.form(return_data) | ||||
|             else: | ||||
|                 return self.form() | ||||
|         return None | ||||
|    | ||||
|    | ||||
|  | @ -259,12 +264,13 @@ class Resource(object): | |||
|             if method in ('PUT', 'POST'): | ||||
|                 parser = self.determine_parser(request) | ||||
|                 data = parser(self, request).parse(request.raw_post_data) | ||||
|                 form = self.determine_form(data) | ||||
|                 form = self.determine_form(input_data=data) | ||||
|                 data = self.cleanup_request(data, form) | ||||
|                 (status, ret, headers) = func(data, request.META, *args, **kwargs) | ||||
| 
 | ||||
|             else: | ||||
|                 (status, ret, headers) = func(request.META, *args, **kwargs) | ||||
|                 form = self.determine_form(return_data=ret) | ||||
| 
 | ||||
| 
 | ||||
|         except ResourceException, exc: | ||||
|  | @ -274,7 +280,7 @@ class Resource(object): | |||
|         if emitter is None: | ||||
|             mimetype, emitter = self.emitters[0] | ||||
|          | ||||
|         # Use a form unbound to any data if one has not yet been created | ||||
|         # Create an unbound form if one has not yet been created | ||||
|         if form is None: | ||||
|             form = self.determine_form() | ||||
|          | ||||
|  | @ -284,7 +290,6 @@ class Resource(object): | |||
|         # Serialize the response content | ||||
|         ret = self.cleanup_response(ret) | ||||
|         content = emitter(self, request, status, headers, form).emit(ret) | ||||
|         print content | ||||
| 
 | ||||
|         # Build the HTTP Response | ||||
|         resp = HttpResponse(content, mimetype=mimetype, status=status) | ||||
|  | @ -308,7 +313,7 @@ class ModelResource(Resource): | |||
|     fields = None | ||||
|     form_fields = None | ||||
| 
 | ||||
|     def determine_form(self, data=None): | ||||
|     def determine_form(self, input_data=None, return_data=None): | ||||
|         """Return a form that may be used in validation and/or rendering an html emitter""" | ||||
|         if self.form: | ||||
|             return self.form | ||||
|  | @ -317,12 +322,14 @@ class ModelResource(Resource): | |||
|             class NewModelForm(ModelForm): | ||||
|                 class Meta: | ||||
|                     model = self.model | ||||
|                     fields = self.form_fields if self.form_fields else self.fields | ||||
|                     fields = self.form_fields if self.form_fields else None #self.fields | ||||
|                      | ||||
|             if data is None: | ||||
|                 return NewModelForm() | ||||
|             if input_data: | ||||
|                 return NewModelForm(input_data) | ||||
|             elif return_data: | ||||
|                 return NewModelForm(instance=return_data) | ||||
|             else: | ||||
|                 return NewModelForm(data) | ||||
|                 return NewModelForm() | ||||
|          | ||||
|         else: | ||||
|             return None | ||||
|  | @ -359,6 +366,12 @@ class ModelResource(Resource): | |||
|                 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): | ||||
|  | @ -417,7 +430,7 @@ class ModelResource(Resource): | |||
|             ret = { } | ||||
|             #handler = self.in_typemapper(type(data), self.anonymous)  # TRC | ||||
|             handler = None                                             # TRC | ||||
|             get_absolute_uri = False | ||||
|             get_absolute_url = False | ||||
|              | ||||
|             if handler or fields: | ||||
|                 v = lambda f: getattr(data, f.attname) | ||||
|  | @ -445,11 +458,12 @@ class ModelResource(Resource): | |||
|                                 if exclude.match(field): | ||||
|                                     get_fields.discard(field) | ||||
|                      | ||||
|                     get_absolute_url = True | ||||
| 
 | ||||
|                 else: | ||||
|                     get_fields = set(fields) | ||||
| 
 | ||||
|                 if 'absolute_uri' in get_fields:   # MOVED (TRC) | ||||
|                     get_absolute_uri = True | ||||
|                     if 'absolute_url' in get_fields:   # MOVED (TRC) | ||||
|                         get_absolute_url = True | ||||
| 
 | ||||
|                 met_fields = _method_fields(handler, get_fields)  # TRC | ||||
| 
 | ||||
|  | @ -508,14 +522,37 @@ class ModelResource(Resource): | |||
|                             #    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)) | ||||
|                  | ||||
|                 fields = dir(data.__class__) + ret.keys() | ||||
|                 add_ons = [k for k in dir(data) if k not in fields] | ||||
|                 # 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 k in add_ons: | ||||
|                     ret[k] = _any(getattr(data, k)) | ||||
|                 #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 | ||||
|  | @ -532,10 +569,14 @@ class ModelResource(Resource): | |||
|             #    except: pass | ||||
|              | ||||
|             # absolute uri | ||||
|             if hasattr(data, 'get_absolute_url') and get_absolute_uri: | ||||
|                 try: ret['absolute_uri'] = self.make_absolute(data.get_absolute_url()) | ||||
|             if hasattr(data, 'get_absolute_url') and get_absolute_url: | ||||
|                 try: ret['absolute_url'] = self.make_absolute(data.get_absolute_url()) | ||||
|                 except: pass | ||||
|              | ||||
|             for key, val in ret.items(): | ||||
|                 if key.endswith('_url') or key.endswith('_uri'): | ||||
|                     ret[key] = self.make_absolute(val) | ||||
| 
 | ||||
|             return ret | ||||
|          | ||||
|         def _qs(data, fields=()): | ||||
|  | @ -560,8 +601,9 @@ class ModelResource(Resource): | |||
|         return _any(data, self.fields) | ||||
| 
 | ||||
| 
 | ||||
|     def create(self, data, headers={}): | ||||
|         instance = self.model(**data) | ||||
|     def create(self, data, headers={}, *args, **kwargs): | ||||
|         all_kw_args = dict(data.items() + kwargs.items()) | ||||
|         instance = self.model(**all_kw_args) | ||||
|         instance.save() | ||||
|         headers = {} | ||||
|         if hasattr(instance, 'get_absolute_url'): | ||||
|  | @ -569,13 +611,22 @@ class ModelResource(Resource): | |||
|         return (201, instance, headers) | ||||
| 
 | ||||
|     def read(self, headers={}, *args, **kwargs): | ||||
|         try: | ||||
|             instance = self.model.objects.get(**kwargs) | ||||
|         except self.model.DoesNotExist: | ||||
|             return (404, '', {}) | ||||
| 
 | ||||
|         return (200, instance, {}) | ||||
| 
 | ||||
|     def update(self, data, headers={}, *args, **kwargs): | ||||
|         try: | ||||
|             instance = self.model.objects.get(**kwargs)     | ||||
|             for (key, val) in data.items(): | ||||
|                 setattr(instance, key, val) | ||||
|         except self.model.DoesNotExist: | ||||
|             instance = self.model(**data) | ||||
|             instance.save() | ||||
| 
 | ||||
|         instance.save() | ||||
|         return (200, instance, {}) | ||||
| 
 | ||||
|  | @ -583,3 +634,14 @@ class ModelResource(Resource): | |||
|         instance = self.model.objects.get(**kwargs) | ||||
|         instance.delete() | ||||
|         return (204, '', {}) | ||||
| 
 | ||||
| 
 | ||||
| class QueryModelResource(ModelResource): | ||||
|     allowed_methods = ('read',) | ||||
| 
 | ||||
|     def determine_form(self, input_data=None, return_data=None): | ||||
|         return None | ||||
| 
 | ||||
|     def read(self, headers={}, *args, **kwargs): | ||||
|         query = self.model.objects.all() | ||||
|         return (200, query, {}) | ||||
|  |  | |||
|  | @ -11,10 +11,10 @@ | |||
|   <body> | ||||
|     <h1>{{ resource_name }}</h1> | ||||
|     <p>{{ resource_doc }}</p> | ||||
|     <pre>{% autoescape off %}<b>{{ status }} {{ reason }}</b> | ||||
|     <pre><b>{{ status }} {{ reason }}</b>{% autoescape off %} | ||||
| {% for key, val in headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }} | ||||
| {% endfor %} | ||||
| {{ content|urlize_quoted_links }}{% endautoescape %}    </pre> | ||||
| {{ content|urlize_quoted_links }}    </pre>{% endautoescape %} | ||||
| 
 | ||||
| {% if 'read' in resource.allowed_operations %} | ||||
| 	<div class='action'> | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ html_gunk_re = re.compile(r'(?:<br clear="all">|<i><\/i>|<b><\/b>|<em><\/em>|<st | |||
| hard_coded_bullets_re = re.compile(r'((?:<p>(?:%s).*?[a-zA-Z].*?</p>\s*)+)' % '|'.join([re.escape(x) for x in DOTS]), re.DOTALL) | ||||
| trailing_empty_content_re = re.compile(r'(?:<p>(?: |\s|<br \/>)*?</p>\s*)+\Z') | ||||
| 
 | ||||
| def urlize_quoted_links(text, trim_url_limit=None, nofollow=False, autoescape=False): | ||||
| def urlize_quoted_links(text, trim_url_limit=None, nofollow=False, autoescape=True): | ||||
|     """ | ||||
|     Converts any URLs in text into clickable links. | ||||
| 
 | ||||
|  | @ -90,6 +90,10 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=False, autoescape=Fa | |||
|             words[i] = escape(word) | ||||
|     return u''.join(words) | ||||
| 
 | ||||
| 
 | ||||
| #urlize_quoted_links.needs_autoescape = True | ||||
| urlize_quoted_links.is_safe = True | ||||
| 
 | ||||
| # Register urlize_quoted_links as a custom filter | ||||
| # http://docs.djangoproject.com/en/dev/howto/custom-template-tags/ | ||||
| register = template.Library() | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							|  | @ -1,63 +1,90 @@ | |||
| from django.db import models | ||||
| from django.template.defaultfilters import slugify | ||||
| from datetime import datetime | ||||
| import uuid | ||||
| 
 | ||||
| def uuid_str(): | ||||
|     return str(uuid.uuid1()) | ||||
| 
 | ||||
| class ExampleModel(models.Model): | ||||
|     num = models.IntegerField(default=2, choices=((1,'one'), (2, 'two'))) | ||||
|     hidden_num = models.IntegerField(verbose_name='Something', help_text='HELP') | ||||
|     text = models.TextField(blank=False) | ||||
|     another = models.CharField(max_length=10) | ||||
| #class ExampleModel(models.Model): | ||||
| #    num = models.IntegerField(default=2, choices=((1,'one'), (2, 'two'))) | ||||
| #    hidden_num = models.IntegerField(verbose_name='Something', help_text='HELP') | ||||
| #    text = models.TextField(blank=False) | ||||
| #    another = models.CharField(max_length=10) | ||||
| 
 | ||||
| 
 | ||||
| class ExampleContainer(models.Model): | ||||
|     """Container.  Has a key, a name, and some internal data, and contains a set of items.""" | ||||
|     key = models.CharField(primary_key=True, default=uuid_str, max_length=36, editable=False) | ||||
|     name = models.CharField(max_length=256) | ||||
|     internal = models.IntegerField(default=0) | ||||
| #class ExampleContainer(models.Model): | ||||
| #    """Container.  Has a key, a name, and some internal data, and contains a set of items.""" | ||||
| #    key = models.CharField(primary_key=True, default=uuid_str, max_length=36, editable=False) | ||||
| #    name = models.CharField(max_length=256) | ||||
| #    internal = models.IntegerField(default=0) | ||||
| 
 | ||||
|     @models.permalink | ||||
|     def get_absolute_url(self): | ||||
|         return ('testapp.views.ContainerInstance', [self.key]) | ||||
| #    @models.permalink | ||||
| #    def get_absolute_url(self): | ||||
| #        return ('testapp.views.ContainerInstance', [self.key]) | ||||
| 
 | ||||
| 
 | ||||
| class ExampleItem(models.Model): | ||||
|     """Item.  Belongs to a container and has an index number and a note. | ||||
|     Items are uniquely identified by their container and index number.""" | ||||
|     container = models.ForeignKey(ExampleContainer, related_name='items') | ||||
|     index = models.IntegerField() | ||||
|     note = models.CharField(max_length=1024) | ||||
|     unique_together = (container, index) | ||||
| #class ExampleItem(models.Model): | ||||
| #    """Item.  Belongs to a container and has an index number and a note. | ||||
| #    Items are uniquely identified by their container and index number.""" | ||||
| #    container = models.ForeignKey(ExampleContainer, related_name='items') | ||||
| #    index = models.IntegerField() | ||||
| #    note = models.CharField(max_length=1024) | ||||
| #    unique_together = (container, index) | ||||
| 
 | ||||
| 
 | ||||
| RATING_CHOICES = ((0, 'Awful'), | ||||
|                   (1, 'Poor'), | ||||
|                   (2, 'OK'), | ||||
|                   (3, 'Good'), | ||||
|                   (4, 'Excellent')) | ||||
| 
 | ||||
| class BlogPost(models.Model): | ||||
|     slug = models.SlugField(editable=False, primary_key=True, default='blah') | ||||
|     title = models.CharField(max_length=128) | ||||
|     content = models.TextField() | ||||
|     when = models.DateTimeField(editable=False) | ||||
|     key = models.CharField(primary_key=True, max_length=64, default=uuid_str, editable=False) | ||||
|     title = models.CharField(max_length=128, help_text='The article title (Required)') | ||||
|     content = models.TextField(help_text='The article body (Required)') | ||||
|     created = models.DateTimeField(auto_now_add=True) | ||||
|     slug = models.SlugField(editable=False, default='') | ||||
| 
 | ||||
|     class Meta: | ||||
|         ordering = ('created',) | ||||
| 
 | ||||
|     @models.permalink | ||||
|     def get_absolute_url(self): | ||||
|         return ('testapp.views.BlogPostInstance', (self.slug,)) | ||||
|         return ('testapp.views.BlogPostInstance', (self.key,)) | ||||
| 
 | ||||
|     @property | ||||
|     @models.permalink | ||||
|     def comments_url(self): | ||||
|         """Link to a resource which lists all comments for this blog post.""" | ||||
|         return ('testapp.views.CommentList', (self.key,)) | ||||
| 
 | ||||
|     @property | ||||
|     @models.permalink | ||||
|     def comment_url(self): | ||||
|         """Link to a resource which can create a comment for this blog post.""" | ||||
|         return ('testapp.views.CommentCreator', (self.key,)) | ||||
| 
 | ||||
|     def __unicode__(self): | ||||
|         return self.title | ||||
| 
 | ||||
|     def save(self, *args, **kwargs): | ||||
|         self.slug = slugify(self.title) | ||||
|         self.when = datetime.now() | ||||
|         super(self.__class__, self).save(*args, **kwargs) | ||||
| 
 | ||||
| 
 | ||||
| class Comment(models.Model): | ||||
|     blogpost = models.ForeignKey(BlogPost, related_name='comments') | ||||
|     name = models.CharField(max_length=128) | ||||
|     content = models.TextField() | ||||
|     when = models.DateTimeField(auto_now_add=True) | ||||
|     blogpost = models.ForeignKey(BlogPost, editable=False, related_name='comments') | ||||
|     username = models.CharField(max_length=128, help_text='Please enter a username (Required)') | ||||
|     comment = models.TextField(help_text='Enter your comment here (Required)') | ||||
|     rating = models.IntegerField(blank=True, null=True, choices=RATING_CHOICES, help_text='Please rate the blog post (Optional)') | ||||
|     created = models.DateTimeField(auto_now_add=True) | ||||
| 
 | ||||
|     @models.permalink | ||||
|     def get_absolute_url(self): | ||||
|         return ('testapp.views.CommentInstance', (self.blogpost.slug, self.id)) | ||||
|         return ('testapp.views.CommentInstance', (self.blogpost.key, self.id)) | ||||
|      | ||||
|     @property | ||||
|     @models.permalink | ||||
|     def blogpost_url(self): | ||||
|         return ('testapp.views.BlogPostInstance', (self.blogpost.key,)) | ||||
|          | ||||
|     def save(self): | ||||
|         self.index = self.blogpost.comments.count() | ||||
|  | @ -1,8 +1,4 @@ | |||
| """ | ||||
| This file demonstrates two different styles of tests (one doctest and one | ||||
| unittest). These will both pass when you run "manage.py test". | ||||
| 
 | ||||
| Replace these with more appropriate tests for your application. | ||||
| """Test a range of REST API usage of the example application. | ||||
| """ | ||||
| 
 | ||||
| from django.test import TestCase | ||||
|  | @ -13,134 +9,154 @@ import json | |||
| 
 | ||||
| 
 | ||||
| class AcceptHeaderTests(TestCase): | ||||
|     def assert_accept_mimetype(self, mimetype, expect=None, expect_match=True): | ||||
|         """ | ||||
|         Assert that a request with given mimetype in the accept header, | ||||
|         gives a response with the appropriate content-type. | ||||
|         """ | ||||
|     """Test correct behaviour of the Accept header as specified by RFC 2616: | ||||
| 
 | ||||
|     http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1""" | ||||
| 
 | ||||
|     def assert_accept_mimetype(self, mimetype, expect=None): | ||||
|         """Assert that a request with given mimetype in the accept header, | ||||
|         gives a response with the appropriate content-type.""" | ||||
|         if expect is None: | ||||
|             expect = mimetype | ||||
| 
 | ||||
|         resp = self.client.get(reverse(views.ReadOnlyResource), HTTP_ACCEPT=mimetype) | ||||
|         resp = self.client.get(reverse(views.RootResource), HTTP_ACCEPT=mimetype) | ||||
| 
 | ||||
|         if expect_match: | ||||
|         self.assertEquals(resp['content-type'], expect) | ||||
|         else: | ||||
|             self.assertNotEquals(resp['content-type'], expect) | ||||
| 
 | ||||
|     def test_accept_xml(self): | ||||
|         self.assert_accept_mimetype('application/xml') | ||||
| 
 | ||||
|     def test_accept_json(self): | ||||
|         """Ensure server responds with Content-Type of JSON when requested.""" | ||||
|         self.assert_accept_mimetype('application/json') | ||||
| 
 | ||||
|     def test_accept_xml_prefered_to_json(self): | ||||
|         self.assert_accept_mimetype('application/xml,q=0.9;application/json,q=0.1', expect='application/xml') | ||||
|     def test_accept_xml(self): | ||||
|         """Ensure server responds with Content-Type of XML when requested.""" | ||||
|         self.assert_accept_mimetype('application/xml') | ||||
| 
 | ||||
|     def test_accept_json_prefered_to_xml(self): | ||||
|     def test_accept_json_when_prefered_to_xml(self): | ||||
|         """Ensure server responds with Content-Type of JSON when it is the client's prefered choice.""" | ||||
|         self.assert_accept_mimetype('application/json,q=0.9;application/xml,q=0.1', expect='application/json') | ||||
| 
 | ||||
|     def test_dont_accept_invalid(self): | ||||
|         self.assert_accept_mimetype('application/invalid', expect_match=False) | ||||
|     def test_accept_xml_when_prefered_to_json(self): | ||||
|         """Ensure server responds with Content-Type of XML when it is the client's prefered choice.""" | ||||
|         self.assert_accept_mimetype('application/xml,q=0.9;application/json,q=0.1', expect='application/xml') | ||||
| 
 | ||||
|     def test_default_json_prefered(self): | ||||
|         """Ensure server responds with JSON in preference to XML.""" | ||||
|         self.assert_accept_mimetype('application/json;application/xml', expect='application/json') | ||||
| 
 | ||||
|     def test_accept_generic_subtype_format(self): | ||||
|         """Ensure server responds with an appropriate type, when the subtype is left generic.""" | ||||
|         self.assert_accept_mimetype('text/*', expect='text/html') | ||||
| 
 | ||||
|     def test_accept_generic_type_format(self): | ||||
|         """Ensure server responds with an appropriate type, when the type and subtype are left generic.""" | ||||
|         self.assert_accept_mimetype('*/*', expect='application/json') | ||||
| 
 | ||||
|     def test_invalid_accept_header_returns_406(self): | ||||
|         resp = self.client.get(reverse(views.ReadOnlyResource), HTTP_ACCEPT='invalid/invalid') | ||||
|         """Ensure server returns a 406 (not acceptable) response if we set the Accept header to junk.""" | ||||
|         resp = self.client.get(reverse(views.RootResource), HTTP_ACCEPT='invalid/invalid') | ||||
|         self.assertNotEquals(resp['content-type'], 'invalid/invalid') | ||||
|         self.assertEquals(resp.status_code, 406) | ||||
|      | ||||
|     def test_prefer_specific(self): | ||||
|         self.fail("Test not implemented") | ||||
|     def test_prefer_specific_over_generic(self):   # This test is broken right now | ||||
|         """More specific accept types have precedence over less specific types.""" | ||||
|         self.assert_accept_mimetype('application/xml;*/*', expect='application/xml') | ||||
| 
 | ||||
| 
 | ||||
| class AllowedMethodsTests(TestCase): | ||||
|     def test_reading_read_only_allowed(self): | ||||
|         resp = self.client.get(reverse(views.ReadOnlyResource)) | ||||
|     """Basic tests to check that only allowed operations may be performed on a Resource""" | ||||
| 
 | ||||
|     def test_reading_a_read_only_resource_is_allowed(self): | ||||
|         """GET requests on a read only resource should default to a 200 (OK) response""" | ||||
|         resp = self.client.get(reverse(views.RootResource)) | ||||
|         self.assertEquals(resp.status_code, 200) | ||||
|          | ||||
|     def test_writing_read_only_not_allowed(self): | ||||
|         resp = self.client.put(reverse(views.ReadOnlyResource), {}) | ||||
|     def test_writing_to_read_only_resource_is_not_allowed(self): | ||||
|         """PUT requests on a read only resource should default to a 405 (method not allowed) response""" | ||||
|         resp = self.client.put(reverse(views.RootResource), {}) | ||||
|         self.assertEquals(resp.status_code, 405) | ||||
| 
 | ||||
|     def test_reading_write_only_not_allowed(self): | ||||
|         resp = self.client.get(reverse(views.WriteOnlyResource)) | ||||
|         self.assertEquals(resp.status_code, 405) | ||||
| 
 | ||||
|     def test_writing_write_only_allowed(self): | ||||
|         resp = self.client.put(reverse(views.WriteOnlyResource), {}) | ||||
|         self.assertEquals(resp.status_code, 200) | ||||
| 
 | ||||
| 
 | ||||
| class EncodeDecodeTests(TestCase): | ||||
|     def setUp(self): | ||||
|         super(self.__class__, self).setUp() | ||||
|         self.input = {'a': 1, 'b': 'example'} | ||||
| 
 | ||||
|     def test_encode_form_decode_json(self): | ||||
|         content = self.input | ||||
|         resp = self.client.put(reverse(views.WriteOnlyResource), content) | ||||
|         output = json.loads(resp.content) | ||||
|         self.assertEquals(self.input, output) | ||||
| 
 | ||||
|     def test_encode_json_decode_json(self): | ||||
|         content = json.dumps(self.input) | ||||
|         resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json') | ||||
|         output = json.loads(resp.content) | ||||
|         self.assertEquals(self.input, output) | ||||
| 
 | ||||
|     #def test_encode_xml_decode_json(self): | ||||
|     #    content = dict2xml(self.input) | ||||
|     #    resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/json') | ||||
|     #    output = json.loads(resp.content) | ||||
|     #    self.assertEquals(self.input, output) | ||||
| 
 | ||||
|     #def test_encode_form_decode_xml(self): | ||||
|     #    content = self.input | ||||
|     #    resp = self.client.put(reverse(views.WriteOnlyResource), content, HTTP_ACCEPT='application/xml') | ||||
|     #    output = xml2dict(resp.content) | ||||
|     #    self.assertEquals(self.input, output) | ||||
| 
 | ||||
|     #def test_encode_json_decode_xml(self): | ||||
|     #    content = json.dumps(self.input) | ||||
|     #    resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml') | ||||
|     #    output = xml2dict(resp.content) | ||||
|     #    self.assertEquals(self.input, output) | ||||
| 
 | ||||
|     #def test_encode_xml_decode_xml(self): | ||||
|     #    content = dict2xml(self.input) | ||||
|     #    resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml') | ||||
|     #    output = xml2dict(resp.content) | ||||
|     #    self.assertEquals(self.input, output) | ||||
| 
 | ||||
| class ModelTests(TestCase): | ||||
|     def test_create_container(self): | ||||
|         content = json.dumps({'name': 'example'}) | ||||
|         resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json') | ||||
|         output = json.loads(resp.content) | ||||
|         self.assertEquals(resp.status_code, 201) | ||||
|         self.assertEquals(output['name'], 'example') | ||||
|         self.assertEquals(set(output.keys()), set(('absolute_uri', 'name', 'key'))) | ||||
| 
 | ||||
| class CreatedModelTests(TestCase): | ||||
|     def setUp(self): | ||||
|         content = json.dumps({'name': 'example'}) | ||||
|         resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json', HTTP_ACCEPT='application/json') | ||||
|         self.container = json.loads(resp.content) | ||||
| 
 | ||||
|     def test_read_container(self): | ||||
|         resp = self.client.get(self.container["absolute_uri"]) | ||||
|         self.assertEquals(resp.status_code, 200) | ||||
|         container = json.loads(resp.content) | ||||
|         self.assertEquals(container, self.container) | ||||
| 
 | ||||
|     def test_delete_container(self): | ||||
|         resp = self.client.delete(self.container["absolute_uri"]) | ||||
|         self.assertEquals(resp.status_code, 204) | ||||
|         self.assertEquals(resp.content, '') | ||||
| 
 | ||||
|     def test_update_container(self): | ||||
|         self.container['name'] = 'new' | ||||
|         content = json.dumps(self.container) | ||||
|         resp = self.client.put(self.container["absolute_uri"], content, 'application/json') | ||||
|         self.assertEquals(resp.status_code, 200) | ||||
|         container = json.loads(resp.content) | ||||
|         self.assertEquals(container, self.container) | ||||
| # | ||||
| #    def test_reading_write_only_not_allowed(self): | ||||
| #        resp = self.client.get(reverse(views.WriteOnlyResource)) | ||||
| #        self.assertEquals(resp.status_code, 405) | ||||
| # | ||||
| #    def test_writing_write_only_allowed(self): | ||||
| #        resp = self.client.put(reverse(views.WriteOnlyResource), {}) | ||||
| #        self.assertEquals(resp.status_code, 200) | ||||
| # | ||||
| # | ||||
| #class EncodeDecodeTests(TestCase): | ||||
| #    def setUp(self): | ||||
| #        super(self.__class__, self).setUp() | ||||
| #        self.input = {'a': 1, 'b': 'example'} | ||||
| # | ||||
| #    def test_encode_form_decode_json(self): | ||||
| #        content = self.input | ||||
| #        resp = self.client.put(reverse(views.WriteOnlyResource), content) | ||||
| #        output = json.loads(resp.content) | ||||
| #        self.assertEquals(self.input, output) | ||||
| # | ||||
| #    def test_encode_json_decode_json(self): | ||||
| #        content = json.dumps(self.input) | ||||
| #        resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json') | ||||
| #        output = json.loads(resp.content) | ||||
| #        self.assertEquals(self.input, output) | ||||
| # | ||||
| #    #def test_encode_xml_decode_json(self): | ||||
| #    #    content = dict2xml(self.input) | ||||
| #    #    resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/json') | ||||
| #    #    output = json.loads(resp.content) | ||||
| #    #    self.assertEquals(self.input, output) | ||||
| # | ||||
| #    #def test_encode_form_decode_xml(self): | ||||
| #    #    content = self.input | ||||
| #    #    resp = self.client.put(reverse(views.WriteOnlyResource), content, HTTP_ACCEPT='application/xml') | ||||
| #    #    output = xml2dict(resp.content) | ||||
| #    #    self.assertEquals(self.input, output) | ||||
| # | ||||
| #    #def test_encode_json_decode_xml(self): | ||||
| #    #    content = json.dumps(self.input) | ||||
| #    #    resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml') | ||||
| #    #    output = xml2dict(resp.content) | ||||
| #    #    self.assertEquals(self.input, output) | ||||
| # | ||||
| #    #def test_encode_xml_decode_xml(self): | ||||
| #    #    content = dict2xml(self.input) | ||||
| #    #    resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml') | ||||
| #    #    output = xml2dict(resp.content) | ||||
| #    #    self.assertEquals(self.input, output) | ||||
| # | ||||
| #class ModelTests(TestCase): | ||||
| #    def test_create_container(self): | ||||
| #        content = json.dumps({'name': 'example'}) | ||||
| #        resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json') | ||||
| #        output = json.loads(resp.content) | ||||
| #        self.assertEquals(resp.status_code, 201) | ||||
| #        self.assertEquals(output['name'], 'example') | ||||
| #        self.assertEquals(set(output.keys()), set(('absolute_uri', 'name', 'key'))) | ||||
| # | ||||
| #class CreatedModelTests(TestCase): | ||||
| #    def setUp(self): | ||||
| #        content = json.dumps({'name': 'example'}) | ||||
| #        resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json', HTTP_ACCEPT='application/json') | ||||
| #        self.container = json.loads(resp.content) | ||||
| # | ||||
| #    def test_read_container(self): | ||||
| #        resp = self.client.get(self.container["absolute_uri"]) | ||||
| #        self.assertEquals(resp.status_code, 200) | ||||
| #        container = json.loads(resp.content) | ||||
| #        self.assertEquals(container, self.container) | ||||
| # | ||||
| #    def test_delete_container(self): | ||||
| #        resp = self.client.delete(self.container["absolute_uri"]) | ||||
| #        self.assertEquals(resp.status_code, 204) | ||||
| #        self.assertEquals(resp.content, '') | ||||
| # | ||||
| #    def test_update_container(self): | ||||
| #        self.container['name'] = 'new' | ||||
| #        content = json.dumps(self.container) | ||||
| #        resp = self.client.put(self.container["absolute_uri"], content, 'application/json') | ||||
| #        self.assertEquals(resp.status_code, 200) | ||||
| #        container = json.loads(resp.content) | ||||
| #        self.assertEquals(container, self.container) | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,13 +2,18 @@ from django.conf.urls.defaults import patterns | |||
| 
 | ||||
| urlpatterns = patterns('testapp.views', | ||||
|     (r'^$', 'RootResource'), | ||||
|     (r'^read-only$', 'ReadOnlyResource'), | ||||
|     (r'^write-only$', 'WriteOnlyResource'), | ||||
|     (r'^read-write$', 'ReadWriteResource'), | ||||
|     (r'^model$', 'ModelFormResource'), | ||||
|     (r'^container$', 'ContainerFactory'), | ||||
|     (r'^container/((?P<key>[^/]+))$', 'ContainerInstance'), | ||||
|     #(r'^read-only$', 'ReadOnlyResource'), | ||||
|     #(r'^write-only$', 'WriteOnlyResource'), | ||||
|     #(r'^read-write$', 'ReadWriteResource'), | ||||
|     #(r'^model$', 'ModelFormResource'), | ||||
|     #(r'^container$', 'ContainerFactory'), | ||||
|     #(r'^container/((?P<key>[^/]+))$', 'ContainerInstance'), | ||||
|      | ||||
|     (r'^blogpost/create$', 'BlogPostCreator'), | ||||
|     (r'^blogposts/(?P<slug>[^/]+)', 'BlogPostInstance'), | ||||
|     (r'^blog-posts/$', 'BlogPostList'), | ||||
|     (r'^blog-post/$', 'BlogPostCreator'), | ||||
|     (r'^blog-post/(?P<key>[^/]+)/$', 'BlogPostInstance'), | ||||
| 
 | ||||
|     (r'^blog-post/(?P<blogpost_id>[^/]+)/comments/$', 'CommentList'), | ||||
|     (r'^blog-post/(?P<blogpost_id>[^/]+)/comment/$', 'CommentCreator'), | ||||
|     (r'^blog-post/(?P<blogpost>[^/]+)/comments/(?P<id>[^/]+)/$', 'CommentInstance'), | ||||
| ) | ||||
|  |  | |||
|  | @ -1,78 +1,111 @@ | |||
| from rest.resource import Resource, ModelResource | ||||
| from testapp.forms import ExampleForm | ||||
| from testapp.models import ExampleModel, ExampleContainer, BlogPost, Comment | ||||
| from rest.resource import Resource, ModelResource, QueryModelResource | ||||
| from testapp.models import BlogPost, Comment | ||||
|   | ||||
| class RootResource(Resource): | ||||
|     """This is my docstring | ||||
|     """ | ||||
|     """This is the top level resource for the API. | ||||
|     All the sub-resources are discoverable from here.""" | ||||
|     allowed_operations = ('read',) | ||||
| 
 | ||||
|     def read(self, headers={}, *args, **kwargs): | ||||
|         return (200, {'read-only-api': self.reverse(ReadOnlyResource), | ||||
|                       'write-only-api': self.reverse(WriteOnlyResource), | ||||
|                       'read-write-api': self.reverse(ReadWriteResource), | ||||
|                       'model-api': self.reverse(ModelFormResource), | ||||
|                       'create-container': self.reverse(ContainerFactory), | ||||
|                       'blog-post-creator': self.reverse(BlogPostCreator)}, {}) | ||||
|         return (200, {'blog-posts': self.reverse(BlogPostList), | ||||
|                       'blog-post': self.reverse(BlogPostCreator)}, {}) | ||||
| 
 | ||||
| 
 | ||||
| class ReadOnlyResource(Resource): | ||||
|     """This is my docstring | ||||
|     """ | ||||
|     allowed_operations = ('read',) | ||||
| # Blog Post Resources | ||||
| 
 | ||||
|     def read(self, headers={}, *args, **kwargs): | ||||
|         return (200, {'ExampleString': 'Example', | ||||
|                       'ExampleInt': 1, | ||||
|                       'ExampleDecimal': 1.0}, {}) | ||||
| 
 | ||||
| 
 | ||||
| class WriteOnlyResource(Resource): | ||||
|     """This is my docstring | ||||
|     """ | ||||
|     allowed_operations = ('update',) | ||||
| 
 | ||||
|     def update(self, data, headers={}, *args, **kwargs): | ||||
|         return (200, data, {}) | ||||
| 
 | ||||
| 
 | ||||
| class ReadWriteResource(Resource): | ||||
|     allowed_operations = ('read', 'update', 'delete') | ||||
|     create_form = ExampleForm | ||||
|     update_form = ExampleForm | ||||
| 
 | ||||
| 
 | ||||
| class ModelFormResource(ModelResource): | ||||
|     allowed_operations = ('read', 'update', 'delete') | ||||
|     model = ExampleModel | ||||
| 
 | ||||
| # Nice things: form validation is applied to any input type | ||||
| #              html forms for output | ||||
| #              output always serialized nicely | ||||
| class ContainerFactory(ModelResource): | ||||
|     allowed_operations = ('create',) | ||||
|     model = ExampleContainer | ||||
|     fields = ('absolute_uri', 'name', 'key') | ||||
|     form_fields = ('name',) | ||||
| 
 | ||||
| 
 | ||||
| class ContainerInstance(ModelResource): | ||||
|     allowed_operations = ('read', 'update', 'delete') | ||||
|     model = ExampleContainer | ||||
|     fields = ('absolute_uri', 'name', 'key') | ||||
|     form_fields = ('name',) | ||||
| 
 | ||||
| ####################### | ||||
| class BlogPostList(QueryModelResource): | ||||
|     """A resource which lists all existing blog posts.""" | ||||
|     allowed_operations = ('read', ) | ||||
|     model = BlogPost | ||||
| 
 | ||||
| 
 | ||||
| class BlogPostCreator(ModelResource): | ||||
|     """A Resource with which blog posts may be created. | ||||
|     This is distinct from blog post instance so that it is discoverable by the client. | ||||
|     (ie the client doens't need to know how to form a blog post url in order to create a blog post)""" | ||||
|     """A resource with which blog posts may be created.""" | ||||
|     allowed_operations = ('create',) | ||||
|     model = BlogPost | ||||
|     fields = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url') | ||||
| 
 | ||||
| 
 | ||||
| class BlogPostInstance(ModelResource): | ||||
|     """Represents a single Blog Post.""" | ||||
|     """A resource which represents a single blog post.""" | ||||
|     allowed_operations = ('read', 'update', 'delete') | ||||
|     model = BlogPost | ||||
|     fields = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url') | ||||
| 
 | ||||
| 
 | ||||
| # Comment Resources | ||||
| 
 | ||||
| class CommentList(QueryModelResource): | ||||
|     """A resource which lists all existing comments for a given blog post.""" | ||||
|     allowed_operations = ('read', ) | ||||
|     model = Comment | ||||
| 
 | ||||
| 
 | ||||
| class CommentCreator(ModelResource): | ||||
|     """A resource with which blog comments may be created for a given blog post.""" | ||||
|     allowed_operations = ('create',) | ||||
|     model = Comment | ||||
|     fields = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url') | ||||
| 
 | ||||
| 
 | ||||
| class CommentInstance(ModelResource): | ||||
|     """A resource which represents a single comment.""" | ||||
|     allowed_operations = ('read', 'update', 'delete') | ||||
|     model = Comment | ||||
|     fields = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url') | ||||
|      | ||||
| # | ||||
| #'read-only-api': self.reverse(ReadOnlyResource), | ||||
| #                      'write-only-api': self.reverse(WriteOnlyResource), | ||||
| #                      'read-write-api': self.reverse(ReadWriteResource), | ||||
| #                      'model-api': self.reverse(ModelFormResource), | ||||
| #                      'create-container': self.reverse(ContainerFactory), | ||||
| # | ||||
| #class ReadOnlyResource(Resource): | ||||
| #    """This is my docstring | ||||
| #    """ | ||||
| #    allowed_operations = ('read',) | ||||
| # | ||||
| #    def read(self, headers={}, *args, **kwargs): | ||||
| #        return (200, {'ExampleString': 'Example', | ||||
| #                      'ExampleInt': 1, | ||||
| #                      'ExampleDecimal': 1.0}, {}) | ||||
| # | ||||
| # | ||||
| #class WriteOnlyResource(Resource): | ||||
| #    """This is my docstring | ||||
| #    """ | ||||
| #    allowed_operations = ('update',) | ||||
| # | ||||
| #    def update(self, data, headers={}, *args, **kwargs): | ||||
| #        return (200, data, {}) | ||||
| # | ||||
| # | ||||
| #class ReadWriteResource(Resource): | ||||
| #    allowed_operations = ('read', 'update', 'delete') | ||||
| #    create_form = ExampleForm | ||||
| #    update_form = ExampleForm | ||||
| # | ||||
| # | ||||
| #class ModelFormResource(ModelResource): | ||||
| #    allowed_operations = ('read', 'update', 'delete') | ||||
| #    model = ExampleModel | ||||
| # | ||||
| ## Nice things: form validation is applied to any input type | ||||
| ##              html forms for output | ||||
| ##              output always serialized nicely | ||||
| #class ContainerFactory(ModelResource): | ||||
| #    allowed_operations = ('create',) | ||||
| #    model = ExampleContainer | ||||
| #    fields = ('absolute_uri', 'name', 'key') | ||||
| #    form_fields = ('name',) | ||||
| # | ||||
| # | ||||
| #class ContainerInstance(ModelResource): | ||||
| #    allowed_operations = ('read', 'update', 'delete') | ||||
| #    model = ExampleContainer | ||||
| #    fields = ('absolute_uri', 'name', 'key') | ||||
| #    form_fields = ('name',) | ||||
| 
 | ||||
| ####################### | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user