跳转到主要内容

一个简单的Python egg和jQuery插件,可以轻松在Django/Plone/..网站上使用jQuery。

项目描述

jquery.pyproxy

jquery.pyproxy的目标是帮助Python站点整合Ajax调用与JQuery。主要思想是在Python调用中使用jQuery语法来更新用户看到的页面。这样,您就不必返回复杂的数据给需要进行解密的JavaScript客户端。

重要提示:该产品尚未最终完成,可能不适用于所有情况。

安装

将jquery.pyproxy添加到buildout eggs中并运行buildout,如果您不使用buildout,可以轻松安装jquery.pyproxy(或像通常使用其他产品那样操作)。

在Plone中,转到快速安装器并安装jquery.pyproxy。就这样,快乐的Plone用户现在可以跳转到“简单示例”。

在Django中,您还应该将django-appmedia添加到buildout eggs中,并将appmedia添加到已安装应用列表中。运行buildout,然后运行bin/django symlinkmedia。现在,在您的模板中添加以下内容:

<script type="text/javascript" src="{{MEDIA_URL}}/pyproxy/jquery.pyproxy.min.js"></script>

如果您希望在请求完成后自动显示旋转器,您也可以在您的页眉中添加以下内容:

<script type="text/javascript" src="{{MEDIA_URL}}/pyproxy/jquery.pyproxy.spinner.min.js"></script>
<link rel="stylesheet" href="{{MEDIA_URL}}/pyproxy/jquery.pyproxy.spinner.css" type="text/css" media="screen">

您需要在网站的根目录下有一个pyproxy_spinner.gif图像可用。如果没有(或者如果您的媒体文件托管在不同的子域上),您应该声明一个名为pyproxy_spinner_location的JavaScript变量,该变量指向文件的URL。声明应该在包含jquery.pyproxy.spinner.js文件之前完成。

如果您不想使用app_media,您可以从GitHub(https://github.com/vincent-psarga/jquery.pyproxy/blob/master/jquery/pyproxy/media/jquery.pyproxy.min.js - 请确保匹配egg版本和js版本)下载javascript库,并将其安装到您的媒体文件夹中。然后您可以将它包含到您的模板中。

简单示例

一个简单的示例应该更容易解释jquery.pyproxy背后的理念。您不希望用户在提交博客评论时重新加载整个页面,因此您将使用Ajax调用。以下是使用jquery.pyproxy执行此操作的方法。

首先,您创建一个视图来管理用户发送的信息(这里使用django,但原理与Plone相同)

from jquery.pyproxy.jq_django import JQueryProxy, jquery

# The @jquery decorator handles the transformation of your results
# into JSON so we can decode it on client side.

@jquery
def ajax_add_comment(request):
    # The JQuery proxy object helps us to manipulate the page the user sees.
    jq = JQueryProxy()
    # The data/form sent with Ajax just apear like a classical POST form.
    form = request.POST

    #we do some validation of the form.
    ...

    if errors:
        # We display an error message.
        jq('#my_error_message').show()
        return jq
    ...
    # We display a success message.
    jq('#my_success_message').show()

    # If the JQueryProxy object is not returned, nothing happens.
    return jq

最后,将jquery.pyproxy动作绑定到用于提交表单的按钮上

$(document).ready(function() {
  $('#submit_comment').pyproxy('click',
                               '/ajax_add_comment',
                               '#comment_form');
});

使用JQueryPyproxy对象

首先,您需要导入正确的JQueryProxy对象

  • 如果您使用Plone:from jquery.pyproxy.plone import JQueryProxy

  • 如果您使用Django:from jquery.pyproxy.jq_django import JQueryProxy

两者都基于base.py模块中定义的同一对象

>>> from jquery.pyproxy.base import JQueryProxy

该项目背后的主要思想是能够使用与jQuery相同的语法,因此您可以在一定程度上复制粘贴一些网络上的示例或现有代码。当然,因为我们使用Python,我们不能使用美元符号作为标识符

>>> $ = JQueryPyproxy()
Traceback (most recent call last):
...
SyntaxError: invalid syntax

因此,在我们的示例中,我们声明我们的对象为jq,这是Plone中命名jQuery对象的经典方式

>>> jq = JQueryProxy()

目前,JQueryProxy支持JQuery 1.4的所有操作API和所有效果API。

>>> sorted(jq.grammar.keys())
['addClass', 'after', 'animate', 'append', 'appendTo',
 'attr', 'before', 'css', 'detach', 'empty', 'fadeIn',
 'fadeOut', 'fadeTo', 'find', 'hide', 'html', 'insertAfter',
 'insertBefore', 'parent', 'prepend', 'prependTo', 'remove',
 'removeAttr', 'removeClass', 'replaceAll', 'replaceWith',
 'show', 'slideDown', 'slideToggle', 'slideUp', 'text',
 'toggle', 'toggleClass', 'unwrap', 'wrap', 'wrapAll', 'wrapInner']

因此,如果您知道如何在jQuery中使用它们,您就知道如何在pyproxy中使用它们,例如

>>> jq('#error_msg').html('Errors have been found, please correct them')
>>> jq('#error_email').show()

jQuery方法的声明方式与jQuery API相匹配(除了回调,请参阅“限制”部分)。因此,如果您使用错误的参数,您将在Python代码中获得错误(这应该会极大地帮助您进行调试,至少您应该有服务器日志)

>>> jq('.to_slide').slideDown()
Traceback (most recent call last):
...
TypeError: Method 'slideDown' takes exactly 1 arguments

>>> jq('.empty').empty('Spanish argument is like Spanish inquisition: no one expects it')
Traceback (most recent call last):
...
TypeError: Method 'empty' does not take any argument

>>> jq('.to_fade').fadeTo('something', 'wrong type')
Traceback (most recent call last):
...
TypeError: Argument 2 of method fadeTo must be: <type 'int'>

如果您需要处理选择器列表,您可以使用JQueryObject的batch方法。它接受五个参数

  • 选择器列表

  • 要应用的方法

  • 此方法的应用参数列表

  • 在每个选择器之前添加的前缀(可选)

  • 选择器将应用到的替换列表(可选)

这在显示错误列表时可能很有用。

>>> my_errors = ['email', 'title', 'text']
>>> jq.batch(my_errors, 'addClass', ['error'], substitution='#%s_error')
>>> jq.list_calls()[-3:]
["jq('#email_error').addClass('error')",
 "jq('#title_error').addClass('error')",
 "jq('#text_error').addClass('error')"]

这相当于:

>>> for err in my_errors:
...     jq('#' + err + '_error').addClass('error')
>>> jq.list_calls()[-3:]
["jq('#email_error').addClass('error')",
 "jq('#title_error').addClass('error')",
 "jq('#text_error').addClass('error')"]

您也可以像在JavaScript端一样链式调用jQuery调用。

>>> jq('.nice_divs').css({'width': '200px'}).fadeIn(10)
>>> jq.list_calls()[-1]
"jq('.nice_divs').css({'width': '200px'}).fadeIn(10)"

请注意,我们还没有检查(尚未)调用返回的可以链式调用的内容。

当您需要清楚地了解jq对象执行了哪些调用时,可以使用list_calls方法。

>>> jq.list_calls()
["jq('#error_msg').html('Errors have been found, please correct them')",
 "jq('#error_email').show()",
 "jq('.to_slide').slideDown()",
 "jq('.empty').empty()",
 "jq('.to_fade').fadeTo()",
 "jq('#email_error').addClass('error')",
 "jq('#title_error').addClass('error')",
 "jq('#text_error').addClass('error')",
 "jq('#email_error').addClass('error')",
 "jq('#title_error').addClass('error')",
 "jq('#text_error').addClass('error')",
 "jq('.nice_divs').css({'width': '200px'}).fadeIn(10)"]

您可以看到,即使失败的调用也存储在这里(但不是参数)。这是因为我们处于doctests中。在现实生活中的使用案例中,当异常被引发时,您的代码将在问题找到后停止。

扩展JQuery代理

如果您想使用默认情况下未知的jQuery方法,您必须扩展JQueryPyproxy已知的方法列表。在我们的示例中,我们假设您想使用来自额外插件的showDialog方法。默认情况下,它不起作用(这是逻辑的,因为pyproxy不知道您正在使用哪些jQuery插件)。

>>> jq('.bla').showDialog()
Traceback (most recent call last):
...
AttributeError: 'JQueryCommand' object has no attribute 'showDialog'

要使用此方法,您需要定义要使用的方法列表和预期参数。在这里,我们只定义了showDialog方法,该方法接受三个参数(第一个参数是字符串或Unicode,第二个参数是整数、字符串或Unicode,最后一个参数是可选字典)。

>>> from types import NoneType
>>> my_plugin = {'showDialog': [[str, unicode],
...                             [str, unicode, int],
...                             [dict, NoneType]]}

然后,您可以使用extend_grammar方法使您的方法定义被识别。

>>> jq.extend_grammar(my_plugin)
>>> 'showDialog' in jq.grammar
True
>>> jq.grammar['showDialog']
[[<type 'str'>, <type 'unicode'>],
 [<type 'str'>, <type 'unicode'>, <type 'int'>],
 [<type 'dict'>, <type 'NoneType'>]]
>>> jq('#my_dialog').showDialog('some text', 42, dict(opt1 = 2, opt2 = False))

当然,它遵守您定义的语法。

>>> jq('.bla').showDialog()
Traceback (most recent call last):
...
TypeError: Method 'showDialog' takes between 2 and 3 arguments

>>> jq('.bla').showDialog(1, 2, 3)
Traceback (most recent call last):
...
TypeError: Argument 1 of method showDialog must have one of the following types: [<type 'str'>, <type 'unicode'>]

>>> jq('.grep').showDialog('blabla', 'fich')

如果您需要在所有Ajax视图中使用自定义方法,每次扩展语法都会很痛苦。您有一些选项可以解决这个问题。

1) 如果您使用jquery.pyproxy的源代码:在jquery.pyproxy/jquery/pyproxy/plugins中添加一个名为my_plugin.py的文件。在这个文件中,使用前面解释的字典描述您的插件。此字典必须称为grammar

2) 如果您不使用源代码,只使用egg。创建一个新的Python类,继承自JQueryProxy。在这个对象中声明一个base_grammar属性,该属性描述了您的语法。

>>> class MyJQueryProxy(JQueryProxy):
...     base_grammar = {'showDialog': [[str, unicode],
...                                    [str, unicode, int],
...                                    [dict, NoneType]]}
>>> my_jq = MyJQueryProxy()
>>> my_jq('.bla').showDialog('a', 2)

并在您的视图中使用此类而不是JQueryProxy类。

3) 在下一个版本中集成您的语法。在任何情况下,都不必犹豫提交您定义的语法,以便它可以集成到下一个版本的jquery.pyproxy中。

限制

目前有两个(至少)主要限制。

首先,您不能像这样存储您的选择器:

>>> jq = JQueryProxy()
>>> divs = jq('.nice_divs')
>>> divs.css({'width': '200px'})
>>> divs.fadeIn(10)

它看起来工作得很好,但如果我们仔细看看jq对象存储的内容,我们可以看到只保存了最后一个调用,并且对‘css({width’: ‘200px’})’的调用永远不会被执行。

>>> jq.list_calls()
["jq('.nice_divs').fadeIn(10)"]

编写此代码的正确方式是:

>>> jq = JQueryProxy()
>>> jq('.nice_divs').css({'width': '200px'})
>>> jq('.nice_divs').fadeIn(10)

现在,如果我们看看对象存储的内容,我们看到所有想要的调用。

>>> jq.list_calls()
["jq('.nice_divs').css({'width': '200px'})",
 "jq('.nice_divs').fadeIn(10)"]

第二点是回调没有被处理,所以您不能使用像这样的事情:

>>> jq('.animated').show(10, 'my_callback')
Traceback (most recent call last):
...
TypeError: Method 'show' takes between 0 and 1 arguments

即使jQuery文档说show方法可以接受多个参数(这是真的,但不是在这里)。

有没有一些示例可用?

如果您使用Plone,请将以下内容添加到可用的configure.zcml文件中(例如您的主题):

<include package="jquery.pyproxy.samples.plone" />

重启实例后,然后打开http://localhost:8080/your_plone_site/pyproxy_samples

如果您使用Django,一些示例将在以后添加。

测试模块

此包中包含了一些测试以确保它正确工作。要在Python端运行测试,可以运行:

bin/instance test -m jquery.pyproxy (for Plone users)
bin/django test pyproxy (for Django users with a buildout)
./manage.py test pyproxy (for Django users without buildout)

同时也有qUnit测试来确保jQuery库能正确工作。目前它仅适用于Plone用户。首先,您需要从jquery.pyproxy加载“tests.zcml”文件。例如,在您开发的产品的主configure.zcml中

<include package="jquery.pyproxy"
         file="tests.zcml" />

然后,在ZMI中,转到portal_setup,然后是“导入”选项卡。从列表中选择“jquery.pyproxy tests”,选择“Skins tools”步骤,然后点击“导入所选步骤”。在“portal_skins”工具中,您应该看到一个名为“pyproxy_tests”的新文件夹。现在打开“http://localhost:8080/your_plone_site/pyproxy_tests”,您将看到qUnit测试正在运行。

我使用Python,但不是Django或Plone

您应该使用Django。

如果此解决方案不可接受,您仍然可以更新@jquery装饰器以与您的框架兼容。这个装饰器所做的唯一事情是将函数返回的JQueryProxy对象转换为JSON。为了进行转换,这段代码就足够了

>>> import simplejson as json
>>> jq_to_json = json.dumps(jq.json_serializable())
>>> jq_to_json
'[{"args": [{"width": "200px"}], "call": "css", "selector": ".nice_divs"}, {"args": [10], "call": "fadeIn", "selector": ".nice_divs"}, {"args": [], "call": "show", "selector": ".animated"}]'

然后,根据您的框架系统,必须返回jq_to_json对象(例如,对于Plone,我们只是返回它;对于Django,我们将其包装在HttpResponse对象中)。

如果您已将@jquery装饰器移植到任何框架,请让我知道,以便它可以集成到下一个版本中。

兼容性

已测试

  • jQuery 1.2和1.4

  • Python 2.4和2.6

  • Firefox

  • Chrome

  • Safari

  • IE

路线图

下一个版本应该包含的内容。

0.5

  • 针对Plone特定性的测试。[完成]

  • 针对Django特定性的测试(应该只有@jquery装饰器)。[完成]

0.6

  • 重构jQuery插件,使其只接受一个参数,该参数将是一个包含选项的字典。

  • 包含一些从URL中提取数据的工具(与原生的“params”方法相反)。

  • 允许针对特定调用启用调试模式,而不是全局设置。

  • 在请求开始/结束时添加自定义事件。

  • 使用事件来显示加载指示器。

1.0

  • 允许存储选择器并在其上执行多个调用。

稍后

  • 允许回调。

变更日志

0.5 (2013-09-24)

  • 添加空升级步骤以正确设置我们的配置文件版本。[maurits]

  • 将导入调整为在Plone 3.3至4.3中工作。[maurits]

0.4.3 (2013-06-04)

0.4.2 (2013-02-25)

  • 修复了Diazo将响应包裹在HTML标签中的问题。[vincent]

0.4.1 (2011-09-19)

  • 更新了最小化版本。[vincent]

0.4 (2011-09-19)

  • 添加了find()和parent()支持。[vincent]

  • 添加了对链式调用的支持。[vincent]

  • 添加了对“this”的支持。[vincent]

0.3.1 (2011-05-09)

  • 移除了有时与其他产品冲突的“debug”函数。[vincent]

0.3 (2011-02-25)

  • 现在您可以在不指定表单的情况下指定回调。[vincent]

  • 添加了jQuery插件的测试。[vincent]

  • 在process_call中移除了“eval()”调用。还移除了不再需要的arg_to_string并禁用了“clean_string”,因为它也不再需要了。[vincent]

  • 从jQuery插件中提取了子方法以简化测试。所有这些方法都以pyproxy为前缀,以避免名称冲突。[vincent]

  • 添加了路线图。[vincent]

  • 添加了额外的JS/CSS文件,以在请求完成后显示加载指示器。加载指示器图像是用ajaxload.info/生成的。[vincent]

  • 最终添加了基本Python代码的测试。jQuery插件和定制的Plone.Django对象的测试将随后添加。[vincent+maurits]

  • 重要:已将“grammar”属性重命名为“base_grammar”属性。如果您创建了JQueryProxy对象的子类,请更新它们。使用“grammar”属性导致了问题,因为它没有在__init__方法中初始化,其值被传播到JQueryProxy对象的每个实例。[vincent]

  • 在JQueryProxy对象上添加了“batch”方法来处理多个条目。它应该用于替换show_errors和hide_errors。[vincent]

  • 弃用jq.show_errors和jq.hide_errors,因为它们基于Plone CSS类。[vincent]

  • 将‘clean_string’,‘package_contents’和‘custom_endswith’移动到utils。[vincent]

  • 在Plone版本中修复了小bug,以隐藏之前的门户消息。[vincent]

  • 更新__init__.py以声明命名空间,以避免与setuptools 0.7的问题。[vincent]

0.2 (2010-10-22)

  • 将jq_django.py中的django.py重命名为django.py,因为它在导入HttpResponse时产生冲突。[vincent]

  • 将jquery.pyproxy.js移动到名为“media”的文件夹中,以便django用户可以通过django-appmedia轻松使用。还为plone用户添加了一些注册,以便jquery.pyproxy出现在quick_installer中,并自动将JS文件添加到jsregistry。[vincent]

  • bug修复:在plugins/jq_effects中,fadeOut和fadeIn没有正确的名称。[vincent]

  • 在make_call中添加了‘PreventDefault’ - 使用‘return false’对链接有效,但对提交按钮不够。[vincent]

0.1 (2010-04-19)

  • 在base中修复了bug:package_contents在python 2.4中不起作用。[vincent]

  • 将jquerypyproxy重命名为jquery.pyproxy。[vincent]

  • 在base中修复了bug:语法未传递给创建的对象。[vincent]

  • 在base中修复了bug:str.endswith(tuple)仅在python 2.5+中工作,添加了一个自定义endswith。[vincent]

  • 在plone.py中修复了bug:类名中有错别字。[vincent]

  • 在语法中添加了append。[vincent]

  • 添加了基本的javascript调用。[vincent]

  • 添加了JQueryProxy Python类和plone和Django的装饰器。[vincent]

  • 添加了日志文件+python egg。[vincent]

项目详情


下载文件

下载适合您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

jquery.pyproxy-0.5.zip (75.3 kB 查看散列)

上传时间

由以下机构支持