zoukankan      html  css  js  c++  java
  • IronPython 个人网站样例宝藏挖掘

    IronPython for ASP.NET 的 CTP 已经发布两个多星期了,惭愧的是,因为工作繁忙,一直没有太多时间来学习。居然忽略了 Personal Web Site Starter Kit 的 IronPython 样例。幸亏了 Scott Guthrie 这篇博客:
    http://blog.joycode.com/scottgu/archive/2006/11/18/86737.aspx,才让我发现了这个宝库。

    今天下午花了点时间学习了一下,收获不少。记录在这里。

    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 的支持,我都不知道这么多代码要能正确编写出来需要花费多少调试的精力。希望很快能够下载到正式版本。
  • 相关阅读:
    Appium环境搭建+cordova
    Appium简单测试用例
    appium常用方法整理
    JAVA+Maven+TestNG搭建接口测试框架及实例
    stm32填坑之旅一
    再度分(tu)析(cao)Egret这个年轻人
    Egret的若干局限
    egret随笔-egret浅入浅出
    一步一步实现AS3拖放组件
    react-native学习笔记——ViewStack组件
  • 原文地址:https://www.cnblogs.com/RChen/p/ipy_starterkit.html
Copyright © 2011-2022 走看看