zoukankan      html  css  js  c++  java
  • odoo框架初识,简单小应用

    odoo12版本学习

    一·odoo简介

    ​ odoo是快速开发ERP系统的框架,适合商用. 内置crud,丰富的组件:看板,日历,图表.

    ​ odoo采用mvc架构模式. m即model,数据层, v及view,视图层(展示层),c即controller,逻辑层

    odoo结构:使用开发者模式快速入门 Odoo 12

    数据层:

    ​ 持久化层,负责存储.odoo借用PostgreSQL来实现. 不支持MySQL数据(可借用的第三方集成MySQL)

    ​ 文件附件,图片一类的二进制存储在filestore目录下

    逻辑层

    ​ 负责与数据层交互 . odoo核心代码种,提供了ORM引擎,ORM提供插件模块与数据交互的API

    展示层

    ​ 展示数据与用户交互, 自带web客户端.

    ​ 包含CMS框架,支持灵活创建网页

    二·odoo环境配置

    ​ 本实验采用ubuntu系统

    步骤一

    ​ 安装PostgreSQL数据库

    sudo apt update
    sudo apt install postgresql -y  # 安装PostgreSQL
    sudo su -c "createuser -s $USER" postgres # 创建数据库超级用户
    

    步骤二

    ​ 安装python3环境,以及其他依赖

    sudo apt update
    sudo apt upgrade
    
    # 安装Git
    sudo apt install git -y 
    # 安装python3
    sudo apt install python3-dev python3-pip -y # Python 3 for dev
    # 安装依赖
    sudo apt install build-essential libxslt-dev libzip-dev libldap2-dev libsasl2-dev libssl-dev -y
    
    # 安装Node.js和包管理器
    sudo apt install npm 
    sudo ln -s /usr/bin/nodejs /usr/bin/node # 通过node运行Node.js
    sudo npm install -g less less-plugin-clean-css # 安装less
    

    步骤三

    ​ 安装odoo源码

    1.在 Home 目录创建工作目录
    	# 创建工作目录
    	mkdir ~/odoo-dev 
    	# 进入工作目录
    	cd ~/odoo-dev 
    	
    2.克隆odoo12版本源码
    	git clone https://github.com/odoo/odoo.git -b 12.0 --depth=1 # 获取 Odoo 源码
    	
    3.安装odoo所需的依赖
    	pip3 install -r ~/odoo-dev/odoo/requirements.txt
    	
    4.安装odoo其他依赖包
    	pip3 install num2words phonenumbers psycopg2-binary watchdog xlwt
    

    步骤四

    ​ 启动odoo服务

    ~/odoo-dev/odoo/odoo-bin
    
    # 默认监听端口8069 ,访问 http://localhost:8069
    

    步骤五

    ​ 管理odoo数据库

    # 由于采用的是psql数据库,需要手动创建
    
    1.创建PostgreSQL数据库
    	createdb MyDB
    2.创建odoo数据库
    	 createdb --template=MyDB MyDB2
    3.删除数据库 # dropdb是不可撤销的,永久性删除
    	dropdb MyDB2  
    
    # 访问odoo客户端 页面会出现以下内容
    Database Name:数据库的标识名称,在同一台服务器上可以有多个数据库
    
    Email:管理员的登录用户名,可以不是 email 地址
    
    Password:管理员登录的密码
    
    Language:数据库的默认语言
    
    Country:数据库中公司数据所使用的国家,这个是可选项,与发票和财务等带有本地化特征的应用中会用到
    
    Demo data:勾选此选项会在数据库中创建演示数据,通常在开发和测试环境中可以勾选
    
    
    ## 服务端添加了master password , 要求输入密码,目的是阻止未经授权的管理员操作
    

    odoo其他配置

    ### 修改监听的端口 , 即可运行多个odoo实例	
    	 ~/odoo-dev/odoo/odoo-bin --http-port=8070
    	 ~/odoo-dev/odoo/odoo-bin --http-port=8071 
    
    
    ### 数据库选项
    	Odoo 开发时,经常会使用多个数据库,有时还会用到不同版本。在同一端口上停止、启动不同服务实例,或在不同数据库间切换,会导致网页客户端会话异常。因为浏览器会存储会话的 Cookie。
    	# 它接收一个正则表达式来过滤可用数据库名,要精确匹配一个名称,表达式需要以^开头并以$结束
     	~/odoo-dev/odoo/odoo-bin --db-filter=^testdb$
        
    ### 管理服务器日志消息
    	 –log-level=debug参数
         参数如下:
            debug_sql:查看服务中产生的 SQL 查询
            debug_rpc:服务器接收到的请求详情
            debug_rpc_answer:服务器发送的响应详情
    

    安装第三方插件

    # Odoo应用商店可以下载一系列模块安装到系统中, 为 Odoo 添加模块,仅需将其拷贝到官方插件的 addons 文件夹中即可
    
    
    例子:
    	# 1.拷贝library模块
    	cd ~/odoo-dev
    	git clone https://github.com/alanhou/odoo12-development.git library
    	
    
    	# 2.配置插件(add-ons)路径
    	"""
    	Odoo 服务有一个addons_path参数可设置查找插件的路径,默认指向Odoo 服务所运行处的/addons文件夹。我们可以指定多个插件目录,这样就可以把自定义模块放到另一个目录下,无需与官方插件混到一起。
    	"""
    	
    	cd ~/odoo-dev/odoo
    	./odoo-bin -d 12-library --addons-path="../library,./addons"
    

    激活开发者模式

    # 方式一  在地址栏添加参数
    	未添加: http://127.0.0.1:8069/web#
    	添加: http://127.0.0.1:8069/web?debug=1#
    # 方式二 
    	找到设置(Settings),最底下有激活开发者模式选项
    
    

    三·odoo模块结构

    # 原博客地址:
    https://alanhou.org/odoo12-first-application/
    
    ####  在odoo开发中,一个应用就相当于是一个模块
    

    分析模块结构

    # odoo 的结构
    
    - modelname
    	- controllers 
    		- __init__.py
    		- main.py
        - i18n  #翻译文件
        	- zh-cn.po # po翻译文件
    	- data  # 数据文件
    		- book_demo.xml
    		- library.book.csv
    	- models  # 模型
    		- __init__.py
    		- library_book.py
    	- reports  # 报表
    		- __init__.py
    		- library_book_report.py
    		- library_book_report.xml
    	- security  # 安全 权限
    		- ir.model.access.csv
    		- library_security.xml
    	- static   # 静态资源
    		- src
    			- js
    			- css
    	- tests    # 测试
    		- __init__.py
    		- test_book.py
    	- views    # 展示视图
    		- book_list_template.xml
    	- __init__.py 
    	- __manifest__.py # 模块信息描述文件
    

    四·创建应用

    添加顶级菜单

    ​ 菜单项是使用 XML 文件中添加的视图组件,通过创建views/library_menu.xml来定义菜单项:

    <?xml version="1.0"?>
    <odoo>
        <!-- Library App Menu -->
        <menuitem id="menu_library" name="Library" />
    </odoo>
    

    ​ 需要在__manifest__.py中使用 data 属性来添加安装或更新时需要加载的模块列表

    'data': [
            'views/library_menu.xml',
    ],
    

    添加权限组

    ​ Odoo 中使用安全组来实现,权限授予组,组中分配用户。Odoo 应用通常有两个组:针对普通用户的用户组,包含额外应用配置权限的管理员组。

    ​ 权限安全相关的文件通常放在模块下/security子目录中,创建security/library_security.xml 文件来进行权限定义

    <?xml version="1.0" ?>
    <odoo>
        <record id="module_library_category" model="ir.module.category">
            <field name="name">Library</field>
        </record>
    </odoo>
    

    添加两个安全组,首先添加用户组

    <?xml version="1.0" ?>
    <odoo>
        <record id="module_library_category" model="ir.module.category">
            <field name="name">Library</field>
        </record>
    
        <!-- 加入安全组 Library User Group -->
        <record id="library_group_user" model="res.groups">
                <field name="name">User</field>
                <field name="category_id" ref="module_library_category" />
                <field name="implied_ids" eval="[(4, ref('base.group_user'))]" />
        </record>
    </odoo>
    
    ### 字段说明
        name:   组名
        
        category_id:关联应用,这是一个关联字段,因此使用了 ref 属性来通过 XML ID 连接已创建的分类
        
        implied_ids:这是一个one-to-many关联字段,包含一系列组来对组内用户生效。这里使用了一个特殊语法,
    

    创建管理员组,授予用户组的所有权限以及为应用管理员保留的其它权限

    <?xml version="1.0" ?>
    <odoo>
        <record id="module_library_category" model="ir.module.category">
            <field name="name">Library</field>
        </record>
    
        <!-- 添加用户组 Library User Group -->
        <record id="library_group_user" model="res.groups">
                <field name="name">User</field>
                <field name="category_id" ref="module_library_category" />
                <field name="implied_ids" eval="[(4, ref('base.group_user'))]" />
        </record>
        
        <!--创建管理员组 Library Manager Group -->
        <record id="library_group_manager" model="res.groups">
            <field name="name">Manager</field>
            <field name="category_id" ref="module_library_category" />
            <field name="implied_ids" eval="[(4, ref('library_group_user'))]" />
            <field name="users" eval="[
                        (4, ref('base.user_root')),
                        (4, ref('base.user_admin'))
                    ]" />
        </record>
    </odoo>
    

    ​ 同样需要在声明文件中添加该 XML 文件:

    'data': [
        'security/library_security.xml',
        'views/library_menu.xml',
    ],
    

    添加自动化测试

    # 1.测试应放在tests/子目录中,在tests/__init__.py
    	from . import test_book
    # 2.在tests/test_book.py文件中添加实际的测试代码:
    from odoo.tests.common import TransactionCase
    class TestBook(TransactionCase):
        def setUp(self, *args, **kwargs):
            result = super().setUp(*args, **kwargs)
            self.Book = self.env['library.book']
            self.book_ode = self.Book.create({
                'name': 'Odoo Development Essentials',
                'isbn': '879-1-78439-279-6'})
            return result
        def test_create(self):
            "Test Books are active by default"
            self.assertEqual(self.book_ode.active, True)
    

    模型层

    创建数据模型
    1. 在模块主__init__.py文件添加
        from . import models
    2. 在models/__init__.py文件种引入模型
    	from . import library_book
    
    # 创建模型 models/library_book.py
    
    from odoo import fields, models
    class Book(models.Model):
        _name = 'library.book'  # 在视图中能够引用到
        _description = 'Book'   # 模型描述
        
        name = fields.Char('Title', required=True)
        isbn = fields.Char('ISBN')
        active = fields.Boolean('Active?', default=True)
        date_published = fields.Date()
        image = fields.Binary('Cover')
        publisher_id = fields.Many2one('res.partner', string='Publisher')
        author_ids = fields.Many2many('res.partner', string='Authors')
    
    #### odoo数据库基本字段类型
    
    Binary:二进制类型,用于保存图片、视频、文件、附件等,在视图层显示为一个文件上传按钮。【Odoo底层对该类型字段的容量作了限制,最多能容纳20M内容】
    
    Char:字符型,size属性定义字符串长度。
    
    Boolean:布尔型
    
    Float:浮点型,如 rate = fields.float('Relative Change rate',digits=(12,6)), digits定义数字总长和小数部分的位数。
    
    Integer:整型
    
    Date:短日期,年月日,在view层以日历选择框显示。
    
    Datetime:时间戳。
    
    Text:文本型,多用于多行文本框,可以用widget属性为它添加样式。
    
    Html:与text类似,用于多行文本编辑,不过自带编辑器样式,并且会把内容以html解析。
    
    Selection:下拉列表,枚举类型。
    
    
    #### 关联字段类型
    one2one: 一对一关系。 在V5.0以后的版本中不建议使用,而是用many2one替代。
        格式:
            fields.one2one(关联对象Name, 字段显示名, ... )
        
    many2one: 多对一关系  
        格式:
            fields.many2one(关联对象Name, 字段显示名, ... )
        参数:
            comodel_name(string) -- 目标模型名称,除非是关联字段否则该参数必选
            domain -- 可选,用于在客户端筛选数据的domain表达式
            context -- 可选,用于在客户端处理时使用
            ondelete -- 当所引用的数据被删除时采取的操作,取值:'set null', 'restrict', 'cascade'
            auto_join -- 在搜索该字段时是否自动生成JOIN条件,默认False
            delegate -- 设置为True时可以通过当前model访问目标model的字段,与_inherits功能相同
            
            
    one2many: 一对多关系 
        格式:
            fields.one2many(关联对象Name, 关联字段, 字段显示名, ... )
        参数:
            comodel_name -- 目标模型名称,
            inverse_name -- 在comodel_name 中对应的Many2one字段
            domain -- 可选,用于在客户端筛选数据的domain表达式
            context -- 可选,用于在客户端处理时使用
            auto_join -- 在搜索该字段时是否自动生成JOIN条件,默认False
            limit(integer) -- 可选,在读取时限制数量
            
    many2many: 多对多关系
        格式: (生成第三表: ...._ref表)
      'category_id'=fields.many2many('res.partner.category','res_partner_category_rel','partner_id','category_id','Categories')
    
    # 表示以多对多关系关联到对象res.partner.category,关联表为'res_partner_category_rel',关联字段为 'partner_id'和'category_id'。
    # 当定义上述字段时,OpenERP会自动创建关联表为 'res_partner_category_rel',它含有关联字'partner_id'和'category_id'
    
        参数:
            comodel_name -- 目标模型名称,除非是关联字段否则该参数必选
            relation -- 关联的model在数据库存储的表名,默认采用comodel_name获取数据
            column1 -- 与relation表记录相关联的列名
            column2 --与relation表记录相关联的列名
            domain -- 用于在客户端筛选数据的domain表达式
            context -- 用于在客户端处理时使用
            limit(integer) --在读取时限制数量
    
            
    
    #### 引用类型
    related字段
    	格式:
            字段=fields.类型(related="某个字段.类字段",store=true/false)
        # related字段可以简记为“带出字段”,由当前模型的某个关联类型字段的某个字段带出值。
    
    reference字段
    	reference是比related更高级的引用字段,可以指定该字段引用那些模型范围内的模型的哪些字段的值,范围更广。
      
    
    #### Odoo保留字段
    name(Char) -- _rec_name的默认值,在需要用来展示的时候使用
    active(Boolean) -- 设置记录的全局可见性,当值为False时通过search和list是获取不到的
    sequence(Integer) -- 可修改的排序,可以在列表视图里通过拖拽进行排序
    state(Selection) -- 对象的生命周期阶段,通过fileds的states属性使用
    parent_id(Many2one) -- 用来对树形结构的记录排序,并激活domain表达式的child_of运算符
    parent_left,parent_right -- 与 _parent_store结合使用,提供更好的树形结构数据读取
    
    #### 自动化属性
    	id  Identifier field 是模型中每条记录的唯一数字标识符
        _log_access 是否创建日期字段,默认创建(default:True)
        create_date 记录创建日期 Type:Datetime
        create_uid 第一创建人 Type:res.users
        write_date 最后一次修改日期 Type:Datetime
        write_uid 最后一次修改人 Type:res.users
        _last_update 最后一次修改日期 它不存储在数据库,用于做并发检测
        display_name 对外显示的名称
    	
        # 不想在model自动添加这些属性 ,在类种添加:
        	_log_access=False
    
    #### Compute字段   
        ompute字段不是一种字段类型,而是指某个字段的值是计算出来的。
        一个字段的值,可以通过一个函数来动态计算出来。定义格式如下:
        
    字段名=fields.类型(compute="函数名",store=True/false) #store定义了该动态改变的字段值是否保存到数据库表中
    
    @api.depends(依赖的字段值)#depend的字段值一旦发生变化,就会触发该函数,从而更新compute字段值。
    def 函数(self):
        self.字段=计算字段值
    # 原博客https://www.cnblogs.com/ygj0930/p/10826099.html
    

    设置访问权限

    添加访问权限控制

    ​ 权限通过security/ir.model.access.csv文件来实现,添加该文件并加入如下内容:

    id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
    access_book_user,BookUser,model_library_book,library_group_user,1,0,0,0
    access_book_manager,BookManager,model_library_book,library_group_manager,1,1,1,1
    

    ​ 注解:

    # 1. 应注意该文件第一行后不要留有空格,否则会导致报错
    
    # 2. csv列解读
    	id         是记录的外部标识符(也称为XML ID),需在模块中唯一
        
    	name       是描述性标题,仅在保证唯一时提供有用信息
        
    	model_id   是赋权模型的外部标识符,模型有ORM自动生成的XML ID,对于library.book,标识符为model_library_book
        
    	group_id   指明授权的安全组,我们给前文创建的安全组授权:library_group_user和library_group_manager
        
    	perm_…     字段标记read读, write写, create创建, 或unlink删除权限,我们授予普通用户读权限、管理员所有权限
        
    # 3. __manifest__.py的 data 属性中添加对新文件的引用    
        'data': [
            'security/library_security.xml',
            'security/ir.model.access.csv',
            'views/library_menu.xml',
        ],
    
    行级权限规则

    ​ 添加记录规则,需编辑security/library_security.xml文件

    记录规则在ir.rule中定义,和往常一样我们选择一个唯一名称。还应获取操作的模型及使用权限限制的域过滤器。域过滤器使用 Odoo 中常用的元组列表,添加域表达式.
    
    <data noupdate="1">
            <record id="book_user_rule" model="ir.rule">
                <field name="name">Library Book User Access</field>
                <field name="model_id" ref="model_library_book" />
                <field name="domain_force">
                    [('active','=',True)]
                </field>
                <field name="groups" eval="[(4,ref('library_group_user'))]" />
            </record>
    </data>
    

    视图层

    视图类别:

    ​ tree视图,form表单视图,search搜索视图 等

    添加菜单项

    ​ 添加相应菜单项。编辑views/library_menu.xml文件,在 XML 元素中定义菜单项以及执行的操作:

        <!-- Action to open the Book list -->
        <act_window id="action_library_book"
            name="Library Books"
            res_model="library.book"
            view_mode="tree,form"
        />
        <!-- Menu item to open the Book list -->
        <menuitem id="menu_library_book"
            name="Books"
            parent="menu_library"
            action="action_library_book"
        />
    

    ​ 注释:

    1.<act_window> 元素定义客户端窗口操作,按找顺序通过启用列表和表单视图打开library.book模型
    2.<menuitem> 定义一个调用 action_library_book操作的定义菜单
    
    # act 是行为动作, menu是菜单.  页面上生成的菜单-->行为动作指向action
    
    创建表单视图

    ​ 添加views/book_view.xml文件来定义表单视图:

    <?xml version="1.0"?>
    <odoo>
        <record id="view_form_book" model="ir.ui.view">
            <field name="name">Book Form</field>
            <field name="model">library.book</field>
            <field name="arch" type="xml">
                <form string="Book">
                    <group>
                        <field name="name" />
                        <field name="author_ids" widget="many2many_tags" />
                        <field name="publisher_id" />
                        <field name="date_published" />
                        <field name="isbn" />
                        <field name="active" />
                        <field name="image" widget="image" />
                    </group>
                </form>
            </field>
        </record>
    </odoo>
    
    业务文件表单视图

    ​ header 和 sheet将form表单分成 两部分

    ​ header部分包含:可操作的button按钮, 状态栏

    ​ sheet部分包含: 数据列

    <form string="Book">
        <header>
            <!-- 此处添加按钮 -->
            <button name="button_check_isbn" type="object"
            string="Check ISBN" />
        </header>
        <sheet>
            <group>
                <field name="name" />
                ...
            </group>
        </sheet>
    </form>
    
    Odoo 12 开发之创建第一个 Odoo 应用
    组来组织表单

    <group> 标签,用来组织表单视图的布局

    ​ 推荐在group 元素中添加 name 属性,更易于其它模块对其进行继承

    <sheet>
        <!-- 分组展示	 -->
        <group name="group_top">
            <!--  左侧部分展会的字段 -->
            <group name="group_left">
                <field name="name" />
                <field name="author_ids" widget="many2many_tags" />
                <field name="publisher_id" />
                <field name="date_published" />
            </group>
            <!-- 右侧部分展示的字段 -->
            <group name="group_right">
                <field name="isbn" />
                <field name="active" />
                <field name="image" widget="image" />
            </group>
        </group>
    </sheet>
    

    ​ 注释

    # 1.  ir.ui.view 和record是固定搭配
    # 2. name 视图名称 , id 当前视图唯一的xml ID标识符 , model依赖的模型  arch form视图固定搭配
    # 3. 视图具有继承视图,即:在原有的视图上添加新的字段,新的展示内容
    
    # 4. __manifest__.py 的 data中声明
    	'views/book_view.xml',
    
    列表视图

    ​ 列表视图即 : <tree>视图 (初代结构)

    <record id="view_tree_book" model="ir.ui.view">
        <field name="name">Book List</field>
        <field name="model">library.book</field>
        <field name="arch" type="xml">
            <tree>
                <field name="name" />
                <field name="author_ids" widget="many2many_tags" />
                <field name="publisher_id" />
                <field name="date_published" />
            </tree>
        </field>
    </record>
    
    搜索视图

    <search> 搜索视图

    <record id="view_search_book" model="ir.ui.view">
        <field name="name">Book Filters</field>
        <field name="model">library.book</field>
        <field name="arch" type="xml">
            <search>
                <!-- 可搜索的字段值 -->
                <field name="publisher_id" />
                <!-- domain 过滤条件 -->
                <filter name="filter_active"
                        string="Active"
                        domain="[('active','=',True)]" />
                <filter name="filter_inactive"
                        string="Inactive"
                        domain="[('active','=',False)]" />
            </search>
        </field>
    </record>
    

    Odoo 12 开发之创建第一个 Odoo 应用

    业务逻辑层

    ​ 业务逻辑层编写应用的业务规则,如验证和自动计算。

    添加业务逻辑
    # 模型中有isbn字段, 上文在表单视图中添加了button按钮 name为:button_check_isbn . 现在为其添加上业务校验逻辑
    
    # 在models的 Book 模型中添加 校验逻辑
    
    	#1. 检验 isbn有效行
        def _check_isbn(self):
            self.ensure_one() 
            isbn = self.isbn.replace('-', '') # 为保持兼容性 Alan 自行添加
            digits = [int(x) for x in isbn if x.isdigit()]
            if len(digits) == 13:
                ponderations = [1, 3] * 6
                terms = [a * b for a,b in zip(digits[:12], ponderations)]
                remain = sum(terms) % 10
                check = 10 - remain if remain !=0 else 0
                return digits[-1] == check
            
            
         # 2. 按钮点击时触发此方法  button_check_isbn 方法与button的name必须同名
        from odoo import api, fields, models
    	from odoo.exceptions import Warning
        def button_check_isbn(self):
            for book in self:
                if not book.isbn:
                    raise Warning('Please provide an ISBN for %s' % book.name)
                if book.isbn and not book._check_isbn():
                    raise Warning('%s is an invalid ISBN' % book.isbn)
                return True
    
    总结:
    1.在处理业务逻辑是,添加异常后,odoo自动做事务回滚.
    2.业务逻辑一般放在models定义的模型类中 , 本身是由orm去操作的,需要成熟面向对象思想
    

    网页和控制器

    ​ 在页面上响应数据

    # 1. 在library_app/__init__.py导入控制器模块目录加入控制器
    	from . import models
        from . import controllers
    # 2.创建main.py,在library_app/controllers/__init__.py文件来让目录可被 Python 导入
    	from . import main
        
    # 3.定义代码
    from odoo import http
    class Books(http.Controller):
    
        @http.route('/library/books', auth='user')
        def list(self, **kwargs):
            Book = http.request.env['library.book']
            books = Book.search([])
            return http.request.render(
                'library_app.book_list_template', {'books':books})
    
        
    ### 注释: 
    		# 1. http.request.env获取环境,从目录中获取有效图书记录集
        	# 2. http.request.render() 来处理 library_app.index_template Qweb 模板并生成输出 HTML
            # 3. auth 制定访问的授权模式
    		# 4. {'books':books} book图书集合传递到Qweb中
    
    QWeb模板是视图类型

    ​ 放在/views子目录下,创建views/book_list_template.xml文件:

    <?xml version="1.0" encoding="utf-8"?>
    <odoo>
        <template id="book_list_template" name="Book List">
            <div id="wrap" class="container">
                <h1>Books</h1>
                <t t-foreach="books" t-as="book">
                    <div class="row">
                        <span t-field="book.name" />,
                        <span t-field="book.date_published" />,
                        <span t-field="book.publisher_id" />
                    </div>
                </t>
            </div>
        </template>
    </odoo>
    

    ​ 注释:

    ​ 1.<template>

    ​ 2. t-foreach用于遍历变量 books的每一项

    ​ 3. t-field用于渲染记录字段的内容

    基于博客:https://alanhou.org/odoo12-first-application/

  • 相关阅读:
    hdu 5902 Seam Carving
    hdu 5091 Beam Cannon
    hdu 1542 Atlantis
    hdu 2196 Computer
    第一个爬虫和测试
    排球比赛规则
    第十周博客作业
    科学计算可视化
    用matplotlib绘制图像
    面对对象学习
  • 原文地址:https://www.cnblogs.com/dengz/p/12888285.html
Copyright © 2011-2022 走看看