Flask项目总结

Flask项目

  • 这次项目,是用flask框架实现一个可以显示租房信息的网站,提供了买家和卖家的入口,一个身份既可以发布房源,也可以根据入住时间,地区查找租房信息,并下订单,完成租房。
  • 项目源码

接口文档

  • 这次项目完全实现了前后端分离,所以在项目我们写了接口文档,以下是其中的一部分

  • 接口文档参考

"""
#这部分文档,为整个项目的文档提供一个目录功能,一般写在主项目文件夹下。
### 爱家接口文档
#### 用户模块
- [注册接口](docs/user/user_register.md)
- [登录接口](docs/user/user_login.md)
- [登出接口](docs/user/user_logout.md)
"""
"""
# 这部分是具体对某个接口具体描述,以便前端可以了解请求类型,参数,以及各种响应状态
# 文件一般写在docs文件夹下
### 注册接口
#### request请求
    post /user/register/
##### params参数:
    mobile str 电话
    password str 密码
    password2 str 确认密码


#### response响应

##### 失败响应1:
    {
        "code": 900,
        "msg": "参数错误"
    }

##### 失败响应2:
    {
        "code": 1002,
        "msg": "手机号码已注册"
    }

##### 失败响应3:
    {
        "code": 1003,
        "msg": "两次密码不一致"
    }

##### 失败响应4:
    {
        "code": 1001,
        "msg": "手机号码不符合规则"
    }

##### 成功响应:
    {
        "code": 200,
        "msg": "请求成功"
    }
"""
"""
#### 我的房源接口
#### request请求
    get /house/auth_myhouse/

#### response
##### 成功响应
    {
      "code": 200,
      "hlist_list": [
        {
          "address": "\u91d1\u878d\u4e2d\u5fc3",
          "area": "\u91d1\u725b\u533a",
          "create_time": "2018-05-24 18:48:14",
          "id": 9,
          "image": "/static/upload\\jinrongcheng1.jpg",
          "order_count": 0,
          "price": 399,
          "room": 8,
          "title": "\u91d1\u878d\u57ce"
        }
      ]
    }

##### params参数:
    address str 地址
    area str 房屋所在区域
    create_time data 创建时间
    id int 房屋id
    image str 房屋主图片路径
    order_count int 订单数
    price int 单价 单位分
    room int 房屋数
    title str 房屋标题

"""

Status Code

  • 这次项目中自定义了各种状态及其状态码,以便可以反映各种问题, 并根据不同问题做不同的处理
OK = 200
SUCCESS = {'code': 200, 'msg': '请求成功'}
DATABASE_ERROR = {'code': 401, 'msg': '数据库访问失败'}
PARAMS_ERROR = {'code': 900, 'msg': '参数错误'}
# 用户模块
USER_REGISTER_PARAMS_ERROR = {'code': 1000, 'msg': '注册信息参数错误'}
USER_REGISTER_MOBILE_ERROR = {'code': 1001, 'msg': '手机号码不符合规则'}
USER_REGISTER_MOBILE_IS_EXSITS = {'code': 1002, 'msg': '手机号码已存在'}
USER_REGISTER_PASSWORD_IS_ERROR = {'code': 1003, 'msg': '两次密码不一致'}

USER_LOGIN_IS_NOT_EXSIST = {'code': 1004, 'msg': '用户不存在'}
USER_LOGIN_PASSWORD_IS_ERROR = {'code': 1005, 'msg': '密码错误'}

USER_UPLOAD_IMAGE_IS_ERROR = {'code': 1006, 'msg': '上传图片格式错误'}
USER_NAME_IS_EXSIT = {'code': 1007, 'msg': '用户名已存在'}

USER_AUTH_ID_CARD_IS_ERROR = {'code': 1008, 'msg': '身份证格式错误'}

# 房屋模块
MYHOUSE_USER_IS_NOT_AUTH = {'code': 2000, 'msg': '用户没有实名认证'}
MYHOUSE_UPLOAD_IMAGE_IS_ERROR = {'code': 2001, 'msg': '上传图片格式错误'}
MYHOUSE_UPLOAD_IMAGE_IS_EXIST = {'code': 2002, 'msg': '图片已存在'}

# 订单模块
ORDER_START_TIME_GT_END_TIME = {'code': 3000, 'msg': '创建订单时间有误'}

Model

  • 为了减少重复代码,这次为数据创建类的时候,用到了很多新的方法。

from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash

from utils.functions import db


class BaseModel(object):
    # 定义基础的模型
    create_time = db.Column(db.DATETIME, default=datetime.now())
    update_time = db.Column(db.DATETIME, default=datetime.now(), onupdate=datetime.now())

    # 不用重复写添加和提交
    def add_update(self):
        db.session.add(self)
        db.session.commit()

    def delete(self):
        db.session.delete(self)
        db.session.commit()


# 多重继承
class User(BaseModel, db.Model):

    __tablename__ = 'ihome_user'
    id = db.Column(db.Integer, primary_key=True)
    phone = db.Column(db.String(11), unique=True)
    pwd_hash = db.Column(db.String(200))
    name = db.Column(db.String(30), unique=True)
    avatar = db.Column(db.String(100))  # 头像
    id_name = db.Column(db.String(30))  # 实名认证的姓名
    id_card = db.Column(db.String(18), unique=True)  # 实名认证的身份证号码

    houses = db.relationship('House', backref='user')
    orders = db.relationship('Order', backref='user')

    # 读
    @property
    def password(self):
        return ''

    # 写
    # 在保存密码时直接加密
    @password.setter
    def password(self, pwd):
        self.pwd_hash = generate_password_hash(pwd)

    # 对比
    def check_pwd(self, pwd):
        return check_password_hash(self.pwd_hash, pwd)

    # 为每个类写了一个转换为Json格式的方法,这样就不用在每次向前端传接口时,在编写额外的代码
    def to_basic_dict(self):
        return {
            'id': self.id,
            'avatar': self.avatar if self.avatar else '',
            'name': self.name,
            'phone': self.phone
        }


ihome_house_facility = db.Table(
    "ihome_house_facility",
    db.Column("house_id", db.Integer, db.ForeignKey("ihome_house.id"), primary_key=True),
    db.Column("facility_id", db.Integer, db.ForeignKey("ihome_facility.id"), primary_key=True)
)

装饰器

  • 为了保证有些方法只有在登录之后可以调用,在functions模块中定义了is_login的装饰器
import functools
# 定义了一个判断是否登录的装饰器
def is_login(view_fun):
    @functools.wraps(view_fun) 
    # 这句是将要装饰函数的__name__付给装饰它的函数
    # 这样可以避免几个函数同时调用这个装饰器,而互相影响
    def decorator():
        try:
            # 验证用户是否登录
            # if session['user_id']:
            if 'user_id' in session:
                return view_fun()
            else:
                return redirect('/user/login/')
        except:
            return redirect('/user/login/')
    return decorator

一些小的技巧

  • 使用all可以多个数据进行判断
  • 如何根据不同的条件调用不同的状态码
@user.route('/register/', methods=['POST'])
def user_register():
    register_dict = request.form

    mobile = register_dict.get('mobile')
    password = register_dict.get('password')
    password2 = register_dict.get('password2')

    #  检查这个3值是否有空,如果有一个值为空,返回True
    if not all([mobile, password, password2]):
        return jsonify(status_code.PARAMS_ERROR)

    if not re.match(r'^1[345789]\d{9}$', mobile):
        return jsonify(status_code.USER_REGISTER_MOBILE_ERROR)

    if User.query.filter(User.phone == mobile).count():
        return jsonify(status_code.USER_REGISTER_MOBILE_IS_EXSITS)

    if password != password2:
        return jsonify(status_code.USER_REGISTER_PASSWORD_IS_ERROR)

    user = User()
    user.phone = mobile
    user.name = mobile
    user.password = password
    try:
        user.add_update()
        return jsonify(status_code.SUCCESS)
    except Exception as e:
        return jsonify(status_code.DATABASE_ERROR)
  • is_login装饰器的使用
  • flask中上传图片
@user.route('/user/', methods=['PUT'])
@is_login
def user_profile():

    profile_dict = request.form
    file_dict = request.files
    user = User.query.filter(User.id == session['user_id']).first()

    if 'avatar' in file_dict:

        f1 = file_dict['avatar']
        # mimetype文件类型
        if not re.match(r'^image/.*$', f1.mimetype):
            return jsonify(status_code.USER_UPLOAD_IMAGE_IS_ERROR)
        # 图片在flask中的存储路径
        # 用这种方法,比直接写路径并用字符相加的方法,要节省内存
        UPLOAD_DIRS = os.path.join(os.path.join(BASEDIR, 'static'), 'upload')
        url = os.path.join(UPLOAD_DIRS, f1.filename)
        # 将图片存在flask中
        f1.save(url)

        image_url = os.path.join('/static/upload', f1.filename)
        user.avatar = image_url
        try:
            db.session.commit()
            return jsonify(code=status_code.OK, url=image_url)
        except Exception as e:
            return jsonify(status_code.DATABASE_ERROR)

    # 如果没有传参数,然后参数错误
    else:
        return jsonify(status_code.PARAMS_ERROR)
  • 多对多关联中
facilities_list = []
    for fac_id in facilities:
        fac = Facility.query.get(fac_id)
        house.facilities.append(fac)
        facilities_list.append(fac)

    db.session.add_all(facilities_list)

#通过这种方法可以实现和上面一样的效果
if facilities:
  #可以找到facilities中所有的id
    facilitys = Facility.query.filter(Facility.id.in_(facilities)).all()
    # 因为house.facilities接收的值是一个列表,所以不用一个一个用append添加
    house.facilities = facilitys
house.add_update()

前端部分

Ajax 传输数据

  • ajaxSubmit
    • 可以通过ajaxSubmit向后台传输文件数据
    • 相当于模仿表单传输数据
  • serialize
    • 可以将表单里面的数据的序列化,
  • e.preventDefault();
    • 阻止表单的默认行为,通过这种方法,可以让表单的submit按钮失效,不会在点击时想后台
    • 发送post请求提交数据,而是同我们自己定义方法传输数据,这点十分重要
    • 如果不阻止这个行为,可能后台会接收额外的数据,或者因为请求方法不对而引发错误
  • $后面可以.get post ajax
// 下面的两种方法都可以向后台传输文件数据,但明显第一种方法要更简单一点,也推荐用这一种
$(function () {
     $("#form-avatar").submit(function(e){
         e.preventDefault();
         $(this).ajaxSubmit({
             url:'/user/user/',
             type:'PUT',
             dataType:'json',
             success:function (data) {
                 if (data.code == 200) {
                     $('#user-avatar').attr('src', data.url);
                 }else if (data.code == 1006){
                     alert(data.msg)
                 }

             }
         })
          $.ajax({
              url:'/user/user/',
              type:'PUT',
              data: new FormData($('#form-avatar')[0]),
              cache: false,
              processData: false,// 告诉jQuery不要去处理发送的数据
              contentType: false,// 告诉jQuery不要去设置Content-Type请求头
              success:function (data){
                  if (data.code == 200) {
                      $('#user-avatar').attr('src', data.url);
                  }
              },
              error:function (data) {
                 alert('请求失败')
              }
          })
     });
//这种是用serialize序列化表单数据,在通过post传给后端
$('#form-house-info').submit(function (e) {
    e.preventDefault();
    $(this).serialize 可以序列化form表单的数据,在后端可以to.dict()将它转化成字典
     $.post('/house/createhouse', $(this).serialize(), function (data) {

     });

location

  • location.href=”
    • 这个后面加上url可以跳转页面,并在当前页面打开与window.open(url, target=’_self’)
    • 效果相同
  • history.go(-1);
    • 跳转到上一页
  • location.search
    • 返回当前页面?后面的参数,包括?
  • replace(原来的数据, 新的数据)
    • js中替换字符中的
var newId = $(this).attr('area-id');
var newName = $(this).html();
// 获取当前页面url中的?后面的内容例如?id=1$aname='天'
path = location.search;
// 将字符串以‘=’拆分
oldId = location.search.split('&')[0].split('=')[1];
oldName = location.search.split('&')[1].split('=')[1];
// 替换新的内容
path = path.replace('aid=' + oldId, 'aid=' + newId);
path = path.replace('aname=' + oldName, 'aname=' + newName);
# 只需要问号后面的内容就可以用location.href来跳转页面
location.href = path

hide, show

  • 这次在显示页面信息中用到最多的就是hide和show
  • 这两个方法可以对那些style是display:none;的标签进行显示和隐藏的操作

template.js

  • 在flask的学习中我们知道jinjia2模板引擎,它可以帮助我们更加方便的在页面中解析数据
  • 但是jinjia2只能用后台传来的数据,不符合前后端分离的思想,那么有没有一种方法可以接收
  • js中传入的数据呢,答案是肯定的。
  • template.js 前端模板引擎
$.get('/house/housedetail/' + window.location.search, function (data) {
    if (data.code == 200){
        // template.js模板引擎
       // 第一个参数是页面中要传输数据
                    
                    

你可能感兴趣的:(Flask项目总结)