ORM操作数据库--增删改查、表关系

pycharm链接数据库(MySQL)

这个时候pycharm就是数据库的客户端了,类似于Navicat。

链接MySQL的一些操作

1.pycharm中数据库位置

方式1:右上角的Database
方式2:左下角

2.选择MySQL

3.连接MySQL

第一次链接要下载驱动

4.点击下选框tables是查看表结构

5.双击表名是查看表数据

6.提交数据

Django链接数据库(MySQL)(掌握)

settings的设置

django默认自带的有一个小型数据库,sqlite3。

功能比较少,主要用于本地测试。我们实际项目中都会替换掉它。

# sqlite3配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

链接MySQL数据库

修改配置文件,把Django中的default注释掉,增加下面的代码。


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db7',  # 链接的数据库名称,一定要提前建好
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': '1113',
        'CHARSET': 'utf8',
    }
}

以上配置改完之后,框架启动不起来了。是因为djang框架底层链接MySQL用的模块是MySQLdb模块,这个模块的兼容性很差,所以,我们不用。需要我们人为的把MySQLdb模块改为pymysql模块。

修改pymysql模块

如果没有安装pymysql模块,需要先下载pymysql模块。

在Django框架的任意__init__.py文件中加入一下两句话:

import pymysql
pymysql.install_as_MySQLdb()  
#这句话的意思其实就是猴子补丁

如果使用pymysql模块的话,每次都要加上面两句话,也很不方便。也可以安装mysqlclient模块(后期学习),这个模块的兼容性非常强,就不用在加任何东西。但是这个模块有个缺点:一般情况下,安装不上。

Django的ORM操作

ORM: 对象关系映射。

  • 作用:我们以后再操作数据库的时候,就不用在写原生sql语句,用面向对象的代码去写,然后它给你翻译成原生sql语句。
  • 缺点:封装程度太高,ORM的sql执行效率没有原生sql执行的效率高,这个效率的影响可以暂时忽略。

关系映射:

类		>>>: 		表
对象		>>>: 		一条条数据
属性    	>>>: 		字段

创建表

models.py

ORM的相关代码是在models.py文件中书写

# 创建一张表出来
from django.db import models


# Create your models here.
# 每一个类都必须继承models.Model类
class User(models.Model):  # 表名: User
    # 创建字段---> 就是属性
    # id int primary key auto_increment
    id = models.AutoField(primary_key=True)  # 字段名 = 字段类型 + 约束条件

    # username varchar(32)
    '''max_length参数必填'''
    username = models.CharField(max_length=32)

    # password varchar(32)
    password = models.CharField(max_length=64)

迁移命令

代码写完之后,需要执行两个命令才能把表创建出来

python36 manage.py makemigrations
python36 manage.py migrate

# 第一句话是记录迁移过程的所有事情
# 第二句话才是真正的创建表
'''上面两句话缺一不可,都要执行!!!'''

'''只要修改了跟数据库相关的代码,都要执行上面两句话'''

命令的结果是:

E:\python project\day50D>python36 manage.py makemigrati
ons
Migrations for 'app01':
  app01\migrations\0001_initial.py
    - Create model User
    
E:\python project\day50D>python36 manage.py migrate
Operations to perform:
  Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying app01.0001_initial... OK
  Applying app01.0002_auto_20230422_1131... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying sessions.0001_initial... OK

执行了上面两句话后,会创建一堆表,只有一张表是新建的表,剩下的表都是迁移过程的记录表格。

django_migrations表是执行第一句话产生的表,是迁移记录表。

不想要迁移这么多张表格,可以在配置文件中把应用名注释掉。注释掉那个,就不会生成对应记录的表。

INSTALLED_APPS = [
    # 'django.contrib.admin',
    # 'django.contrib.auth',
    # 'django.contrib.contenttypes',
    # 'django.contrib.sessions',
    # 'django.contrib.messages',
    # 'django.contrib.staticfiles',
    'app01.apps.App01Config',
]

表名以后都是应用名_表名,可以去掉前缀应用名(后面学习)

主键字段可以不用创建

由于每张表都应该有一个主键字段,并且主键字段名都叫id,如果满足这两个条件,那么,可以省略不写,Django自动帮你创建主键字段出来。

如果是想要创建一个uid(其他名字的主键字段)主键,就要自己写

# 创建作者表
class Author(models.Model):
    # username varchar(32)
    username = models.CharField(max_length=32)  # 字段名 = 字段类型 + 约束条件

    # password varchar(32)
    password = models.CharField(max_length=32)

执行迁移命令的结果:

E:\python project\day50D>python36 manage.py makemigrati
ons
Migrations for 'app01':
  app01\migrations\0003_author.py
    - Create model Author        

E:\python project\day50D>python36 manage.py migrate
Operations to perform:
  Apply all migrations: admin, app01, auth, contenttype
s, sessions
Running migrations:
  Applying app01.0003_author... OK

迁移表只会在应用第一次创建表时增加,之后再建的表,不会再创建一堆迁移表了。只有自己的表了。

orm针对字段的增删改查

增加

方式一:增加age,执行命令后,在控制台设置默认值
方式二:新增字段age,直接设置默认值

给作者表增加age,直接在models.py的Author类中写age,只要修改了跟数据库相关的代码,都要执行两句话。

class Author(models.Model):
    # username varchar(32)
    username = models.CharField(max_length=32)

    # password varchar(32)
    password = models.CharField(max_length=32)

    # 新增加字段age
    # age int
    age = models.IntegerField()

# Django中只提供了int整型,想要使用smallint,bigint目前不能做,----> 需要后面我们自定义方法。在方法中用smallint,bigint。

执行两句话产生的结果:

E:\python project\day50D>python36 manage.py makemigrations
Migrations for 'app01':

You are trying to add a non-nullable field 'age' to author without a default;(你正试图添加一个没有默认值的非空字段'年龄'到作者表中)
we can't do that (the databapulate existing rows).  
se needs something to populate existing rows).   (我们不能这样做(数据库的现有行需要一些东西来填充现有行))
Please select a fix:  
 1) Provide a one-off default now (will be set on all  existing rows with a null value for this column)    # (现在提供一次性默认值(将在所有现有行上设置此列的空值))
 2) Quit, and let me add a default in models.py      # 2是退出,在models.py中添加一个默认值
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 24
Migrations for 'app01':
  app01\migrations\0004_author_age.py
    - Add field age to author

E:\python project\day50D>python36 manage.py migrate
Operations to perform:
  Apply all migrations: admin, app01, auth, contentty
pes, sessions
Running migrations:
  Applying app01.0004_author_age... OK

方式二:新增字段时,设置默认值

# 创建字段时,直接给默认值
    book = models.CharField(max_length=32, default='书书书')

修改字段

直接在字段上面修改,执行两句话

删除字段

直接注释,然后执行两句话。所以,以后在models.py文件中,不要轻易注释

ORM针对数据的增删改查

models.UserInfo.objects.create()  # insert into
models.UserInfo.objects.filter()  # where
models.UserInfo.objects.filter().update()  # update
models.UserInfo.objects.filter().delete()  # delete from 

补充知识点:模板变量的分配

render中的第三个参数:

方式一:放一个字典数据,{'自己定义的名字':'视图函数中的变量名'}

方式二:locals()

具体使用:

views.py:

def ab_render(request):
    '''模板变量展示'''
    # 分配变量到模板中第一种方式:
    user_dict = {'username': 'kevin', 'password': '123'}
    user_dict1 = {'aaa': 111}
    # return render(request, 'ab_render.html', {'user_dict': user_dict, 'user_dict1': user_dict1})

    # 第二种方式:
    # 会把该函数局部里面的所有变量都分配到模板文件中
    print(locals())
    # {'user_dict1': {'aaa': 111}, 'user_dict': {'username': 'kevin', 'password': '123'}, 'request': <WSGIRequest: GET '/ab_render/'>}
    return render(request, 'ab_render.html', locals())

ab_render.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load static %}
        <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>

</head>
<body>
{{ user_dict }}
{{ user_dict.username }}
{{ user_dict.password }}

<div>
    {{ user_dict1 }}
</div>

</body>
</html>

用户数据的增加

# 方式一:对象.create()方法
models.Userinfo.objects.create(username=name, password=pwd)

# 方式二:对象.save()
obj = models.Userinfo(username=name, password=pwd)
obj.save()

根据用户注册的功能来展示ORM中对数据的增删改查操作

增加数据步骤:

  1. 写对应关系和视图函数框架

  2. 先向用户展示一个注册页面

  3. Django链接MySQL,配置文件修改

  4. model.py文件中建表结构

  5. 接收前端传来的数据

  6. 添加到表中

  7. 数据展示给客户,新页面,重定向redirect

models.py创建表结构

from django.db import models


# Create your models here.

class Userinfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    is_delete = models.IntegerField(default=0)

views.py文件:

from app01 import models

def register(request):
    if request.method == 'POST':
        # 1.获取前端表单中提交过来的数据
        name = request.POST.get('username')
        pwd = request.POST.get('password')

        # 2.把数据插入到表中
        # 第一种方式:对象.create()方法
        res = models.Userinfo.objects.create(username=name, password=pwd)
        # # res:返回的是当前插入的对象
        # print(res)  # Userinfo object
        # print(res.username)  # 字段=对象.属性
        # print(res.password)

        # 第二种方式:对象.save()
        # obj = models.Userinfo(username=name, password=pwd)
        # print(obj)  # Userinfo object
        # obj.save()

        # 3.重定向到数据展示功能
        return redirect('/userlist/')
    return render(request, 'register.html')

register.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
{#    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">#}
{#    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
{#    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>#}
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">注册页面</h1>
<div class="container">
    <div class="row">
        <form action="" method="post">
            <div class="form-group">
                用户名: <input type="text" class="form-control" name="username">
            </div>
            <div class="form-group">
                密码: <input type="password" class="form-control" name="password">
            </div>
            <div class="form-group">
                <input type="submit" class="btn btn-primary btn-block">
            </div>
        </form>
    </div>
</div>
</body>
</html>

用户数据的查询

user_data = models.Userinfo.objects.filter()
print(user_data)

具体使用:

views.py

# 展示数据
def user_list(request):
    '''展示用户数据'''
    # select * from userinfo
    user_data = models.Userinfo.objects.filter(is_delete=False)  # 如果用软删除查询要先过滤筛选
    # user_data = models.Userinfo.objects.filter()
    # user_data = models.Userinfo.objects.filter().all() # 加不加all()都是取所有数据
    # 列表套对象的形式
    # <QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>
    print(user_data)
    print(user_data[0])  # Userinfo object
    return render(request, 'userlist.html', locals())

userlist.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户展示数据</title>
{#    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">#}
{#    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
{#    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>#}
        {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <h1 class="text-center">用户列表</h1>
        <div class="col-md-8 col-offset-2">
            <table class="table table-bordered table-hover">
                <thead>
                    <tr>
                        <td>ID</td>
                        <td>用户名</td>
                        <td>密码</td>
                        <td>操作按钮</td>
                    </tr>
                </thead>
                <tbody>
                    {% for user_obj in user_data %}
                        <tr>
                            <td>{{ user_obj.id }}</td>
                            <td>{{ user_obj.username }}</td>
                            <td>{{ user_obj.password }}</td>
                            <td>
                                <a href="/edit/?edit_id={{ user_obj.id }}" class="btn btn-primary">修改</a>
                                <a href="/del_user/?del_id={{ user_obj.pk }}" class="btn btn-danger delBtn">删除</a>
                            </td>
                        </tr>
                    {% endfor %}
                    
                </tbody>
            </table>
        </div>
    </div>
</div>

<script>
    {#给删除添加事件#}
    $('delBtn').click(function() {
        let res = confilm('你确定要删除吗?')
        if (res) {
        }else{
            {#阻止后续事件执行#}
            return false
        }
    })
</script>
</body>
</html>

用户数据的修改

步骤:

  1. 点击修改按钮,应该跳转一个修改页面,把id值也传输到网址后面 问号传值
<a href="/edit/?edit_id={{ user_obj.id }}" class="btn btn-primary">修改</a>
  1. 后端接收前端GET请求问号后面传来的edit_id值
request.GET.get('edit_id')
  1. 然后拿着这个id值当成查询条件去数据库中查询,得到这条记录的对象

  2. 页面中应该展示出你修改的这条记录的原来的值

render(request, 'edit.html', locals())
  1. 把查询出来的数据对象,分配到模板文件中,展示出来
	设置前端value="{{ edit_data.username }}"
  1. 用户修改数据,提交(POST提交方式),后端接收数据

  2. 用Django中的对象(字段)点方法修改数据库中的数据

方式一:
    models.Userinfo.objects.filter(pk=edit_id).update(username=name, password=pwd)
方式二:
    对象.属性 = 新值
    edit_data.save()
  1. 重定向到数据展示页面

具体使用:
views.py

def edit(request):
    '''修改用户的功能'''
    # 1.接收前端提交过来的id
    edit_id = request.GET.get('edit_id')
    # 2.查询想要修改的数据对象,展示给用户一个默认值
    # edit_data = models.Userinfo.objects.filter(id=edit_id).first()
    # 如果查询的条件的是主键,就可以直接写成pk
    edit_data = models.Userinfo.objects.filter(pk=edit_id).first()
    # print(edit_data)  # Userinfo object

    if request.method == 'POST':
        # 3.接收前端提交过来的数据
        name = request.POST.get('username')
        pwd = request.POST.get('password')

        # 4.修改数据
        # 第一种方式
        # update userinfo set username=username, password=password where id = edit_id;
        models.Userinfo.objects.filter(pk=edit_id).update(username=name, password=pwd)
        print(name)
        print(name)
        print(name)
        # 第二种方式:对象.save()方法
        # 对象.属性 就是记录.字段
        # edit_data.username = name
        # edit_data.password = pwd
        # edit_data.save()
        return redirect('/userlist/')
    return render(request, 'edit.html', locals())

edit.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改用户的页面</title>
{#    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">#}
{#    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
{#    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>#}
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">修改用户的页面</h1>
<div class="container">
    <div class="row">
        <form action="" method="post">
            <div class="form-group">
                用户名: <input type="text" class="form-control" name="username" value="{{ edit_data.username }}">
            </div>
            <div class="form-group">
                密码: <input type="password" class="form-control" name="password" value="{{ edit_data.password }}">
            </div>
            <div class="form-group">
                <input type="submit" class="btn btn-primary btn-block">
            </div>
        </form>
    </div>
</div>
</body>
</html>

</body>
</html>

用户数据的删除

物理删除

步骤:

  1. 点击删除按钮,应该跳转一个删除页面,把id值也传输到网址后面,问号传值
<a href="/del_user/?del_id={{ user_obj.pk }}" class="btn btn-danger">删除</a>
  1. 后端接收前端GET请求问号后面传来删除的del_id值
request.GET.get('del_id')
  1. 删除数据
models.Userinfo.objects.filter(pk=del_id).delete()
  1. 重定向到数据展示页面
删除二次确认:
	可以用事件绑定来确认
	1.给删除的a标签加一个类delBtn
	2.绑定点击事件
	3.判断confilm确认框,用户选择确定还是取消
	4.if判断一下,点击取消,直接return阻止后续事件

软删除

步骤:

  1. 点击删除按钮,应该跳转一个删除页面,把id值也传输到网址后面,问号传值
<a href="/del_user/?del_id={{ user_obj.pk }}" class="btn btn-danger">删除</a>
  1. 后端接收前端GET请求问号后面传来删除的del_id值
request.GET.get('del_id')
  1. 数据要新增一列is_delete
# models.py中添加:
is_delete = models.IntegerField(default=0)
  1. 更新字段的值,把is_delete的原来的0改为1,用True表示

  2. 此时,查询数据就不能用所有了。

软删除,可以防止误删操作

具体使用:

def del_user(request):
    '''删除用户的功能'''
    # 接收前端提交过来的删除数据的id
    del_id = request.GET.get('del_id')
    # 删除方式一:
    # 物理删除,直接从磁盘中把数据删掉了
    # models.Userinfo.objects.filter(pk=del_id).delete()

    # 删除方式二:
    # 软删除
    # 更新字段的值,把is_delete的原来的0改为1
    models.Userinfo.objects.filter(pk=del_id).update(is_delete=True)
    return redirect('/userlist/')

ORM创建表关系

表关系有:

  • 一对一
  • 一对多
  • 多对多

我们以图书表、出版社表、作者表、作者详情表为例。

  1. 作者与作者详情表是一对一的关系
    外键字段建在任何一方都可以,一般建在查询频率较高的表。

  2. 图书和作者是多对多的关系
    外键字段建在第三张表,使用虚拟字段建立第三张表,虚拟字段建在任何一张表中都是可以的,建在查询频率较高的一张表。

  3. 图书和出版社是一对多的关系
    外键字段建在多的一方,书是多,外键字段建在book表中。

用ORM创建以上三种关系:
创建外键关系的时候,先创建一张表中的基础字段,再创建外键字段

model.py代码:

# 图书表
class Book(models.Model):
    title = models.CharField(max_length=64)

    """
        max_digits=None,  总位数
        decimal_places=None 小数后面的位数
    """
    # price decimal(8,2)
    price = models.DecimalField(max_digits=8, decimal_places=2)

    # 如何创建一对多的关系
    # publish_id = models.ForeignKey(to='Publish', to_field='id')  # to_field='id'可以省略
    publish = models.ForeignKey(to='Publish')  # 默认是与Publish表的主键建立关系
    '''如果是外键字段的话,会自动帮你拼接_id,如果你自己写了,它也会给你拼上,所以以后就不需要自己写_id了'''
    
    # 如何创建多对多的关系
    authors = models.ManyToManyField(to='Author')

    '''
        authors字段是一个虚拟字段,意思是不会在book表中创建出来authors字段,它就是用来告诉django给我创建出来第三张表
    '''


# 出版社表
class Publish(models.Model):
    title = models.CharField(max_length=64)
    addr = models.CharField(max_length=255)


# 作者表
class Author(models.Model):
    name = models.CharField(max_length=64)
    
    # 创建一对一关系
    # author_detail_id = models.OneToOneField(to='AuthorDetail', to_field='id')
    # author_detail_id = models.OneToOneField(to=AuthorDetail)  # 如果作者详情表的代码在上面可以不加引号,Django也能找见,但是建议加上

    '''如果是外键字段的话,会自动帮你拼接_id,如果你自己写了,它也会给你拼上'''
    author_detail = models.OneToOneField(to='AuthorDetail')
    '''以后都写在引号里面,防止找不到'''


# 作者详情表
class AuthorDetail(models.Model):
    phone = models.CharField(max_length=64)
    qq = models.CharField(max_length=64)
    wechat = models.CharField(max_length=64)

Django2中

'''在django1中,默认是级联删除级联更新'''
'''在django2中,现在的创建代码就不行了,必须指定一个参数on_delete'''

    # 如何创建一对多的关系
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)  # 指定级联更新,级联删除
    
    # 如何创建多对多的关系
    authors = models.ManyToManyField(to='Author')  # 这个不需要

    # 创建一对一关系
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)  # 指定级联更新,级联删除

on_delete参数

on_delete参数
当删除关联表中的数据时,当前表与其关联的行的行为。

可选值:
models.CASCADE
删除关联数据,与之关联也删除

models.DO_NOTHING
删除关联数据,引发错误IntegrityError

models.PROTECT
删除关联数据,引发错误ProtectedError

models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)

models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)

models.SET
删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
posted @ 2023-04-26 22:42  星空看海  阅读(82)  评论(0编辑  收藏  举报