Browse Source

implement converting Django forms to JS and feeding them with data. todo: submit

Natenom/support-murmur-13-1446181288462
Michael Ziegler 14 years ago
parent
commit
bb20a44272
  1. 173
      pyweb/extdirect.py

173
pyweb/extdirect.py

@ -21,12 +21,14 @@ import functools
import traceback
from sys import stderr
from django import forms
from django.http import HttpResponse
from django.conf import settings
from django.conf.urls.defaults import patterns
from django.conf.urls.defaults import patterns, url
from django.core.urlresolvers import reverse
from django.utils.datastructures import MultiValueDictKeyError
from django.views.decorators.csrf import csrf_exempt
from django.utils.safestring import mark_safe
def getname( cls_or_name ):
@ -34,6 +36,33 @@ def getname( cls_or_name ):
return cls_or_name.__name__
return cls_or_name
# Template used for the auto-generated form classes
EXT_CLASS_TEMPLATE = """
Ext.namespace('Ext.ux');
Ext.ux.%(clsname)s = function( config ){
Ext.apply( this, config );
var defaultconf = %(defaultconf)s;
Ext.applyIf( this, defaultconf );
this.initialConfig = defaultconf;
Ext.ux.%(clsname)s.superclass.constructor.call( this );
this.form.api = %(apiconf)s;
this.form.paramsAsHash = true;
}
Ext.extend( Ext.ux.%(clsname)s, Ext.form.FormPanel, {} );
Ext.reg( '%(clslowername)s', Ext.ux.%(clsname)s );
"""
# About the this.form.* lines, see
# http://www.sencha.com/forum/showthread.php?96001-solved-Ext.Direct-load-data-in-extended-Form-fails-%28scope-issue%29
class Provider( object ):
""" Provider for Ext.Direct. This class handles building API information and
routing requests to the appropriate functions, and serializing their
@ -83,26 +112,52 @@ class Provider( object ):
self.name = name
self.autoadd = autoadd
self.classes = {}
self.forms = {}
def register_method( self, cls_or_name ):
def register_method( self, cls_or_name, flags={} ):
""" Return a function that takes a method as an argument and adds that
to cls_or_name.
The flags parameter is for additional information, e.g. formHandler=True.
Note: This decorator does not replace the method by a new function,
it returns the original function as-is.
"""
return functools.partial( self._register_method, cls_or_name, flags=flags )
def _register_method( self, cls_or_name, method, flags={} ):
""" Actually registers the given function as a method of cls_or_name. """
clsname = getname(cls_or_name)
if clsname not in self.classes:
self.classes[clsname] = {}
return functools.partial( self._register_method, cls_or_name )
def _register_method( self, cls_or_name, method ):
""" Actually registers the given function as a method of cls_or_name. """
self.classes[ getname(cls_or_name) ][ method.__name__ ] = method
self.classes[ clsname ][ method.__name__ ] = method
method.EXT_argnames = inspect.getargspec( method ).args[1:]
method.EXT_len = len( method.EXT_argnames )
method.EXT_flags = flags
return method
def register_form( self, formclass ):
""" Register a Django Form class for handling teh stuffz, yoo know. """
formname = formclass.__name__.lower()
self.forms[formname] = formclass
getfunc = functools.partial( self.get_form_data, formname )
getfunc.EXT_len = 1
getfunc.EXT_argnames = ["pk"]
getfunc.EXT_flags = {}
updatefunc = functools.partial( self.update_form_data, formname )
updatefunc.EXT_len = 1
updatefunc.EXT_argnames = ["pk"]
updatefunc.EXT_flags = { 'formHandler': True }
self.classes["XD_%s"%formclass.__name__] = {
"get": getfunc,
"update": updatefunc,
}
return formclass
@csrf_exempt
def get_api( self, request ):
""" Introspect the methods and get a JSON description of this API. """
@ -110,10 +165,12 @@ class Provider( object ):
for clsname in self.classes:
actdict[clsname] = []
for methodname in self.classes[clsname]:
actdict[clsname].append( {
methinfo = {
"name": methodname,
"len": self.classes[clsname][methodname].EXT_len
} )
}
methinfo.update( self.classes[clsname][methodname].EXT_flags )
actdict[clsname].append( methinfo )
lines = ["%s = %s;" % ( self.name, simplejson.dumps({
"url": reverse( self.request ),
@ -137,10 +194,14 @@ class Provider( object ):
rawjson = [{
'action': request.POST['extAction'],
'method': request.POST['extMethod'],
'data': request.POST['extData'],
'type': request.POST['extType'],
'tid': request.POST['extTID'],
'data': {}
}]
for fld in request.POST:
if not fld.startswith( "ext" ):
rawjson[0]['data'][fld] = request.POST[fld]
except (MultiValueDictKeyError, KeyError):
rawjson = simplejson.loads( request.raw_post_data )
if not isinstance( rawjson, list ):
@ -221,10 +282,100 @@ class Provider( object ):
else:
return HttpResponse( simplejson.dumps( responses ), mimetype="text/javascript" )
def get_form( self, request, formname ):
""" Convert the form given in "formname" to an ExtJS FormPanel. """
items = []
clsname = self.forms[formname].__name__
for fldname in self.forms[formname].base_fields:
field = self.forms[formname].base_fields[fldname]
extfld = {
"fieldLabel": field.label is not None and unicode(field.label) or fldname,
"name": fldname,
"xtype": "textfield",
#"allowEmpty": field.required,
}
if hasattr( field, "choices" ) and field.choices:
extfld.update({
"name": fldname,
"hiddenName": fldname,
"xtype": "combo",
"store": field.choices,
"typeAhead": True,
"emptyText": 'Select...',
"triggerAction": 'all',
"selectOnFocus": True,
})
elif isinstance( field, forms.BooleanField ):
extfld.update({
"xtype": "checkbox"
})
elif isinstance( field, forms.IntegerField ):
extfld.update({
"xtype": "numberfield",
})
elif isinstance( field, forms.FileField ) or isinstance( field, forms.ImageField ):
extfld.update({
"xtype": "textfield",
"inputType": "file"
})
elif isinstance( field.widget, forms.Textarea ):
extfld.update({
"xtype": "textarea",
})
elif isinstance( field.widget, forms.PasswordInput ):
extfld.update({
"xtype": "textfield",
"inputType": "password"
})
items.append( extfld )
if field.help_text:
items.append({
"xtype": "label",
"text": unicode(field.help_text),
"cls": "form_hint_label",
})
clscode = EXT_CLASS_TEMPLATE % {
'clsname': clsname,
'clslowername': formname,
'defaultconf': simplejson.dumps({
'items': items,
'defaults': { "anchor": "-20px" },
'paramsAsHash': True,
}, indent=4 ),
'apiconf': ('{'
'load: ' + ("XD_%s.get" % clsname) + ","
'submit:' + ("XD_%s.update" % clsname) + ","
"}"),
}
return HttpResponse( mark_safe( clscode ), mimetype="text/javascript" )
def get_form_data( self, formname, request, pk ):
formcls = self.forms[formname]
instance = formcls.Meta.model.objects.get( pk=pk )
forminst = formcls( instance=instance )
data = {}
for fld in forminst.fields:
data[fld] = getattr( instance, fld )
return { 'data': data, 'success': True }
def update_form_data( self, formname, request, *args ):
print args
return True
@property
def urls(self):
""" Return the URL patterns. """
return patterns('',
pat = patterns('',
(r'api.js$', self.get_api ),
(r'router/?', self.request ),
)
if self.forms:
pat.append( url( r'(?P<formname>\w+).js$', self.get_form ) )
return pat
Loading…
Cancel
Save