CherryPy 应用程序的最佳结构实践有哪些?

5 投票
2 回答
3930 浏览
提问于 2025-04-15 21:45

我正在写一个cherrypy应用程序,想知道在构建较大应用时,处理程序和代码的最佳结构是什么样的?

我知道可以通过cherrypy.root简单地进行分配,但在编写处理程序和分配它们时,有哪些好的做法呢?

(让我来证明我有多困惑!) 我最初的想法是写一个标准的处理程序类,根据当前的URL或类/方法组合来推断要运行的模板。然后,我会将这个处理程序的一个实例多次分配给不同的路径,以创建页面。不过,我觉得这样做可能不太行,因为递归引用可能会出问题。

所以,考虑到我对自己代码的结构已经感到迷茫,我非常希望能得到一些建议和示例!

如果需要我进一步解释,随时可以问我详细的问题。虽然网上有很多cherrypy的教程材料,但大多数只是表面上的介绍。

2 个回答

10

CherryPy这个框架特别设计成不需要你从它提供的基础类去继承,这样你就可以自由地设计自己的继承方式,或者更重要的是,根本不使用继承。你当然可以定义自己的基础类并从中继承;这样一来,你就可以通过你类的__init__方法,以及类级别的变量和方法来统一处理器的构建和配置。

不过,推荐的做法其实是不同的。对于大多数网页应用来说,你并不想改变处理器的实际构建逻辑,也不太关心类级别的变量或方法;相反,你更希望每个URI(统一资源标识符)或者URI的子树,甚至是整个网站都有可重用的变量和方法,而不是每个类都有。你通常是通过实例配置(处理器的元数据)和实例方法(处理器的逻辑)来区分不同的处理器。传统的基于类的继承虽然可以做到这一点,但对于这种定制化来说,效果不是很好。

因此,CherryPy被设计成能够提供这种基于资源集的定制化,而传统的类继承做得并不好。它通过两种方式来实现这一点:1)配置系统的设计,允许你将元数据绑定到单个URI、URI的子树、处理器的子树,或者整个网站,使用相同的语法(可以查看http://docs.cherrypy.org/dev/intro/concepts/config.html了解概况);2)钩子和工具系统,允许你将逻辑绑定到单个URI、URI的子树、处理器的子树,或者整个网站。可以查看http://docs.cherrypy.org/dev/intro/concepts/tools.html

所以,实际上:你可以在cherrypy.root上使用普通属性来构建你的处理器树:

def make_app():
    root = Root()
    root.foo = Foo()
    root.bars = BarCollection()
    return root

但是,不要让Root、Foo和Bar继承自一个共同的基础类。相反,写一些独立的工具来完成像“推断模板”这样的事情。也就是说,不要这样:

from cherrypy import expose

class Foo(MyAppBase):
    @expose()
    def index(self, a, b, c):
        ...

root.foo = Foo(template='foo.html')

而是这样写:

from cherrypy import expose, tools

class Foo(object):
    @tools.render(template='foo.html')
    @expose()
    def index(self, a, b, c):
        ...

root.foo = Foo()

...其中'tools.render'是你写的一个CherryPy工具,用来查找和应用给定的模板。这种方法可以让你在配置文件中覆盖工具的参数,而不需要重新打包或修改你的代码:

[/foo/]
tools.render.template = 'foo2.html'
5

这个问题比较主观,但我还是试着回答一下。

  • 首先,数据库和数据代码要和网页代码分开。我通常会在一个叫 DB/ 的文件夹里放很多小文件,每个文件里有一个类,然后把它们合并成一个 Base.py 文件,比如:

    Web/
        Base.py - The main "base" class, which includes the classes in other 
                  web files, starts the web server in __init__
        Users.py - The class which includes methods generally from "DB/Users.py" 
                   which checks permissions etc before returning (you may 
                   wish to add DB-level security later though)
        ...
    DB/
        Base.py - The main base DB class, includes the other DB classes. Creates 
                  new SQLAlchemy/whatever instances and create database schemas if 
                  they don't etc. May pay to have database-wide methods
                  here to keep creating connections etc in one place if you 
                  decide to change databases later 
        Users.py - The user/password etc DB storage class file
        ...
    Templates/
        (HTML templates go here)
    Static/
        (Static images/CSS/javscript etc go here)
    
    别忘了在每个模块目录里加上 __init__.py,这样 Python 才能找到子目录里的模块。

  • 我认为,代码结构的方法不一定要固定,但要保持一致。我会写一份文档,列出我的命名规则和使用这些规则的理由,并尽量遵循这些规则,直到它们变得不合理为止。不过,正如那句名言所说,a foolish consistency is the hobgoblin of small minds,这是引用自 Python 风格文档 :-)

  • 尽量使用类,而不是直接用函数。对于小项目可能没什么关系,但对于稍微复杂一点的项目,事情可能会变得棘手。我更喜欢把每个文件的目的明确,只在一个文件里放一个类,除非有多个类放在一起是合理的。

  • 这个观点可能有点争议——我通常会把我的类命名为 Class,然后通过模块名来引用它。我给你一个 Base.py 的例子:

    import Users
    class Base(Users.Class):
        def __init__(self):
            Users.Class.__init__(self)
    
    这样可以减少在导入时其他模块相互引用时出现的问题,因为 from Users import Users 如果 Users.py 里有 from Base import x 就会冲突,所以我总是通过模块名来引用。这只是个人偏好,所以你可以随意 :-P

希望你能从这篇帖子中得到一些启发。

撰写回答