验证

在static/js下新建一个reg.js文件.
这里有个小tips,就是在js的开头写一个分号”;”,因为考虑到多个js文件合并的问题。最小化的js,大家都跑到一行去了,如果不加分号难免会出错。

并且,如果js是子页面的,那么在主模板js上就不能直接写相关的js文件,而是在主模板下的script的最下面添加:

  1. {% block js %}
  2. {% endblock %}

然后在子页面中的这块中写我们的js文件地址。

  1. {% block js %}
  2. <script type="text/javascript" src="{{buildStaticUrl('/js/reg.js')}}"></script>
  3. {% endblock %}

这样的话,在子页面中的js就能正常的加载在主模板的js下面。
image.png
同理,如果是css的话,在主模板中使用自定义的块:

  1. {% block css %}
  2. {% endblock %}

然后子模版的特定的css写在这个块里面。

如果元素是form表单,那么点击submit之后,链接会自动产生一个get请求,即在浏览器中可以看到链接后面多了一个问号?

js内容

  1. ;
  2. var member_reg_ops =
  3. {
  4. init:function ()
  5. {
  6. this.eventBind();
  7. },
  8. eventBind:function()
  9. {
  10. $(".reg_wrap .do-reg").click(function(){
  11. // 校验,如果正在处理中,不允许重复点击提交确认按钮
  12. var btn_target = $(this);
  13. if (btn_target.hasClass("disabled")) {
  14. alert("正在处理中,请不要重复点击~");
  15. return ;
  16. }
  17. var login_name = $('.reg_wrap input[name=login_name').val();
  18. var login_pwd = $('.reg_wrap input[name=login_pwd').val();
  19. var login_pwd2 = $('.reg_wrap input[name=login_pwd2').val();
  20. if (login_name == undefined||login_name.length<1) {
  21. alert("请输入用户名~");
  22. return ;/*这里的return不知道是干啥用的*/
  23. };
  24. if (login_pwd==undefined || login_pwd.length<6) {
  25. alert("请输入正确的密码!");
  26. return ;
  27. };
  28. if (login_pwd2!=login_pwd||login_pwd2==undefined) {
  29. alert("两次密码不一致!");
  30. return ;
  31. };
  32. btn_target.addClass("disabled");
  33. // 上面通过后将要进行Ajax处理,此时先添加一个disabled的类
  34. // 下面是jQuery中的Ajax的写法
  35. $.ajax({
  36. url:"/member/reg",
  37. type:"POST",
  38. data:{
  39. login_name:login_name,
  40. login_pwd:login_pwd,
  41. login_pwd2:login_pwd2,
  42. },
  43. dataType:"json",
  44. success:function (res){
  45. //如果成功的话,将disabled的类移除
  46. btn_target.removeClass("disabled")
  47. }
  48. });
  49. });
  50. }
  51. };
  52. $(document).ready(function(){
  53. member_reg_ops.init();
  54. });

后端校验并返回

因为此时在member.py中定义的reg请求,是使用的默认的get不是post,所以这里的post出错了,因此需要去member.py中修改一下请求方法。
image.png

前端校验写完了,并且数据也发送到后端了,后端需要进行校验和返回了。但是为什么后端需要写校验呢,因为可以通过调用接口直接发数据,前端只是针对普通用户的。

这里需要用到一个统一的返回,因此,单独需要写一个Helper.py

  1. # _*_ coding:utf-8 _*_
  2. from flask import jsonify
  3. def ops_renderJson(code =200, msg = '操作成功~' ,data = {}):
  4. resp = {'code':code,'msg':msg,'data':data}
  5. #有一点不太明白,
  6. #resp的data对应的值这里不能写jsonify(data)不然会报错Object of type 'Response' is not JSON serializable
  7. return jsonify(resp)
  8. def ops_RenderErrJson(msg = "系统繁忙,请稍后再试~",data={}):
  9. return ops_renderJson(code = -1,msg=msg,data=data)

这里出现了一个小插曲,获取request的请求方式是通过request.method 而不是request.methods,虽然在写装饰器的时候,使用了methods:@member_page.route(‘/reg’,methods = [‘GET’,’POST’])。于是我想回去从新使用postman测试一下看看request是什么【但是这个其实老师有说过,他只是个代理】,结果发现只有/可以访问,其他的都不能访问了,例如/me,./get全都404了。index.py文件:

  1. from flask import Blueprint,request,make_response,jsonify,render_template
  2. from sqlalchemy import text
  3. from application import db
  4. from common.models.user import User
  5. index_page = Blueprint("index_page",__name__)
  6. @index_page.route('/')
  7. def index():
  8. return render_template("common/layout.html")
  9. # var_a = request.args.get('a','Welcom')
  10. # return 'hello, this is me request:%sparams:%s,var_a:%s,request:%s'%(request.method,request.args,var_a,type(request))
  11. @index_page.route('/me')
  12. def hello():
  13. return "hello, this is me"
  14. @index_page.route('/get')
  15. def get():
  16. var_a = request.args.get('a','Welcom')
  17. return 'request:%sparams:%s,var_a:%s,request'%(request.method,request.args,var_a,request)
  18. @index_page.route('/post',methods=['POST'])
  19. def post():
  20. var_b = request.values['b'] if 'b' in request.values else 'welcome'
  21. return 'request: %s; params: %s; var_b: %s;%s'%(request.method,request.args,var_b,request.values)
  22. @index_page.route('/upload',methods=['POST'])
  23. def upload():
  24. var_c = request.files['file'] if 'file' in request.files else None
  25. return 'request: %s; params: %s; var_c: %s; values:%s'%(request.method,request.files,var_c,request.values)
  26. @index_page.route('/text_get')
  27. def text_get():
  28. return "text/html"
  29. @index_page.route('/text_same')
  30. def text_same():
  31. response = make_response('text/html',200)
  32. return response
  33. @index_page.route('/json')
  34. def json():
  35. data = {'a':'Tom'}
  36. import json
  37. response = make_response(json.dumps(data))
  38. response.headers['Content-type'] = 'application/json'
  39. return response
  40. @index_page.route('/json_same')
  41. def json_same():
  42. data = {'b':'Jerry'}
  43. response = make_response(jsonify(data))
  44. return response
  45. @index_page.route('/template')
  46. def template():
  47. context = {}
  48. context['user'] = {'name' : "juha",'age':25}
  49. context['subject'] = ['english','math','Chinese']
  50. return render_template ('index.html',**context)
  51. @index_page.route('/extend_template')
  52. def extend_template():
  53. return render_template ('extend_template.html')
  54. @index_page.route('/db_select')
  55. def db_select():
  56. context = {}
  57. # sql = text("select * from `user`")
  58. # result = db.engine.execute(sql)
  59. result = User.query.all()
  60. context['result'] = result
  61. return render_template("db_data.html",**context)

这里面实际用到的就只有第一个定义的根目录,其他都是学习测试用的,但是之前是可以访问的,现在不知道为啥访问不了了。
此时的member.py:

  1. # _*_ coding:utf-8 _*_
  2. from flask import Blueprint,render_template,request,jsonify
  3. from application import app
  4. from common.libs.Helper import ops_renderJson,ops_RenderErrJson
  5. from common.models.user import User
  6. #引入数据库和数据库里的登录名做校验
  7. member_page = Blueprint('member_page',__name__)
  8. @member_page.route('/reg',methods = ['GET','POST'])
  9. def reg():
  10. if request.method == "GET" :
  11. return render_template("member/reg.html")
  12. req = request.values
  13. login_name = req['login_name'] if 'login_name' in req else ""
  14. login_pwd = req['login_pwd'] if 'login_pwd' in req else ""
  15. login_pwd2 = req['login_pwd2'] if 'login_pwd2' in req else ""
  16. if login_name is None or len(login_name)<1:
  17. return ops_RenderErrJson(msg = "请输入正确的登录名~")
  18. if login_pwd is None or len(login_pwd)<6:
  19. return ops_RenderErrJson(msg = "请输入正确的密码!")
  20. if login_pwd2 is None or login_pwd2!=login_pwd:
  21. return ops_RenderErrJson(msg = "两次密码不一致!")
  22. user_info = User.query.filter_by(login_name=login_name)
  23. if user_info:
  24. return ops_RenderErrJson(msg = "登录名已被占用!")
  25. return ops_renderJson(msg = "注册成功!")
  26. @member_page.route('/login')
  27. def login():
  28. return render_template("member/login.html")

都写好了之后,这里的返回值不知道为啥不是中文字符注册成功,使用postman是没问题的。
image.png

后端往数据库写数据

终于到了我认为最重要的环节了,开始往数据库写数据了。

但是在引入数据库重复值校验的时候,我这里出了一点小问题。

user_info = User.query.filter_by(login_name=login_name)
SQL语句显示的是这样的,感觉有问题,无论我输入的是什么,这里都是会重复的。
SELECT user.ID AS user_ID, user.alias AS user_alias, user.login_name AS user_login_name
FROM user
WHERE user.login_name = %(login_name_1)s

后面发现,是我写错了,应该是user_info = User.query.filter_by(login_name=login_name).first()

SELECT user.ID AS user_ID, user.alias AS user_alias, user.login_name AS user_login_name
FROM user
WHERE user.login_name = %(login_name_1)s
LIMIT %(param_1)s

并且打印出了如下信息
2020-06-20 21:15:17,039 INFO sqlalchemy.engine.base.Engine {‘login_name_1’: ‘Jerry’, ‘param_1’: 1}

接着又踩坑了。
因为之前偷懒,直接在DataHelper中把getCurrentTime函数的入参写为了加上微秒的吗,因为希望能直接在对应的css和js文件上加上此标识,但是这里直接作为日期类型传入到数据库就会报错。

跟着一步一步操作终于成功写入数据库了,开心呐。

继续写js

为了方便把js管理起来,而不使用相对链接,这里还需要创建一个common.js.
有点复杂,不是太能看懂。

;
var common_ops = {
    // 统一控制的方法
    buildUrl:function(path,params){
        // 空字符串+path
        // 这里实现的功能是例如入参是{"a":1,"b":"LI"},拼接为?a=1&b=LI
        var url = "" + path;
        var _param_url = "";
        // params 是json
        if (params) {
            _param_url = Object.keys(params).map(function(k){

                return [encodeURIComponent(k),encodeURIComponent(params[k])].join["="]
            }).join("&");
            _param_url = "?" + _param_url
        }
        return url + _param_url;
    }
};

下面继续优化js
这里使用到了layer.js一个组件库。
common.js

;
var common_ops = {
    // 统一控制的方法
    buildUrl:function(path,params){
        // 空字符串+path
        // 这里实现的功能是例如入参是{"a":1,"b":"LI"},拼接为?a=1&b=LI
        var url = "" + path;
        var _param_url = "";
        // params 是json
        if (params) {
            _param_url = Object.keys(params).map(function(k){

                return [encodeURIComponent(k),encodeURIComponent(params[k])].join["="]
            }).join("&");
            _param_url = "?" + _param_url
        }
        return url + _param_url;
    },

    // 下面进行提示窗的优化
    alert:function(msg,cb){
        layer.alert(msg,{
            yes:function(index){
                if (typeof cb == "function") {
                    cb();
                }
                layer.close(index);
            }
        });
    }
};

reg.js

;
var member_reg_ops = 
{
    init:function () 
    {
        this.eventBind();
    },
    eventBind:function()
    {
        $(".reg_wrap .do-reg").click(function(){

            // 校验,如果正在处理中,不允许重复点击提交确认按钮
            var btn_target = $(this);
            if (btn_target.hasClass("disabled")) {
                common_ops.alert("正在处理中,请不要重复点击~");
                return ;
            }
            var login_name = $('.reg_wrap input[name=login_name').val();
            var login_pwd = $('.reg_wrap input[name=login_pwd').val();
            var login_pwd2 = $('.reg_wrap input[name=login_pwd2').val();    
            if (login_name == undefined||login_name.length<1) {
                common_ops.alert("请输入正确的用户名~");
                return ;/*这里的return不知道是干啥用的*/
            };
            if (login_pwd==undefined || login_pwd.length<6) {
                common_ops.alert("请输入正确的密码!");
                return ;
            };
            if (login_pwd2!=login_pwd||login_pwd2==undefined) {
                common_ops.alert("两次密码不一致!");
                return ;
            };



            btn_target.addClass("disabled");
            // 上面通过后将要进行Ajax处理,此时先添加一个disabled的类

            // 下面是jQuery中的Ajax的写法
            $.ajax({

                // url:"/member/reg",
                // 因为有了统一的路径管理,这里可以用下面的进行替代
                url:common_ops.buildUrl("/member/reg"),
                type:"POST",
                data:{
                    login_name:login_name,
                    login_pwd:login_pwd,
                    login_pwd2:login_pwd2,
                },
                dataType:"json",
                success:function (reg){
                    //如果成功的话,将disabled的类移除
                    btn_target.removeClass("disabled")

                    // 下面是注册成功的话返回至首页
                    var callback = null;
                    if (reg.code ==200) {

                        // 这里就是利用到了common.js中的alert方法,把函数的结果作为参数
                        callback = function(){

                        // 这里的也可以被替代了
                        // window.location.href = "/";
                        window.location.href = common_ops.buildUrl("/");

                        };

                    }
                    // 这里的弹窗点击确定之后,会回到首页
                    common_ops.alert(reg.msg,callback);
                }
            });

        });
    }
};

$(document).ready(function(){

    member_reg_ops.init();
});

老师的知识总结:

统一链接管理器;

如何正确加载模板JS和CSS文件;

版本管理;

按钮的重复点击控制;

引入的layer组件。

作业:注册页面增加一个昵称表单并添加到数据。

注册功能到此结束,感觉脑子快不够用了…