博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IronPython 个人网站样例----宝藏挖掘
阅读量:5746 次
发布时间:2019-06-18

本文共 8491 字,大约阅读时间需要 28 分钟。

IronPython for ASP.NET 的 CTP 已经发布两个多星期了,惭愧的是,因为工作繁忙,一直没有太多时间来学习。居然忽略了 Personal Web Site Starter Kit 的 IronPython 样例。幸亏了 Scott Guthrie 这篇博客:
,才让我发现了这个宝库。
今天下午花了点时间学习了一下,收获不少。记录在这里。
IronPython 版本的 Personal Web Site Starter Kit 是一个代码样例。我们用 Visual Studio 创建一个网站项目时,选择该模版就可以创建自己的网站项目了。下面我简单的说说该项目中一些值得学习的东西。
1. 数据访问:IronPython 版本的 SqlHelper
我们知道,IronPython for ASP.NET 提供了 App_Script 目录作为公用代码存放的地方,类似于 C# 或 VB.NET 版本的网站项目中的 App_Code 目录。在这个 Starter Kit 中,App_Script 目录下只包含了一个文件 PhotoManager.py,这是一个包含了所有公用代码的模块。其实从代码组织的方式来说,我觉得这么弄不太合适,因为像 SqlHelper 类,数据访问方法,实体类等内容全都定义在这里面了,没有做到一个类一个文件。不过因为 Python 的语法非常简单(比起 C# 而言),这个文件中的代码也不多,也没有给阅读带来太大的障碍。下面我们着重分析这个文件中一些可借鉴的地方。
先看看 py 版本的 SqlHelper 吧,在这里被命名成了 __sqlHelper,之所以这么命名是因为,这个类是被用作内部的帮助类的,并不会在客户端(该模块的使用者,即 importer)直接使用的,而 Python 中的模块有个特性,在使用 from moduleName import * 的语法的时候,以 "__" 开头的属性不会被导入,从而造成了被隐藏的效果。(当然这并不能阻止你通过  import moduleName 语法来使用它,或者也可以使用显式的导入语句 from moduleName import SomeAttr)。
其代码如下:
#
 Internal SQL Helper
#
#
 这里定义了一个 sqlHelper 帮助类,客户端并不直接调用它,
#
 而是通过更高层的函数来调用。
#
 注意:这个类只提供了对存储过程操作的支持。当然要扩展为普通 sql 的支持也是很容易的。
#
class
 
__sqlHelper
:
    
def
 
__init__
(self):
        self.connection 
=
 None
        self.command 
=
 None
        
    
def
 Open(self, commandname, sqlparams):
        self.connection 
=
 SqlConnection(ConfigurationManager.ConnectionStrings[
'
Personal
'
].ConnectionString)
        self.command 
=
 SqlCommand(commandname, self.connection)
        self.command.CommandType 
=
 CommandType.StoredProcedure
        
for
 k, v 
in
 sqlparams.items():
            
if
 
not
 k.startswith(
'
@
'
): k 
=
 
'
@
'
 
+
 k
            
if
 k 
==
 
'
@IsPublic
'
 
and
 v 
is
 None:
                user 
=
 HttpContext.Current.User
                v 
=
 
not
 (user.IsInRole(
'
Friends
'
or
 user.IsInRole(
'
Administrators
'
))
            self.command.Parameters.Add(SqlParameter(k, v))
        self.connection.Open()
        
    
def
 Execute(self):
        self.command.ExecuteNonQuery()
    
def
 ReadBytes(self):
        result 
=
 self.command.ExecuteScalar()
        
if
 result 
is
 
not
 None 
and
 len(result) 
==
 0: result 
=
 None
        
return
 result
    
def
 ReadList(self, itemtype):
        list 
=
 []
        reader 
=
 self.command.ExecuteReader()
        
try
:
            
while
 reader.Read(): list.Add(itemtype(reader))
        
finally
:
            reader.Close()
        
return
 list
    
def
 Close(self):
        
if
 self.command 
is
 
not
 None: self.command.Dispose()
        
if
 self.connection 
is
 
not
 None: self.connection.Close()
这个类不多说。既然我说了这个类不是让客户端直接使用的,那么包装它的函数在哪里呢,请看:
#
#############################################################
#
 下面是利用 __sqlHelper 类创建的几个快捷的数据库操作函数
#
#
 执行 sp,不返回结果        
def
 SqlExecute(command, 
**
args):
    sql 
=
 
__sqlHelper
()
    
try
:
        sql.Open(command, args)
        sql.Execute()
    
finally
:
        sql.Close()
#
 执行 sp 并返回 scalar 信息
def
 SqlSelectBytes(command, 
**
args):
    result 
=
 None
    sql 
=
 
__sqlHelper
()
    
try
:
        sql.Open(command, args)
        result 
=
 sql.ReadBytes()
    
finally
:
        sql.Close()
    
return
 result
#
 执行 sp,并返回多个实体的列表,itemtype 是实体类型
def
 SqlSelectList(itemtype, command, 
**
args):
    result 
=
 None
    sql 
=
 
__sqlHelper
()
    
try
:
        sql.Open(command, args)
        result 
=
 sql.ReadList(itemtype)
    
finally
:
        sql.Close()
    
return
 result
下面我列举几个使用这些数据访问函数的例子,都非常简单:
#
 Album-Related Methods
#
 获得所有相册的列表
def
 GetAlbums():
    
return
 SqlSelectList(Album, 
'
GetAlbums
'
, IsPublic
=
None)
#
 添加相册
def
 AddAlbum(caption, ispublic):
    SqlExecute(
'
AddAlbum
'
, Caption
=
caption, IsPublic
=
ispublic)
#
 删除相册
def
 RemoveAlbum(albumid):
    SqlExecute(
'
RemoveAlbum
'
, AlbumID
=
albumid)
#
 更新相册信息
def
 EditAlbum(caption, ispublic, albumid):
    SqlExecute(
'
EditAlbum
'
, Caption
=
caption, IsPublic
=
ispublic, AlbumID
=
albumid)
#
 获取一个随机的相册编号
def
 GetRandomAlbumID():
    albums 
=
 GetAlbums()
    
if
 len(albums) 
==
 0: 
return
 
-
1
    
return
 albums[Random().Next(len(albums))].AlbumID
我们看到,利用 Python 可以传递字典参数的特性,这里的语法比 C# 简单太多了!一句话就搞定了一个数据访问方法。这里参数列表里面的参数会被上面讲过的 __sqlHelper 内部组合为 @ 开头的 SqlCommand 的参数名称。非常直接了当。而 SqlExecute 的第一个参数就是需要执行的存储过程的名称。
2. 实体类的简单定义
这个简单的不值一提,看代码:
#
 下面定义了两个实体类,它们都提供了从 DataReader 中获取信息来填充自身字段的构造函数。(但是其实不限于 DataReader, 应该说是任意的 dict 都可以。)
#
#
 相片实体类
class
 Photo:
    
def
 
__init__
(self, reader):
        self.PhotoID
=
 int(reader[
'
PhotoID
'
])
        self.AlbumID 
=
 int(reader[
'
AlbumID
'
])
        
#
self.Caption = str(reader['Caption'])
        self.Caption 
=
 reader[
'
Caption
'
].ToString()
#
 相册实体类
class
 Album:
    
def
 
__init__
(self, reader):
        self.AlbumID 
=
 int(reader[
'
AlbumID
'
])
        self.Count 
=
 int(reader[
'
NumberOfPhotos
'
])
        
#
 self.Caption = str(reader['Caption'])
        self.Caption 
=
 reader[
'
Caption
'
].ToString()
        self.IsPublic 
=
 bool(reader[
'
IsPublic
'
])
但是,请注意我上面黄色加亮的几行代码,被注释掉的是原先官方提供代码里面的,而下面一行是我修改过的代码。这里是我学习过程中发现的一个小小的 bug. 如果采用原来的代码,则会碰到一个问题。如果 reader 这个数据行里面该字段的信息是含有中文的,str 函数就不能正确转换。你会碰到如下的情况:
ipy_starterkit_bug.JPG
按照上面的方法修改之后,中文就可以正常的读出来了:
ipy_starterkit_good.JPG
3. 可被我们复用的缩略图函数
这是我们可以从这个 Starter Kit 里挖到的又一个宝藏:
#
 缩放图片
def
 ResizeImageFile(imageFile, targetSize):
    oldImage 
=
 Image.FromStream(MemoryStream(imageFile))
    
try
:
        newSize 
=
 CalculateDimensions(oldImage.Size, targetSize)
        newImage 
=
 Bitmap(newSize.Width, newSize.Height, PixelFormat.Format24bppRgb)
        
try
:
            canvas 
=
 Graphics.FromImage(newImage)
            
try
:
                canvas.SmoothingMode 
=
 SmoothingMode.AntiAlias
                canvas.InterpolationMode 
=
 InterpolationMode.HighQualityBicubic
                canvas.PixelOffsetMode 
=
 PixelOffsetMode.HighQuality
                canvas.DrawImage(oldImage, Rectangle(Point(0, 0), newSize))
                m 
=
 MemoryStream()
                newImage.Save(m, ImageFormat.Jpeg)
                result 
=
 m.GetBuffer()
            
finally
:
                canvas.Dispose()
        
finally
:
            newImage.Dispose()
    
finally
:
        oldImage.Dispose()
    
return
 result
#
 按缩放逻辑计算图片的新尺寸
def
 CalculateDimensions(oldSize, targetSize):
    newSize 
=
 Size()
    
if
 oldSize.Height 
>
 oldSize.Width:
        newSize.Width 
=
 int(float(oldSize.Width
*
targetSize)
/
oldSize.Height)
        newSize.Height 
=
 targetSize
    
else
:
        newSize.Width 
=
 targetSize
        newSize.Height 
=
 int(float(oldSize.Height
*
targetSize)
/
oldSize.Width)
    
return
 newSize
这个例子里面,所有的图片都是用二进制的方式保存在数据库里面的。其字段类型为 image. 所以我们看到上述函数操作中,返回的是表示图片内容的字节流信息。
4. 自定义 HttpHandler 来处理图片输出
Web_Handler.py 是负责输出图片二进制内容的一个 HttpHandler. 这个文件从当前 Request 对象的 QueryString 中分析信息,得到当前指定的图片 id, 然后去读取数据库中的图片二进制流并输出之。其中还使用了缓存策略以提高性能。看代码:
from
 System.Web 
import
 
*
import
 PhotoManager
SizeMappings 
=
 {
'
S
'
:PhotoManager.SmallSize, 
'
M
'
:PhotoManager.MediumSize, 
'
L
'
:PhotoManager.LargeSize}
#
 这个 http handler 负责输出图片信息,其内部有缓存机制。
def
 ProcessRequest(context):
    request 
=
 context.Request
    response 
=
 context.Response
    
    size 
=
 SizeMappings.get(request.Size, PhotoManager.OriginalSize)
    
    
try
:
        
if
 request.PhotoID 
is
 
not
 None:
            id 
=
 int(request.PhotoID)
            bytes 
=
 PhotoManager.GetPhoto(id, size)
        
else
:
            id 
=
 int(request.AlbumID)
            bytes 
=
 PhotoManager.GetFirstPhoto(id, size)
    
except
:
        bytes 
=
 None
    
    
#
 如果读取失败或者找不到图片,则输出占位符图片    
    
if
 bytes 
==
 None:
        bytes 
=
 PhotoManager.GetPhotoPlaceHolder(size)
    response.ContentType 
=
 
"
image/jpeg
"
    
#
 注意这里使用了缓存策略
    response.Cache.SetCacheability(HttpCacheability.Public)
    response.OutputStream.Write(bytes, 0, len(bytes))
    
于是乎,有了这个 HttpHandler,我们要输出图片就很简单了,看一个例子:
<
img 
src
="../Web_Handler.py?PhotoID=<%# PhotoID %>&Size=L"
 class
="photo_198"
 style
="border:4px solid white"
 alt
='Photo 
Number <%# PhotoID %
>
' />
5. SiteMap 的节点自定义解析函数
SiteMap 默认只能利用静态的 Web.sitemap 文件中定义好的节点层次,并且每个节点的 url 也是固定的,通过处理 SiteMapResolve 事件,我们可以动态修改节点的 url. 这个实现定义在 Global.py 中,顺便可以看一下 IronPython 中的 Global 文件应该怎样写的:
from
 System 
import
 
*
from
 System.Web 
import
 
*
from
 System.Web.Security 
import
 
*
def
 Application_Start():
    SiteMap.SiteMapResolve 
+=
 AppendQueryString
    
if
 
not
 Roles.RoleExists(
'
Administrators
'
): Roles.CreateRole(
'
Administrators
'
)
    
if
 
not
 Roles.RoleExists(
'
Friends
'
): Roles.CreateRole(
'
Friends
'
)
def
 AppendQueryString(sender, e):
    node 
=
 SiteMap.CurrentNode
    
if
 node 
is
 
not
 None:
        node 
=
 node.Clone(True)
        qs 
=
 e.Context.Request.QUERY_STRING
        
if
 len(qs) 
>
 0:
            node.Url 
+=
 
'
?
'
 
+
 qs
            
if
 node.ParentNode 
is
 
not
 None:
                node.ParentNode.Url 
+=
 
'
?
'
 
+
 qs
    
return
 node
6. 享受 IronPython 的便利,简化页面后台代码
IronPython for ASP.NET 的引擎实现了很多便利的特性,这个在 中已经提及。比如不用写 FindControl 了,获取 QueryString 变得方便了,等等。下面的代码是一个 GridView 的 CRUD 事件处理的后台代码:
def
 GridView1_RowDeleting(sender, e):
    id 
=
 GridView1.DataKeys[e.RowIndex].Value
    PhotoManager.RemovePhoto(id)
def
 GridView1_RowEditing(sender, e):
    GridView1.EditIndex 
=
 e.NewEditIndex
def
 GridView1_RowCancelingEdit():
    GridView1.EditIndex 
=
 
-
1
def
 GridView1_RowUpdating(sender, e):
    caption 
=
 GridView1.Rows[e.RowIndex].CaptionTextBox.Text
    id 
=
 GridView1.DataKeys[e.RowIndex].Value
    
    PhotoManager.EditPhoto(caption, id)
    
    GridView1.EditIndex 
=
 
-
1
多么简单!
从这个例子中,我们还可以学到其他一些 ASPX 页面级别的技巧。
比如新增信息的功能,通过 FormView 控件的 InsertItemTemplate 来实现就非常方便。另外,则页面中做数据绑定时,可以通过这样的语法 <%# Container.DataItemIndex %> 来取得记录 ID. 我们平时用的最多的可能是 Container.DataItem 了,但 DataItemIndex 这个属性估计用的人是不多的。
另一个技巧,要在页面回发后维持当前页面的滚动高度,只要在 Page_Load 里面这样写:
MaintainScrollPositionOnPostBack 
=
 True
在 IronPython for ASP.NET 带给我们兴奋的同时,我们也要看到,目前由于缺乏 Intellisense 的支持,我都不知道这么多代码要能正确编写出来需要花费多少调试的精力。希望很快能够下载到正式版本。

 

出处:

转载地址:http://onizx.baihongyu.com/

你可能感兴趣的文章
解决灾难恢复后域共享目录SYSVOL与NELOGON共享丢失
查看>>
eclipse集成weblogic开发环境的搭建
查看>>
写一个bat文件,删除文件名符合特定规则,且更改日期在某
查看>>
我的友情链接
查看>>
写Use Case的一种方式,从oracle的tutorial抄来的
查看>>
【C#】protected 变量类型
查看>>
Ubuntu解压
查看>>
爬虫_房多多(设置随机数反爬)
查看>>
藏地密码
查看>>
爬虫去重(只是讲了去重的策略,没有具体讲实现过程,反正就是云里雾里)...
查看>>
react中将px转化为rem或者vw
查看>>
8816
查看>>
avcodec_open2()分析
查看>>
何如获取单选框中某一个选中的值
查看>>
paip.输入法编程----删除双字词简拼
查看>>
Django的Form(二)
查看>>
BZOJ1237 [SCOI2008]配对
查看>>
直接插入排序
查看>>
MySQL 备份错误日志
查看>>
tcp状态
查看>>