resolving merge between ephelon's fork and the rest, all tests pass
This commit is contained in:
Коммит
15202e9b15
|
@ -16,3 +16,5 @@ Brian McMurray for contributing a patch for #41
|
|||
James Emerton for making the OAuth parts more usable/friendly
|
||||
Anton Tsigularov for providing a patch for incorrect multipart detection
|
||||
Remco Wendt for fixing up the example blog server to conform with 0.2.2, et. al
|
||||
Benoit Garret for providing a fix for oauth headers, issue #56
|
||||
Stephan Preeker for providing some fixes to documentation generation
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import inspect, handler
|
||||
|
||||
from piston.handler import typemapper
|
||||
from piston.handler import handler_tracker
|
||||
|
||||
from django.core.urlresolvers import get_resolver, get_callable, get_script_prefix
|
||||
from django.shortcuts import render_to_response
|
||||
|
@ -36,7 +37,8 @@ class HandlerMethod(object):
|
|||
else:
|
||||
yield (arg, None)
|
||||
|
||||
def get_signature(self, parse_optional=True):
|
||||
@property
|
||||
def signature(self, parse_optional=True):
|
||||
spec = ""
|
||||
|
||||
for argn, argdef in self.iter_args():
|
||||
|
@ -53,18 +55,25 @@ class HandlerMethod(object):
|
|||
return spec.replace("=None", "=<optional>")
|
||||
|
||||
return spec
|
||||
|
||||
signature = property(get_signature)
|
||||
|
||||
def get_doc(self):
|
||||
@property
|
||||
def doc(self):
|
||||
return inspect.getdoc(self.method)
|
||||
|
||||
doc = property(get_doc)
|
||||
|
||||
def get_name(self):
|
||||
@property
|
||||
def name(self):
|
||||
return self.method.__name__
|
||||
|
||||
name = property(get_name)
|
||||
|
||||
@property
|
||||
def http_name(self):
|
||||
if self.name == 'read':
|
||||
return 'GET'
|
||||
elif self.name == 'create':
|
||||
return 'POST'
|
||||
elif self.name == 'delete':
|
||||
return 'DELETE'
|
||||
elif self.name == 'update':
|
||||
return 'PUT'
|
||||
|
||||
def __repr__(self):
|
||||
return "<Method: %s>" % self.name
|
||||
|
@ -97,11 +106,19 @@ class HandlerDocumentation(object):
|
|||
def get_model(self):
|
||||
return getattr(self, 'model', None)
|
||||
|
||||
def get_doc(self):
|
||||
@property
|
||||
def has_anonymous(self):
|
||||
return self.handler.anonymous
|
||||
|
||||
@property
|
||||
def anonymous(self):
|
||||
if self.has_anonymous:
|
||||
return HandlerDocumentation(self.handler.anonymous)
|
||||
|
||||
@property
|
||||
def doc(self):
|
||||
return self.handler.__doc__
|
||||
|
||||
doc = property(get_doc)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.handler.__name__
|
||||
|
@ -159,8 +176,16 @@ def documentation_view(request):
|
|||
"""
|
||||
docs = [ ]
|
||||
|
||||
for handler, (model, anonymous) in typemapper.iteritems():
|
||||
for handler in handler_tracker:
|
||||
docs.append(generate_doc(handler))
|
||||
|
||||
|
||||
def _compare(doc1, doc2):
|
||||
#handlers and their anonymous counterparts are put next to each other.
|
||||
name1 = doc1.name.replace("Anonymous", "")
|
||||
name2 = doc2.name.replace("Anonymous", "")
|
||||
return cmp(name1, name2)
|
||||
|
||||
docs.sort(_compare)
|
||||
|
||||
return render_to_response('documentation.html',
|
||||
{ 'docs': docs }, RequestContext(request))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import generators
|
||||
|
||||
import decimal, re, inspect
|
||||
import copy
|
||||
|
||||
try:
|
||||
# yaml isn't standard with python. It shouldn't be required if it
|
||||
|
@ -68,7 +69,7 @@ class Emitter(object):
|
|||
ret = dict()
|
||||
|
||||
for field in fields:
|
||||
if field in has:
|
||||
if field in has and callable(field):
|
||||
ret[field] = getattr(data, field)
|
||||
|
||||
return ret
|
||||
|
@ -171,7 +172,7 @@ class Emitter(object):
|
|||
get_fields = set(fields)
|
||||
|
||||
met_fields = self.method_fields(handler, get_fields)
|
||||
|
||||
|
||||
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:
|
||||
|
@ -191,7 +192,6 @@ class Emitter(object):
|
|||
|
||||
# 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)
|
||||
|
|
|
@ -23,7 +23,7 @@ class ModelForm(forms.ModelForm):
|
|||
|
||||
class OAuthAuthenticationForm(forms.Form):
|
||||
oauth_token = forms.CharField(widget=forms.HiddenInput)
|
||||
oauth_callback = forms.URLField(widget=forms.HiddenInput)
|
||||
oauth_callback = forms.CharField(widget=forms.HiddenInput)
|
||||
authorize_access = forms.BooleanField(required=True)
|
||||
csrf_signature = forms.CharField(widget=forms.HiddenInput)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ from utils import rc
|
|||
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||
|
||||
typemapper = { }
|
||||
handler_tracker = [ ]
|
||||
|
||||
class HandlerMetaClass(type):
|
||||
"""
|
||||
|
@ -14,6 +15,9 @@ class HandlerMetaClass(type):
|
|||
if hasattr(new_cls, 'model'):
|
||||
typemapper[new_cls] = (new_cls.model, new_cls.is_anonymous)
|
||||
|
||||
if name not in ('BaseHandler', 'AnonymousBaseHandler'):
|
||||
handler_tracker.append(new_cls)
|
||||
|
||||
return new_cls
|
||||
|
||||
class BaseHandler(object):
|
||||
|
|
|
@ -247,10 +247,11 @@ class OAuthRequest(object):
|
|||
@staticmethod
|
||||
def _split_header(header):
|
||||
params = {}
|
||||
header = header.replace('OAuth ', '', 1)
|
||||
parts = header.split(',')
|
||||
for param in parts:
|
||||
# ignore realm parameter
|
||||
if param.find('OAuth realm') > -1:
|
||||
if param.find('realm') > -1:
|
||||
continue
|
||||
# remove whitespace
|
||||
param = param.strip()
|
||||
|
|
|
@ -3,11 +3,10 @@ from django.core.paginator import Paginator
|
|||
from piston.handler import BaseHandler
|
||||
from piston.utils import rc, validate
|
||||
|
||||
from models import TestModel, ExpressiveTestModel, Comment, InheritedModel, PlainOldObject, ListFieldsModel
|
||||
from models import TestModel, ExpressiveTestModel, Comment, InheritedModel, PlainOldObject, Issue58Model, ListFieldsModel
|
||||
from forms import EchoForm
|
||||
from test_project.apps.testapp import signals
|
||||
|
||||
|
||||
class EntryHandler(BaseHandler):
|
||||
model = TestModel
|
||||
allowed_methods = ['GET', 'PUT', 'POST']
|
||||
|
@ -82,3 +81,17 @@ class ListFieldsHandler(BaseHandler):
|
|||
fields = ('id','kind','variety','color')
|
||||
list_fields = ('id','variety')
|
||||
|
||||
class Issue58Handler(BaseHandler):
|
||||
model = Issue58Model
|
||||
|
||||
def read(self, request):
|
||||
return Issue58Model.objects.all()
|
||||
|
||||
def create(self, request):
|
||||
if request.content_type:
|
||||
data = request.data
|
||||
em = self.model(read=data['read'], model=data['model'])
|
||||
em.save()
|
||||
return rc.CREATED
|
||||
else:
|
||||
super(Issue58Model, self).create(request)
|
||||
|
|
|
@ -30,8 +30,11 @@ class PlainOldObject(object):
|
|||
return {'type': 'plain',
|
||||
'field': 'a field'}
|
||||
|
||||
|
||||
class ListFieldsModel(models.Model):
|
||||
kind = models.CharField(max_length=15)
|
||||
variety = models.CharField(max_length=15)
|
||||
color = models.CharField(max_length=15)
|
||||
|
||||
class Issue58Model(models.Model):
|
||||
read = models.BooleanField(default=False)
|
||||
model = models.CharField(max_length=1, blank=True, null=True)
|
||||
|
|
|
@ -15,7 +15,7 @@ except ImportError:
|
|||
|
||||
import urllib, base64
|
||||
|
||||
from test_project.apps.testapp.models import TestModel, ExpressiveTestModel, Comment, InheritedModel, ListFieldsModel
|
||||
from test_project.apps.testapp.models import TestModel, ExpressiveTestModel, Comment, InheritedModel, Issue58Model, ListFieldsModel
|
||||
from test_project.apps.testapp import signals
|
||||
|
||||
class MainTests(TestCase):
|
||||
|
@ -370,8 +370,6 @@ class PlainOldObject(MainTests):
|
|||
resp = self.client.get('/api/popo')
|
||||
self.assertEquals(resp.status_code, 200)
|
||||
self.assertEquals({'type': 'plain', 'field': 'a field'}, simplejson.loads(resp.content))
|
||||
|
||||
|
||||
|
||||
class ListFieldsTest(MainTests):
|
||||
def init_delegate(self):
|
||||
|
@ -410,3 +408,39 @@ class ListFieldsTest(MainTests):
|
|||
self.assertEquals(resp.status_code, 200)
|
||||
self.assertEquals(resp.content, expect)
|
||||
|
||||
class Issue58ModelTests(MainTests):
|
||||
"""
|
||||
This testcase addresses #58 in django-piston where if a model
|
||||
has one of the ['read','update','delete','create'] defined
|
||||
it make piston crash with a `TypeError`
|
||||
"""
|
||||
def init_delegate(self):
|
||||
m1 = Issue58Model(read=True,model='t')
|
||||
m1.save()
|
||||
m2 = Issue58Model(read=False,model='f')
|
||||
m2.save()
|
||||
|
||||
def test_incoming_json(self):
|
||||
outgoing = simplejson.dumps({ 'read': True, 'model': 'T'})
|
||||
|
||||
expected = """[
|
||||
{
|
||||
"read": true,
|
||||
"model": "t"
|
||||
},
|
||||
{
|
||||
"read": false,
|
||||
"model": "f"
|
||||
}
|
||||
]"""
|
||||
|
||||
# test GET
|
||||
result = self.client.get('/api/issue58.json',
|
||||
HTTP_AUTHORIZATION=self.auth_string).content
|
||||
self.assertEquals(result, expected)
|
||||
|
||||
# test POST
|
||||
resp = self.client.post('/api/issue58.json', outgoing, content_type='application/json',
|
||||
HTTP_AUTHORIZATION=self.auth_string)
|
||||
self.assertEquals(resp.status_code, 201)
|
||||
|
|
@ -2,7 +2,7 @@ from django.conf.urls.defaults import *
|
|||
from piston.resource import Resource
|
||||
from piston.authentication import HttpBasicAuthentication
|
||||
|
||||
from test_project.apps.testapp.handlers import EntryHandler, ExpressiveHandler, AbstractHandler, EchoHandler, PlainOldObjectHandler, ListFieldsHandler
|
||||
from test_project.apps.testapp.handlers import EntryHandler, ExpressiveHandler, AbstractHandler, EchoHandler, PlainOldObjectHandler, Issue58Handler, ListFieldsHandler
|
||||
|
||||
auth = HttpBasicAuthentication(realm='TestApplication')
|
||||
|
||||
|
@ -12,7 +12,7 @@ abstract = Resource(handler=AbstractHandler, authentication=auth)
|
|||
echo = Resource(handler=EchoHandler)
|
||||
popo = Resource(handler=PlainOldObjectHandler)
|
||||
list_fields = Resource(handler=ListFieldsHandler)
|
||||
|
||||
issue58 = Resource(handler=Issue58Handler)
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^entries/$', entries),
|
||||
|
@ -20,6 +20,8 @@ urlpatterns = patterns('',
|
|||
url(r'^entries\.(?P<emitter_format>.+)', entries),
|
||||
url(r'^entry-(?P<pk>.+)\.(?P<emitter_format>.+)', entries),
|
||||
|
||||
url(r'^issue58\.(?P<emitter_format>.+)$', issue58),
|
||||
|
||||
url(r'^expressive\.(?P<emitter_format>.+)$', expressive),
|
||||
|
||||
url(r'^abstract\.(?P<emitter_format>.+)$', abstract),
|
||||
|
|
Загрузка…
Ссылка в новой задаче