原 Python3——Django2新手教程(5)django和数据库(sqlite、mysql、多数据库)

8、django和数据库交互

8.0、前注

觉得文章写的对你有帮助,请点赞、关注、Star。

项目资源的github地址:

https://github.com/qq20004604/Python3_Django_Demo

8.1、环境安装

首先,确保你安装了mysql。

如果没有安装,就只能和sqlite交互了。

安装方法:

可以使用我这里提供的 docker 版 mysql,如果是linux或者MacOS系统,直接运行 sh 文件就能安装了。

https://github.com/qq20004604/docker-learning/tree/master/docker-demo-02-MySQL

可以用安装一个虚拟机,装上centos,然后将文件下载到虚拟机上,运行 create-image-mysql.sh 文件。

装虚拟机的教程参照我这篇文章:

https://blog.csdn.net/qq20004604/article/details/85080666

配置MySQL:(账号密码来自于docker版的配置)

  • 账号为 root;
  • 密码为 fwefwefvvdsbwrgbr9jj24intwev0h0nbor32fwfmv1;
  • 端口(port)为3306;
  • IP地址为:xxx;

8.2、diango和数据库的交互方式

diango和mysql交互有两种方式:

  1. 使用自带的驱动(默认是sqlite3,但可以改为mysql),以ORM形式和mysql交互;
  2. 使用第三方的 mysql-connector 数据库,参照廖雪峰的Python3教程(链接略,github项目readme里此处有);或者使用我自己二次封装的:https://github.com/qq20004604/python3-lib/tree/master/mysql_lingling

本文包含:

  • sqlite3;
  • ORM形式和mysql交互;
  • 多数据库情况下和mysql交互;

通过第三方python和mysql交互的使用方法,可以参考上面2个教程。

方便程度来说,sqlite最方便,第三方也很方便,使用自带mysql驱动最麻烦(版本不对的话要修改配置文件,很坑)。

8.3、和sqlite3交互

8.3.1、新建应用:
python manage.py startapp withdb

然后注册应用,编辑 settings.py,在 INSTALLED_APPS 添加新增应用withdb(homepage是之前添加的)

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 以上都是默认,下面是新加入的
    'homepage',
    'withdb'
]
8.3.2、配置model

编辑 withdb/models.py 文件:

from django.db import models


# Create your models here.

# 这个其实就是在建立表结构,使用的是 ORM 的思想
class UserInfo(models.Model):
    # 字段名id,primary_key=True表示主键自增
    id = models.AutoField(primary_key=True)
    # 字段名为username和password,max_length 指这2个字段的最大长度。
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=20)
    # 其他参数可选配置包括:(可以点进CharFiled函数查看)
    # verbose_name = None, name = None, primary_key = False,
    # max_length = None, unique = False, blank = False, null = False,
    # db_index = False, rel = None, default = NOT_PROVIDED, editable = True,
    # serialize = True, unique_for_date = None, unique_for_month = None,
    # unique_for_year = None, choices = None, help_text = '', db_column = None,
    # db_tablespace = None, auto_created = False, validators = (),
    # error_messages = None

以上作用是设置3个字段:id(自增),username,password

8.3.3、创建 table

执行命令:

python manage.py makemigrations

这个命令的效果,相当于在该 app(应用)下建立 migrations 目录,并记录下你所有的关于 modes.py 的改动,比如 0001_initial.py, 但是这个改动还没有作用到数据库文件。

再执行命令:

python manage.py migrate

这个命令的效果,是将上面的改动,作用到数据库之中。

8.3.4、添加路由

老规矩,修改 urls.py ,添加路由:

# 显示页面
path('user/', db_views.index),
# 注册用户
path('user/register', db_views.register),
# 查看用户列表
path('user/getusers', db_views.getusers)
8.3.5、添加异步请求的处理逻辑

修改 withdb/views.py,内容如下:

from django.shortcuts import render, HttpResponse
# 引入
from withdb import models
import hashlib
import json


# 返回字符串的 md5 编码
def get_md5(str):
    md5 = hashlib.md5()
    md5.update(str.encode('utf-8'))
    # 返回 md5 字符串
    return md5.hexdigest()


# Create your views here.
# 这个是显示页面的逻辑(好吧,也没啥逻辑可说)
def index(request):
    return render(request, 'users.html')


# 这个是注册的逻辑
def register(request):
    print('register')
    if not request.method == 'POST':
        return HttpResponse(json.dumps({'code': 0, 'msg': '请求类型错误'}), content_type="application/json")

    # 如果是 POST 请求
    data = json.loads(request.body)
    username = data["username"]
    pw = data["pw"]
    err = None
    # 先验证一下是否符合要求
    if len(username) > 20 or len(pw) > 20:
        err = '用户名或者密码长度过长'
    if len(username) < 8 or len(pw) < 8:
        err = '用户名或密码长度过短(不应短于8位)'

    # 如果有报错信息,则返回
    if not err == None:
        return HttpResponse(json.dumps({'code': 0, 'msg': err}), content_type="application/json")

    # 截取md5前20位(避免明文密码存取)
    md5_pw = get_md5(pw)[0:20]
    # 一切顺利保存到数据库
    save_success = True
    # 防止存储失败
    msg = 'success'
    try:
        # 这个的返回值,是当前 list 的所有值
        models.UserInfo.objects.create(username=username, password=md5_pw)
    except BaseException as e:
        save_success = False
        with open('./log.log', 'a') as f:
            f.write('username = %s ,pw = %s , error = %s\n' % (username, pw, e))
    print(save_success, msg, '-------------------')
    if save_success:
        return HttpResponse(json.dumps({'code': 0, 'msg': msg}), content_type="application/json")
    else:
        return HttpResponse(json.dumps({'code': 0, 'msg': '发生未知错误'}), content_type="application/json")


# 这个是获取用户列表的逻辑
def getusers(request):
    user_list = models.UserInfo.objects.all()
    list = []
    # user_list 是一个 <class 'django.db.models.query.QuerySet'>
    for i in user_list:
        # i 是一个 <class 'withdb.models.UserInfo'> ,注意,不要把pw返回回去了
        list.append({
            'username': i.username,
            'id': i.id
        })
    result = {'code': 0, 'data': list}
    return HttpResponse(json.dumps(result), content_type="application/json")

分为三部分:

  1. 返回页面;
  2. 注册用户;
  3. 显示用户列表;

第二步中,注册用户应该先查询,然后排除重复的用户名,不重复才能注册,这里省略掉了(因为第三步已经有相关逻辑,这里只是教程所以没写出来)

8.3.6、写前端页面

templates 目录下创建 user.html

内容如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>用户</title>
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
</head>
<body>
<p>
    用户列表
    <button id="getlist">点击刷新</button>
</p>
<ul id="list">
    <li>空</li>
</ul>

<div>
    <h2>注册</h2>
    <p>用户名:<input type="text" id="username"></p>
    <p>密码:<input type="text" id="pw"></p>
    <p>
        <button id="register">注册</button>
    </p>
    <p id="msg"></p>
</div>
<script>
    let csrf_token = '{{ csrf_token }}'
    $(function () {
        $("#getlist").click(function () {
            $.ajax({
                url: '/user/getusers',
                type: 'get',
                headers: {
                    // 要加 csrf 的请求头,如下
                    "X-CSRFToken": csrf_token
                },
                // 告诉服务器返回信息要以json格式返回
                dataType: "json",
            }).done(function (result) {
                // 打印返回结果
                console.log(result)
                // 将返回信息插入到页面中
                let text = result.data.map(user => {
                    return `<li>用户id:${user.id},用户名:${user.username}</li>`
                }).join('')
                if (text.length === 0) {
                    text = '<li>没有加载到数据</li>'
                }
                $("#list").html(text)
            })
        })

        $("#register").click(function () {
            $.ajax({
                url: '/user/register',
                type: 'post',
                headers: {
                    // 要加 csrf 的请求头,如下
                    "X-CSRFToken": csrf_token,
                    // 要改请求头,以 json 格式发送信息
                    'Content-Type': 'application/json',
                },
                // 发送的数据要先转为 json 格式
                data: JSON.stringify({
                    username: $("#username").val(),
                    pw: $("#pw").val()
                }),
                // 告诉服务器返回信息要以json格式返回
                dataType: "json",
            }).done(function (result) {
                // 打印返回结果
                console.log(result)
                // 将返回信息插入到页面中
                $("#msg").html(result.msg)
            })
        })
    })
</script>
</body>
</html>

两个功能:

  • 显示用户列表;
  • 注册新用户;
8.3.7、梳理逻辑和验证

1、用户访问链接 /user/ 显示页面(templates/user.html);

2、页面里有两个功能:【显示用户列表】和【注册新用户】

3、显示用户列表:将请求链接:/user/getusers,然后通过 ORM 表结构(widthdb/models.py)来读取数据库,并通过 widthdb/views.py 的 getusers 函数返回相关信息;

4、注册用户:以异步请求的方式将username和password字段通过 /user/getusers 传入到 widthdb/views.py 的 register 函数来处理。在注册前,会验证一下数据是否符合要求。不符合则提示错误信息,符合则将数据插入到数据库之中;

分别点击页面的 点击刷新按钮,和注册按钮,发现可以正常运行(可以参考我的代码 https://github.com/qq20004604/Python3_Django_Demo

8.4、和mysql交互(ORM形式)

8.4.1、简述

方式和8.3的 sqlite3 使用方式几乎一样,唯一区别是存到 mysql 里,而不是 sqlite 里。

这里使用的是django默认和mysql交互的方式来实现的。(不需要手写sql,通过ORM形式实现和数据库的管理)

8.4.2、引入mysql驱动

1、python和mysql交互,需要装一个驱动:

pip install pymysql

2、然后编辑 urls.py 同级目录的 __init__.py,添加内容:

import pymysql

pymysql.install_as_MySQLdb()

3、但是报错:

django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.

查找N篇解决方案,此方案可供参考:

https://blog.csdn.net/weixin_33127753/article/details/89100552

选用方案二:【删除源代码的警告代码!】(真TM暴力)

简单来说,就是注释掉那行报错的判断信息就行了。

4、注释后,有红字提示:

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

意思是你的应用数据库没迁移(指没从sqlite3迁移到mysql里)。

8.4.3、配置database

编辑 settings.py 的 DATABASES 属性,如下:

DATABASES = {
	# 这里是和mysql交互
	'default': {
		'ENGINE': 'django.db.backends.mysql',  # 数据库引擎,这个用的是pymysql,具体见官方文档
		'NAME': 'django',  # 这里是database 名
		'USER': 'root',  # 为了方便,用root,实际应用中应添加特定用户
		'PASSWORD': 'fwefwefvvdsbwrgbr9jj24intwev0h0nbor32fwfmv1',  # 密码
		'HOST': '127.0.0.1',  # mysql服务所在的主机ip,这里是在本机用的docker装的mysql,所以是127.0.0.1
		'PORT': '3306',  # mysql服务端口
		'OPTIONS': {
			'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
		}
	}
}
8.4.4、数据库准备

1、在数据库里建立 database(名字等于上面 mysqldb 配置中 NAME 的值);

create database django;

2、初始化数据库的表配置:

记录改动:

python manage.py makemigrations

插入到数据库中:

python manage.py migrate

3、查看数据库

进入mysql,然后 use [database_name];,再查看有哪些表 show tables;(可以看到有一个表格django_migrations),再查看表内容 select * from django_migrations;,发现是有数据的。

mysql> show tables;
+----------------------------+
| Tables_in_django           |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
| withdb_userinfo            |
+----------------------------+
11 rows in set (0.01 sec)
8.4.5、其他配置

参考8.3.4、8.3.5、8.3.6,这个时候刷新数据。

验证后可以发现和sqlite版一样正常运行。

8.5、多数据库配置

这里指:

  • 同时使用sqlite3和mysql;
  • 或者mysql使用不同database;
  • 或者直接就是不同server的mysql数据库;
8.5.1、修改 settings.py

修改 settings.py,先修改 DATABASE的配置:

DATABASES = {
    'sqlitedb': {
        # 之前的sqlite3配置
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3.bak'),
    },
    # 这里是和mysql交互,用于配置默认的那些app
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 数据库引擎,这个用的是pymysql,具体见官方文档
        'NAME': 'django_default',  # 这里是database 名
        'USER': 'root',  # 为了方便,用root,实际应用中应添加特定用户
        'PASSWORD': 'fwefwefvvdsbwrgbr9jj24intwev0h0nbor32fwfmv1',  # 密码
        'HOST': '127.0.0.1',  # mysql服务所在的主机ip,这里是在本机用的docker装的mysql,所以是127.0.0.1
        'PORT': '3306',  # mysql服务端口
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
        }
    },
    # 这里是和mysql交互,用于 withdb 这个应用使用
    'userdb': {
        'ENGINE': 'django.db.backends.mysql',  # 数据库引擎,这个用的是pymysql,具体见官方文档
        'NAME': 'django',  # 这里是database 名
        'USER': 'root',  # 为了方便,用root,实际应用中应添加特定用户
        'PASSWORD': 'fwefwefvvdsbwrgbr9jj24intwev0h0nbor32fwfmv1',  # 密码
        'HOST': '127.0.0.1',  # mysql服务所在的主机ip,这里是在本机用的docker装的mysql,所以是127.0.0.1
        'PORT': '3306',  # mysql服务端口
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
        }
    }
}

再添加两行配置:

# 多应用的db路由
DATABASE_ROUTERS = ['Python3_Django_Demo.database_router.DatabaseAppsRouter']
DATABASE_APPS_MAPPING = {
    # example:
    # 'app_name':'database_name',
    # 这些是默认应用
    'admin': 'default',
    'auth': 'default',
    'contenttypes': 'default',
    'sessions': 'default',
    'messages': 'default',
    'staticfiles': 'default',
    # 这些是自定义应用
    'withdb': 'userdb',
}

以上配置表示,使用 database_router.py 文件里的 DatabaseAppsRouter 函数来处理路由。

以及不同app默认使用不同的db设置(也可以在实际使用的时候,手动指定具体使用哪一个)

8.5.2、编写db的路由函数

settings.py 同目录下,新建 database_router.py 文件。

内容如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from django.conf import settings

DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING


class DatabaseAppsRouter(object):
    """
    A router to control all database operations on models for different
    databases.

    In case an app is not set in settings.DATABASE_APPS_MAPPING, the router
    will fallback to the `default` database.

    Settings example:

    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}

    这个路由,针对对不同的数据库,用于操作所有的数据库模型

    如果一个应用没有在 settings 里的 DATABASE_APPS_MAPPING 中配置,那么这个路由将最后被 default 这个数据库来配置

    设置示例(这里指 DATABASE_APPS_MAPPING 属性):

    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
    """

    def db_for_read(self, model, **hints):
        """"
        Point all read operations to the specific database.
        将所有读取操作,指向对特定的数据库
        """
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def db_for_write(self, model, **hints):
        """
        Point all write operations to the specific database.
        将所有的写操作,指向特定的数据库
        """
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow any relation between apps that use the same database.
        允许 任何使用相同数据库的应用 的任何关系
        """
        db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
        db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
        if db_obj1 and db_obj2:
            if db_obj1 == db_obj2:
                return True
            else:
                return False
        return None

    # Django 1.7 - Django 1.11
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        print
        db, app_label, model_name, hints
        if db in DATABASE_MAPPING.values():
            return DATABASE_MAPPING.get(app_label) == db
        elif app_label in DATABASE_MAPPING:
            return False
        return None
8.5.3、数据库准备

我们依然需要进行数据库准备。

首先需要创建2个database,分别是:django_defaultdjango 这2个。

然后执行以下三行代码做数据库的准备(创建table等)

记录改动:

python manage.py makemigrations

插入到数据库中:(后面的名字是下面DATABASES的属性的key值)

python manage.py migrate
python manage.py migrate --database=userdb

上面两行 migrate ,不带参数的,只生成default里配置的那些app的table。

带参数的,生成 DATABASES 这个 dict 里 key 为 userdb ,跟他相关的应用所需要的 table。

8.5.4、其他

其他的复用之前使用默认mysql配置的代码,不需要改动。

唯一需要提一下的是,在 views.py 里操作数据库,我们之前使用代码例如:

models.UserInfo.objects.all()

这里是使用默认指定的db,我们也可以使用指定db,如下:

models.UserInfo.objects.using('userdb').all()

源代码参照我的github项目:(安装好依赖后就直接能跑)

https://github.com/qq20004604/Python3_Django_Demo

8.6、总结

总的来说:

  • 和sqlite交互比较简单,适合轻量级使用,不需要捣鼓mysql环境;
  • 和mysql交互更专业,但配环境很复杂;
  • 一般个人项目需求,觉得sqlite无法满足需求的,可以考虑使用第三方框架来实现操作,比如使用我二次封装的那个;

本文项目地址:

https://github.com/qq20004604/Python3_Django_Demo

请麻烦给一个Star和fork,github上有博主的QQ群,欢迎加群讨论。

参考文献:

  • 略,因为很多都有错误 =_=||
  • 16
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值