56、Django与数据库

Django框架 / 2021-02-16

Django数据库相关

一、pycharm链接数据库

pycharm有类似Navicat的图形化管理数据库功能,默认有安装没有则安装插件database tools and SQL,要么右上角要么左下角有个database按钮,点开后一定要安装提示的download missing驱动文件,点击下载即可,其余使用跟Navicat差不多。

图形化方式连接数据库:

image-20210213214219066

二、Django连接数据库

1.1、配置文件中开启配置

在settings - DATABASES那里默认的ENGINE是SQLite换成MySQL,name是要连接的数据库名,其它都是很熟悉的味道了。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'study',
        'USER': 'root',
        'PASSWORD': 'sanxi666',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'CHARSET': 'utf8'
    }
}
1.2、代码声明

django默认用的是mysqldb模块连接数据库,这模块兼容性不好,需要改为pymysql;因此需要在项目名下的init或者任意的APP名下的init文件中,书写以下代码:

import pymysql

pymysql.install_as_MySQLdb()

三、Django ORM

ORM,对象关系映射,号称能让一个不会SQL语句的小白也能通过Python面向对象的代码简单快捷地操作数据库;缺点?当然是因为封装程度过高,有时候SQL语句的执行效率偏低,可能需要自己写SQL语句。

3.1、创建表

因为是通过Python的面向对象代码,因此我们需要在APP下面的models.py自定义类

class User(models.Model):,一定要继承这个
等价于id int primary_key auto_increment
    id = models.AutoField(primary_key=True, verbose_name='主键')
    # username varchar(32),charfield必须要指定max_length,否则报错
    # verbose是所有字段都有的,就是用来对字段的解释
    username = models.CharField(max_length=32,verbose_name='用户名')
    password = models.IntegerField()
    # 由于一张表中必须要有一个主键,一般都叫id,当没定义时ORM会自动创建一个名为id的主键字段
3.2、数据库迁移命令(重点)

只要修改了models中跟数据库相关的代码,就必须执行上述两条命令!!!

# python manage.py makemigrations,将操作记录记录到小本本上,即migration文件夹里
C:\Users\AERO\PycharmProjects\djangoProject>python manage.py makemigrations
Migrations for 'study':
  study\migrations\0001_initial.py
    - Create model User
# 将操作真正同步到数据库
C:\Users\AERO\PycharmProjects\djangoProject>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, study
Running migrations:
  Applying contenttypes.0001_initial... OK

3.3、字段增删改查

当在已有数据的表中新增字段时,会提示立即给默认值还是退出手动指定,也可以在创建时指定null=True,还可以直接设置默认值default='XXXX'。

# 我在上面的表基础上添加host字段,如果不设默认值会提示,设置了就没有
host = models.CharField(max_length=32, verbose_name='主机', null=True)
# C:\Users\AERO\PycharmProjects\djangoProject>python manage.py makemigrations
# Migrations for 'study':
#  study\migrations\0002_user_host.py
#    - Add field host to user
# C:\Users\AERO\PycharmProjects\djangoProject>python manage.py migrate
# Operations to perform:
#   Apply all migrations: admin, auth, contenttypes, sessions, study
# Running migrations:
#   Applying study.0002_user_host... OK

直接改,再执行那两条命令!

# 我把host改成hostname
hostname = models.CharField(max_length=32, verbose_name='主机', null=True)
# C:\Users\AERO\PycharmProjects\djangoProject>python manage.py makemigrations
# Did you rename user.host to user.hostname (a CharField)? [y/N] y
# Migrations for 'study':
#   study\migrations\0003_auto_20210214_1028.py
#     - Rename field host on user to hostname
# C:\Users\AERO\PycharmProjects\djangoProject>python manage.py migrate
# Operations to perform:
#   Apply all migrations: admin, auth, contenttypes, sessions, study
# Running migrations:
#   Applying study.0003_auto_20210214_1028... OK

直接注释相关代码或者删除代码再执行那两条命令,里面的数据也全没了,一定要慎重操作。当你离开你的工位后,一定要锁屏!

# hostname = models.CharField(max_length=32, verbose_name='主机', null=True)
# C:\Users\AERO\PycharmProjects\djangoProject>python manage.py makemigrations
# Migrations for 'study':
#  study\migrations\0004_remove_user_hostname.py
#     - Remove field hostname from user
# C:\Users\AERO\PycharmProjects\djangoProject>python manage.py migrate
# Operations to perform:
#   Apply all migrations: admin, auth, contenttypes, sessions, study
# Running migrations:
#   Applying study.0004_remove_user_hostname... OK

你models里面定义的类就是字段啦,还看什么!

3.4、数据增删改查

比如在视图函数中,书写对应代码

from study import models
# 第一种添加数据的方式
user_obj = models.User.objects.create(username='kristal', password='666')
    
# 第二种添加方式
user_obj = models.User(username='andy', password='777')
user_obj.save()

查数据也有两种方式,分别是filter和object方法,返回值可以先看成是列表套数据对象的格式,它也支持索引取值,然后切片操作,但是不支持负数索引。

其实这两种都是一样的,取值也是同样的方式:索引+字段名,区别只是第二种作者封装了first方法而已。

# 等同于select * from User where username='sanxi',如果不写条件则意味着select * from User
user_obj = models.User.objects.filter(username='chrystal')


# 第二种方式,列表套对象,支持用索引取值或者作者封装的方法
result = models.User.objects.all()
print(result.first())  # 取对象中的第一个值,或者直接跟上字段名。
改删合并在数据展示实验里
3.5、数据展示实验

先将数据库中的数据用locals将本地所有名称空间回传给前端,用Django提供的封装好的for循环把数据拿出来全部展示到前端,然后给每一个数据两个按钮,一个编辑,一个删除。

后端回传名称空间

def index(request):
    from study import models
    user_dict = models.User.objects.all()
    return render(request, 'first.html', locals())

前端拿到后端的名称空间,用for循环将数据全部取出来展示

<body>
    <h1 class="text-center">数据展示</h1>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2">
                <table class="table table-striped table-hover">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>用户名</th>
                            <th>密码</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for user_data in user_dict %}
                            <tr>
                                <td>{{ user_data.id }}</td>
                                <td>{{ user_data.username }}</td>
                                <td>{{ user_data.password }}</td>
                                <td>
                                    <a href="" class="btn btn-primary btn-xs">编辑</a>
                                    <a href="" class="btn btn-danger btn-xs">删除</a>
                                </td>
                            </tr>
                        {% endfor %}

                    </tbody>
                </table>
            </div>
        </div>
    </div>
</body>
更改数据

编辑页面,用form表单来提交新数据给后端视图函数。

<body>
    <h1 class="text-center">编辑页面</h1>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <form action="" method="post">
                    用户名:<input type="text" name="username"  value="{{ edit_obj.username }}" class="form-control">
                    密码:<input type="password" name="password" value="{{ edit_obj.password }}" class="form-control">
                    <input type="submit" value="更改" class="btn btn-info btn-block">
                </form>
            </div>

        </div>
    </div>
</body>

编辑页面对应的视图函数

def edit_user(request):
    # 获取编辑的那条数据的id
    edit_id = request.GET.get('user_id')
    # 如果是post请求
    if request.method == 'POST':
        # 获取更改后的数据
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 将此前id对应的数据更改为新数据
        models.User.objects.filter(id=edit_id).update(username=username, password=password)
        return redirect('/index/')  # 点击提交后修改数据并返回此前页面
    edit_obj = models.User.objects.filter(id=edit_id).first()  # 获取用户对象
    return render(request, 'edit_user.html', locals())  # 将用户对象等本地所有名称空间传给前端
删除数据

实际运用中真正删除应该有二次确认,这里先不做;删除数据内部其实不是真正的删除,会给数据标识是否被删除,如数据被删仅仅是修改数据的状态为被删除。

def delete_user(request):
    # 获取用户想要删除的数据的id值
    delete_id = request.GET.get('user_id')
    # 筛选出id对应的数据然后直接删除
    models.User.objects.filter(id=delete_id).delete()
    # 返回此前页面
    return redirect('/index/')
3.7、ORM创建表关系

比如图书馆系统中有图书表、出版社表作者表、作者详情表;那么书和出版社是一对多关系,外键字段应建立在多的一方;图书和作者是多对多关系,需要创建第三张表来专门存储;作者与作者详情表是一对一关系。

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    
class Book(models.Model):  # 创建表关系,先将基表创建出来,然后再添加外键字段,默认级联更新级联删除
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)  # 小数总共八位,小数点后面最多两位
    """
    图书和出版社是一对多,并且书是多的一方,外键放这
    """
    # 默认就是与出版社表的主键字段建立外键关联,如果字段对应的是FK,ORM会自动在字段的后面加_id;如果你自己加了_id,ORM还会再加_id
    publish = models.ForeignKey(to='Publish', to_field='id')
    """
    图书和作者是多对多关系,外键在任意一方均可,建议是查询频率较高的一方
    """
    # 这个字段是虚拟字段,主要用来告诉orm,书籍表和作者表是多对多关系,让orm自动帮你创建第三章关系表
    authors = models.ManyToManyField(to='Author')

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # 作者与作者详情是一对一,可在任意一方,建议在查询频率较高的一方;也会自动加_id
    author_detail = models.OneToOneField(to='AuthorDetail')

class  AuthorDetail(models.Model):
    phone = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
世间微尘里 独爱茶酒中