澳门新莆京手机网站-新蒲京娱乐场 > 澳门新莆京手机网站 > 自个儿的首先个python web开拓框架(25)——定制ORM(生龙活虎)

自个儿的首先个python web开拓框架(25)——定制ORM(生龙活虎)

眼下的书房最初化的前端消息已经完美,所今后后始发贯彻DB的Script部分。

今儿晚上到位了Web端新添图书新闻的功效,今后就差DB的切切实实贯彻了。

主要:

  在开班编制ORM模块早先,大家供给先对db_helper进行重构,因为ORM最后生成的sql是内需转给db_helper来施行的,所以具备二个成效康健、强健的数据库操作类是老大供给的。

新增Action:Shelf_Init.sql

因为作者把Book相关的新闻拆分的超级多,所以更新有一些小麻烦。

  1. 常量优化路线
  2. 自动加载类
  3. 优化进口文件
  4. 安全访谈项目目录

  那是种类原db_helper.py代码

图片 1

第风度翩翩,笔者供给创造一个Book Type的Matter;


图片 2图片 3

svc.sql

然后,将图纸路线保存到FileBank中,并赶回FileBankID;

--------------文件结构:--------------------------------------blog
├─App
│ ├─Model 模型
│ │ └─UserModel.class.php 用户模型类 
│ ├─View 视图
│ │ ├─Back后台
│ │ │ └─Index
│ │ │ └─index.html 后台首页面
│ │ └─Home前台
│ │ └─User 用户视图目录
│ │ └─login.html 登录表单页面
│ ├─Controller 控制器
│ │ ├─Back后台
│ │ │ └─IndexController.class.php 后台首页控制器
│ │ └─Home前台
│ │ └─UserController.class.php 用户控制器
├─Public 静态公共文件(js,css,images)
│ ├─Plugins 插件
│ │ └─layui 前端框架插件
│ ├─Back后台
│ │ ├─js/ js文件
│ │ ├─css/ css样式文件
│ │ └─image img图片 
│ └─Home前台
│ ├─js/ js文件
│ ├─css/ css样式文件
│ └─image img图片 
├─Frame 公共使用的类
│ ├─BaseModel.class.php 数据库连接类
│ ├─BaseController.class.php 控制器公共操作(设置编码,信息跳转)
│ ├─FactoryModel.class.php 模型工厂类
│ ├─Init.class.php 初始化应用类
│ └─MySQLDB.class.php 数据库操作工具类
└─index.php 入口文件-----------------------------------------------------------------
#!/usr/bin/env python
# coding=utf-8

import psycopg2
from common import log_helper
from config import const

# 初始化数据库参数
db_name = const.DB_NAME
db_host = const.DB_HOST
db_port = const.DB_PORT
db_user = const.DB_USER
db_pass = const.DB_PASS


def read(sql):
    """
    连接pg数据库并进行数据查询
    如果连接失败,会把错误写入日志中,并返回false,如果sql执行失败,也会把错误写入日志中,并返回false
    如果所有执行正常,则返回查询到的数据,这个数据是经过转换的,转成字典格式,方便模板调用,其中字典的key是数据表里的字段名
    """
    try:
        # 连接数据库
        conn = psycopg2.connect(database=db_name, user=db_user, password=db_pass, host=db_host, port=db_port)
        # 获取游标
        cursor = conn.cursor()
    except Exception as e:
        print(e.args)
        log_helper.error('连接数据库失败:' + str(e.args))
        return False
    try:
        # 执行查询操作
        cursor.execute(sql)
        # 将返回的结果转换成字典格式
        data = [dict((cursor.description[i][0], value) for i, value in enumerate(row)) for row in cursor.fetchall()]
    except Exception as e:
        print(e.args)
        log_helper.error('sql执行失败:' + str(e.args) + ' sql:' + str(sql))
        return False
    finally:
        # 关闭游标和数据库链接
        cursor.close()
        conn.close()
    # 返回结果(字典格式)
    return data


def write(sql, vars):
    """
    连接pg数据库并进行写的操作
    如果连接失败,会把错误写入日志中,并返回false,如果sql执行失败,也会把错误写入日志中,并返回false,如果所有执行正常,则返回true
    """
    try:
        # 连接数据库
        conn = psycopg2.connect(database=db_name, user=db_user, password=db_pass, host=db_host, port=db_port)
        # 获取游标
        cursor = conn.cursor()
    except Exception as e:
        print(e.args)
        log_helper.error('连接数据库失败:' + str(e.args))
        return False
    try:
        # 执行sql语句
        cursor.execute(sql, vars)
        # 提交事务
        conn.commit()
    except Exception as e:
        print(e.args)
        # 如果出错,则事务回滚
        conn.rollback()
        log_helper.error('sql执行失败:' + str(e.args) + ' sql:' + str(sql))
        return False
    else:
        # 获取数据
        try:
            data = [dict((cursor.description[i][0], value) for i, value in enumerate(row))
                         for row in cursor.fetchall()]
        except Exception as e:
            # 没有设置returning或执行修改或删除语句时,记录不存在
            data = None
    finally:
        # 关闭游标和数据库链接
        cursor.close()
        conn.close()

    # 如果写入数据后,将数据库返回的数据返回给调用者
    return data
1 CREATE SCHEMA [svc]
2     AUTHORIZATION [dbo];

接轨,插入Publisher新闻(需求推断name空中楼阁才会insert),然后回到PublisherID;


View Code

Shelf_Init.sql

 1 CREATE PROCEDURE [base].[Publisher#Insert](@json nvarchar(max), @id int out)
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5 ...
 6 
 7         declare    @name nvarchar(100);
 8         select    @name=Publisher from openjson(@json, '$') with (Publisher nvarchar(100))
 9 
10         -- insert Publisher
11         insert    base._Publisher(Name)select @name
12         where    not exists(select 1 from base._Publisher p where p.Name=@name);
13 
14         select    @id=ID from base.Publisher#Raw() where Name=@name;
15 ...
16 END

上黄金年代篇中,建议4个难点待消除,本篇聚集国化学工业进出口总公司解那4个难点,最后变成全部的微型MVC框结构造, 后续博客项目,或别的品种,均能够平素利用该框布局造进行开采学习。

  通过对代码的精短深入分析,可以见到整个模块在初化时,载入数据库链接配置,对数据库的操作也只有大概读与写操作。这样的成效对于常常的数据库增加和删除改查操作已经够用了,但只要事情复杂,有三个库、要求使用事务大概必要拜见分歧门类数据库时,它就缺乏用了。所以率先要做的正是对它进行重构,功效拓宽周到。

 1 CREATE PROCEDURE [svc].[Shelf$Init](@json nvarchar(max))
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5     SET    NOCOUNT    ON;
 6     SET XACT_ABORT ON;
 7     BEGIN TRY
 8         BEGIN    TRAN;
 9 
10         declare    @nickName nvarchar(20), @shelfName nvarchar(20);
11         select    @nickName=NickName,     @shelfName=ShelfName
12         from    openjson (@json, '$')
13         with (
14             NickName        nvarchar(20),
15             ShelfName        nvarchar(20)
16         );
17 
18         insert    core._Party(Type, Alias) select k._User, @nickName
19         from    core.Party#Type() k;
20         declare    @userID int=@@identity;
21 
22         
23         insert    core._Party(PID, Type, Alias) select @userID, k._Shelf, @shelfName
24         from    core.Party#Type() k;
25 
26         COMMIT    TRAN;
27     END TRY
28     BEGIN CATCH
29         if (xact_state() = -1) ROLLBACK TRAN; throw;
30     END CATCH
31 END

 

下载查看该项目源码:

  首先大家须要将配备独立出来,当有亟待链接多少个数据库时,能够读取分歧的布置文件,让程序特别惠及灵活。

好了,小编去尝试前端能否初叶化新闻进DB

一而再再而三,插入Binding音讯(也亟需看清name不设有才insert),重临BindingID;

常量优化路径

策动: 成立分支

1 $ git checkout master2 $ git checkout -b "MVC"

  在config目录下创制db_config.py文件(有多少个库时,能够安排三个例外的参数来引用)

....

 1 CREATE PROCEDURE [base].[Binding#Insert](@json nvarchar(max), @id int out)
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5 ...
 6 
 7         declare    @name nvarchar(100);
 8         select    @name=Binding from openjson(@json, '$') with (Binding nvarchar(100))
 9 
10         -- insert Binding
11         insert    base._Binding(Name)select @name
12         where    not exists(select 1 from base._Binding p where p.Name=@name);
13 
14         select    @id=ID from base.Binding#Raw() where Name=@name;
15 
16 ...
17 END

  思路

  1)把常用的目录路线定义成常量。如 模型目录,调控器目录等
 2)引入类应用定义的常量替代部分路径。 如 include FRAME.BaseModel.class.php
3) 载入视图使用常量替代部分路径 如 include VIEW.'login.html' 轻易款式

#!/usr/bin/env python
# coding=utf-8


### 数据库链接参数 ###
DB = {
    'db_host': '127.0.0.1',
    'db_port': 5432,
    'db_name': 'simple_db',
    'db_user': 'postgres',
    'db_pass': '123456'
}
# 是否将所有要执行的Sql语句输出到日志里
IS_OUTPUT_SQL = False

在测量检验在此以前,大家须要完毕一下Init Razor Pages代码:

 

  代码完毕

1)操作步骤

图片 4图片 5

step 1: 在入口文件中定义所需要的常量step 2: 控制器中引入视图时, 使用常量进行优化 

操作步骤思路

2卡塔尔(قطر‎ 入口文件中定义常用路线常量 【index.php】

 1 <?php 2 /** 3  * 入口文件 4  */ 5 $p = !empty($_GET['p']) ? $_GET['p'] : 'Home';  //平台 6 $c = !empty($_GET['c']) ? $_GET['c'] : 'User';  //控制器 7 $a = !empty($_GET['a']) ? $_GET['a'] : 'login'; //动作 8  9 define('PLAT', $p);  //平台常量10 define('CTR', $c);  //控制器11 define('ACTION', $a); //动作12 13 14 define('DS', DIRECTORY_SEPARATOR); //目录分割符15 define('ROOT', getcwd;  //当前所在目录 项目目录16 define('FRAME', ROOT.'Frame'.DS);17 define('APP', ROOT.'App'.DS);18 define('PUB', ROOT.'Public'.DS);19 define('ADMIN', PUB.'Admin'.DS);20 define('HOME', PUB.'Home'.DS);21 22 //MVC目录23 define('MODEL', APP.'Model'.DS);24 define('VIEW', APP.'View'.DS.PLAT.DS.CTR.DS);25 define('CTRONLLER', APP.'Controller'.DS.PLAT.DS);26 27 $ctr = $c."Controller";28 29 require_once FRAME.'Db.class.php';  //数据库操作类30 require_once FRAME.'BaseModel.class.php';  //基础模型类31 require_once MODEL.'UserModel.class.php';  //用户模型类32 require_once FRAME.'FactoryModel.class.php';//模型工厂类33 require_once FRAME.'BaseController.class.php'; //基础控制器类34 require_once CTRONLLER.$ctr.'.class.php';35 36 37 //实例化控制器38 $userCtr = new $ctr();39 40 $userCtr -> $a();

2State of Qatar 常量的施用:

  后台首页调节器【App/Controller/Admin/IndexController.class.php】

图片 6图片 7

 1 <?php 2 /** 3  * IndexController控制器类 4  * 后台相关操作 5  * User: young 6  */ 7  8 class IndexController extends BaseController 9 {10     //展示后台首页11     public function index()12     {13         include VIEW.'index.html';14     }15 }

后台首页调控器引进视图路线改进

  客商调节器 登陆视图引进路线【App/Controller/Home/UserController.class.php】

图片 8图片 9

 1 <?php 2 /** 3  * UserController.class.php 用户控制器 4  */ 5  6 class UserController  extends  BaseController{ 7     /** 8      * 展示登录界面 9      * @access public10      */11     public function login()12     {13         include VIEW."login.html";14     }15 。。。16 。。。17 。。。

客商调整器登入视图引进路线

3)提交代码

$  git add -A$  git commit -m "常量使用"

  在安排中,大家雷同定义了数据库连接地址、端口、数据库名称、客商名与密码。

Init.cshtml.cs

继续,插入Book信息;

活动加载类

  别的,为了有扶助大家开展排错,检查sql的浮动情形,加多了IS_OUTPUT_SQL是或不是输出奉行的sql语句到日志中那二个开关项,设置为True时,全数被实践的sql语句都会被写到日志中,方便下载日志下来进行深入分析。

 1     using M = Shelf;
 2     public class InitModel : PageModel
 3     {
 4         private readonly IShelfRepo _shelfRepo;
 5         public InitModel(IShelfRepo shelfRepo)
 6         {
 7             _shelfRepo = shelfRepo;
 8         }
 9         [BindProperty]
10         public InitInputModel Input { get; set; }
11 
12         public void OnGet()
13         {
14 
15         }
16 
17         public async Task<IActionResult> OnPostAsync()
18         {
19             if (ModelState.IsValid)
20             {
21                 await _shelfRepo.InitAsync(new M.InitSpec
22                 {
23                     NickName = Input.NickName.Trim(),
24                     ShelfName = Input.ShelfName.Trim()
25                 });
26                 return RedirectToPage("New");
27             }
28             return Page();
29         }
30     }

继续,插入BookInfo的信息;

  思路

  难点: 入口文件中已经require_once 引进6个类,既充实二个需求引进叁个,轻便疏漏,重复和失误。

  消除方法:自动加载类文件

     方式1: 使用电动加载类函数__autoload(卡塔尔能够兑现全自动加载
  方式2: 实际项目中,六人付出,依据实用性,更加多的是接收sql_autoload_register(卡塔尔国注册函数自动加载

  依据目录的特点完毕自动加载
  Model类文件特点,以Model结尾的类名 substr($className,-5卡塔尔(قطر‎
  Controller文件特点: 以Controller结尾的类名, substr($class,-10卡塔尔国

    公共类: 类名未有统大器晚成式样,能够将Fame下的公共类放入到数组中,然后判别类是还是不是在数组中, 进而自动加载该目录下的类公事

 

页面内容也急需修正一下form部分

继续,插入BookNbr信息;

  代码完结

  1) 入口文件得以完毕类的自行加载

 1 <?php 2 /** 3  * 入口文件 4  */ 5 $p = !empty($_GET['p']) ? $_GET['p'] : 'Home';  //平台 6 $c = !empty($_GET['c']) ? $_GET['c'] : 'User';  //控制器 7 $a = !empty($_GET['a']) ? $_GET['a'] : 'login'; //动作 8  9 define('PLAT', $p);  //平台常量10 define('CTR', $c);  //控制器11 define('ACTION', $a); //动作12 13 14 define('DS', DIRECTORY_SEPARATOR); //目录分割符15 define('ROOT', getcwd;  //当前所在目录 项目目录16 define('FRAME', ROOT.'Frame'.DS);17 define('APP', ROOT.'App'.DS);18 define('PUB', ROOT.'Public'.DS);19 define('ADMIN', PUB.'Admin'.DS);20 define('HOME', PUB.'Home'.DS);21 22 //MVC目录23 define('MODEL', APP.'Model'.DS);24 define('VIEW', APP.'View'.DS.PLAT.DS.CTR.DS);25 define('CTRONLLER', APP.'Controller'.DS.PLAT.DS);26 27 $ctr = $c."Controller";28 29 spl_autoload_register('autoload'); //注册自动加载函数30 //自动加载类31 /**32  * 实自动加载类文件33  * @param  string $className 类名34  */35 function autoload($className)36 {37     $upperClassName = strtoupper($className);38     $frame = array('BaseController','BaseModel','Db','FactoryModel');39     if(in_array($className, $frame)) {  //加载公共Frame目录中的类文件40         require_once FRAME."$className.class.php";41     } elseif(substr($upperClassName, -5) == 'MODEL'){  //加载模型Model目录中的类文件42         require_once MODEL."$className.class.php";43     } elseif(substr($upperClassName, -10) == 'CONTROLLER'){  //加载控制器目录中的类文件44         require_once CTRONLLER."$className.class.php";45     }46 }47 48 //实例化控制器49 $userCtr = new $ctr();50 $userCtr -> $a();

2) 提交代码

1 $  git add -A2 $  git commit -m "自动加载类完成"

  对于数据库操作模块,大家必要封装成一个类,在有须要调用时,就足以透过with语句举办发轫化操作,设置相应的数据库链接配置,灵活的连续几日差异的数据库。

Init.cshtml

继续,插入BookSupplement信息;

优化进口文件

  在策画操作类时,大家要求观念多少个难点:

 1 <form method="post">
 2     <div class="form-group form-group-lg">
 3         <label asp-for="Input.NickName"></label>
 4         <input class="form-control form-control-lg" asp-for="Input.NickName" autocomplete="off">
 5         
 6     </div>
 7     <div class="form-group form-group-lg">
 8         <label asp-for="Input.ShelfName"></label>
 9         <input class="form-control form-control-lg" asp-for="Input.ShelfName" autocomplete="off">
10         
11     </div>
12     <div class="form-group text-right">
13         <button class="btn btn-warning btn-lg" type="submit">Save</button>
14     </div>
15 </form>

继续,插入BookTag信息;

  思路

  难点: 此时,入口文件代码零碎增加,随着再三再四代码的加码,入口文件会愈加丰腴复杂,不易管理

  解决措施: 封装入口文件中的操作称为贰个类,那样只须要在进口文件调用类的法子就可以

    创建Init.class.php类文件,放入到Frame中
   将进口文件全部操作封装成类方法
loadClass(卡塔尔(قطر‎ 设置自动加载函数
autoload()自动加载类
setConst(卡塔尔(قطر‎ 定义常量
dispatch() 前端分发器

  1.它能够支撑许多据库操作,即读取不一样的布置能三翻五次操作分歧的数据库(能够透过类开首化时开展注入配置消息)

填写不动书房的新闻:

 1 CREATE PROCEDURE [base].[BookTag#Insert](@json nvarchar(max), @bookID bigint)
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5 ...
 6 
 7         -- insert Tag
 8         insert    base._Tag(Name)select value
 9         from    openjson(@json, '$.Tags') x
10         where    not exists(select 1 from base._Tag p where p.Name=x.value);
11 
12         insert    base._BookTag(BookID, TagID) select @bookID, x.ID
13         from    openjson(@json, '$.Tags') j join base.Tag#Raw() x on x.Name=j.value
14 
15 ...
16 END

  代码完结

  1卡塔尔国 在Frame目录中创建Init.class.php文件, 将进口文件index中的代码复制进行改换为类

  【Frame/Init.class.php】

 1 <?php 2 /** 3  * 应用初始化操作类 4  * User: young 5  */ 6  7 class Init 8 { 9     protected static $frame = array('BaseController','BaseModel','Db','FactoryModel'); //Frame目录公共操作类10     public static function run()11     {12         //平台13         self::dispatch();14 15         //定义常量16         self::setConst();17 18         //自动加载类19         self::loadClass();20 21         $ctr = CTR."Controller";  //拼接控制器名称22 23         //实例化控制器24         $ctrObj = new $ctr();25         $a = ACTION;26         $ctrObj -> $a();27     }28     /**29      * 设置自动加载类方法30      */31     private static function loadClass()32     {33         spl_autoload_register('self::autoload');34     }35 36     /**37      * 实现自动加载38      * @param  string $className 类名39      */40     private static function autoload($className)41     {42         $upperClassName = strtoupper($className);43         if(in_array($className, static::$frame)) {44             require_once FRAME."$className.class.php";45         } elseif(substr($upperClassName, -5) == 'MODEL'){46             require_once MODEL."$className.class.php";47         } elseif(substr($upperClassName, -10) == 'CONTROLLER'){48             require_once CTRONLLER."$className.class.php";49         }50     }51 52     /**53      * 定义常量54      */55     private static function setConst()56     {57         define('DS', DIRECTORY_SEPARATOR); //目录分割符58         define('ROOT', getcwd().DS);59         define('FRAME', ROOT.'Frame'.DS);60         define('APP', ROOT.'App'.DS);61         define('PUB', ROOT.'Public'.DS);62         define('ADMIN', PUB.'Admin'.DS);63         define('HOME', PUB.'Home'.DS);64 65 66         define('MODEL', APP.'Model'.DS);67         define('VIEW', APP.'View'.DS.PLAT.DS.CTR.DS);68         define('CTRONLLER', APP.'Controller'.DS.PLAT.DS);69     }70 71     /**72      * 获取 p c a 的GET值,并设置为常量73      * @return void74      */75     private static function dispatch()76     {77         $p = !empty($_GET['p']) ? $_GET['p'] : 'Home';  //平台78         $c = !empty($_GET['c']) ? $_GET['c'] : 'User';  //控制器79         $a = !empty($_GET['a']) ? $_GET['a'] : 'login'; //动作80 81         define('PLAT', $p);82         define('CTR', $c);83         define('ACTION', $a);84     }85 }

2卡塔尔 入口文件引进初叶化类,并调用其艺术 【index.php】

1 <?php2 /**3  * 入口文件4  */5 6 require_once './Frame/Init.class.php';7 Init::run();

3卡塔尔国 提交代码

1 $  git add -A2 $  git commit -m "优化入口文件,封装初始化类"

  2.它须求帮忙with语句,当大家忘记关闭数据库游标和延续时,自动帮大家关闭(要求得以达成__enter__()与__exit__()方法)

图片 10

 

安然访谈项目目录

  3.它供给协理数据库事务,当实施停业时,能够回滚数据,当全数sql施行都建功立业时,能够统风姿罗曼蒂克交由业务(须要创建rollback(卡塔尔国与commit(卡塔尔国方法)

 

继续,插入BookAuthor信息;

  思路

  难题: 那时,项目中具有目录都是足以因此浏览器访问的,如直接待上访谈Frame/Db.class.php文件 直接能够去查看数据库登入讯息,分明是不安全的。

  解决办法:

    方式1: 在能够采访的公文开始处定义常量,访谈是判别是不是定义常量defined, 没有定义内定常量则直接exit('Access Deny'卡塔尔国;

    格局2: 开启布满式权限配置,编写.htaccess文件, 如幸免访谈, 将该文件放置在取缔访谈的目录中

  4.它须求援救查询、增加、纠正、删除等操作,方便大家操作关系型数据库记录(必要创建sql实行措施)

 点击Save按键提交,OK,符合规律提交了并跳转了。

 1 CREATE PROCEDURE [base].[BookAuthor#Insert](@json nvarchar(max), @bookID bigint)
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5 ...
 6 
 7         -- insert Author
 8         insert    base._Author(Name)select value
 9         from    openjson(@json, '$.Authors') x
10         where    not exists(select 1 from base._Author p where p.Name=x.value);
11 
12         insert    base._BookAuthor(BookID, AuthorID) select @bookID, x.ID
13         from    openjson(@json, '$.Authors') j join base.Author#Raw() x on x.Name=j.value
14 
15 ...
16 END

  实现

  1)使用上述办法2的款型来完毕, 站点配置中投入后生可畏项(景况搭建时已经参预了): 详细见: PHP源码搭建博客1-意况搭建

  apache配置文件httpd-vhosts.conf 中站点构造

1  #允许分布式权限配置2   AllowOverride All

  2) 重启apache后,编写 .htaccess文件, 该文件内容:

deny from all

  3) 将.htaccess文件放置禁绝访问的目录中。 如App/ , Frame/ 目录下。只用放在第生机勃勃层就能够,内层目录自动区别意直接待上访谈。

图片 11

  4卡塔尔国 访谈测量试验

图片 12

  5.它须要补助sql施行优化,将高于内定推行时间的sql语句记录到日志中,方便开采人士实行剖释(必要记录sql施行最初时间与结束时间,并拓宽总计,当这么些时刻大于钦点值时进行日志写入程序)

上一篇:sqlserver升高篇 下一篇:没有了

Copyright © 2015-2019 http://www.carrefourstation.com. 澳门新莆京手机网站-新蒲京娱乐场有限公司 版权所有