单表记录CURD
用pycharm创建一个名为demo的django项目,并在该项目中创建一个名为app01的应用.
记得改模版的路径设置 'DIRS': [os.path.join(BASE_DIR, 'templates')],
暂且把中间件的csrf验证给注释掉!不然post请求会报错.
该demo使用Django自带的sqlite小型数据库..测试用,生产环境中不会用!
写路由 -- 写视图函数 -- 渲染模版
# 模版语法分配变量
一般都会使用locals(),传递函数的命名空间字典给模版!!
关键代码如下:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^a_render/', views.a_render),
]
def a_render(request):
user_dic = {'name': 'egon', 'age': 19}
user_dic1 = {'name': 'dc', 'age': 20}
# -- 方式一
# 注意点!k-v,在模版里用的是k!!
# return render(request,'index.html',{'user_dic':user_dic,'user_dic1':user_dic1})
# -- 方式二(推荐!) ★ request和locals()都会传到模版中!!
return render(request, 'index.html', locals())
<body>
<h1>index</h1>
{# 虽然模版接受视图函数传递过来的user_dic和user_dic1是字典 #}
{# 但模版里不支持字典的[]和get的取值操作,用的是.语法 #}
{{ user_dic }}
{{ user_dic.name }}
<br>
{{ user_dic1 }}
{{ user_dic1.name }}
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 增加数据
注册功能实现
需求: 注册页面输入用户名和密码点击提交按钮后,自动跳转到登陆页面,并在user表中添加一条数据!!
models.py
若一张表的主键字段叫做id,那么可以省略不写,Django会自动帮忙创建一个字段名为id的主键!!
但表的主键字段名不叫id,那么就必须自己手动写啦!!
记得执行数据库迁移的两条命令!
from django.db import models
class User(models.Model):
# id = models.AutoField(primary_key=True)
name = models.CharField(max_length=64)
pwd = models.CharField(max_length=64, null=True)
2
3
4
5
6
demo.urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^a_render/', views.a_render),
url(r'^reg/', views.register), # -- 注册
url(r'^login/', views.login), # -- 登陆
]
2
3
4
5
6
7
8
9
10
11
app01.views.py
★ 增加数据有两种方式:
1> 方式一(推荐)
models.User.objects.create(name=username, pwd=password)
-- 该行代码是有返回值,返回的是影响的行数!!
2> 方式二(很少使用)
user_obj = models.User(name=username,pwd=password)
得到User类的实例对象,没有操作数据库.
user_obj.save()
该行代码才开始操作数据库,将记录添加到表中!!
from django.shortcuts import render,redirect
from app01 import models
def a_render(request):
user_dic = {'name': 'egon', 'age': 19}
user_dic1 = {'name': 'dc', 'age': 20}
return render(request, 'index.html', locals())
def register(request):
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
# -- 入库操作 在表中增加一条数据
# models.User ==> 指定表名; objects ==> ORM里的一个组件
models.User.objects.create(name=username, pwd=password)
# -- 跳转到登陆页面
return redirect('/login/')
# -- GET 渲染页面
return render(request, 'reg.html')
def login(request):
return render(request, 'login.html')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
reg.html 和 login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<title>Title</title>
</head>
<body>
<h1 class="text-center">注册页面</h1>
<div class="container-fluid">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<form action="" method="post">
<p>username: <input type="text" name="username" class="form-control"></p>
<p>password: <input type="text" name="password" class="form-control"></p>
<input type="submit" class="btn btn-success btn-block">
</form>
</div>
</div>
</div>
</body>
</html>
<body>
<h1>登陆页面</h1>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 查询数据
这里仅限于单表查询!!
需求: 浏览器输入网址
http://127.0.0.1:8000/userlist/
, 展示出所有用户的列表!!
进阶需求 -- 只展示满足用户名为egon并且密码为123的数据!!
相较于前面,该 models.py 文件多了__str__方法.. 但不用进行数据迁移.
from django.db import models
class User(models.Model):
name = models.CharField(max_length=64)
pwd = models.CharField(max_length=64, null=True)
def __str__(self):
return self.name
2
3
4
5
6
7
8
demo.urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^a_render/', views.a_render),
url(r'^reg/', views.register), # -- 注册
url(r'^login/', views.login), # -- 登陆
url(r'^userlist/', views.user_show), # -- 用户列表
]
2
3
4
5
6
7
8
9
10
11
12
app01.views.py
在该文件里添加user_show函数用于展示用户!
★ 查询所有数据有两种方式:
1> 使用filter方法 filter方法里还可以写过滤条件!不写就是得到所有数据.
2> 使用all方法
注意,上述两个方法得到的都是QuerySet对象,它本质是一个列表,包含一堆 实例对象/表记录 !!!
拿到QuerySet对象的第一条数据,可调用 first方法 ,本质其实就是取QuerySet列表对象中下标为0的元素!!
# -- 展示所有数据
def user_show(request):
# -- 查询所有数据
# filter方法和all方法都会返回一个QuerySet对象!可以简单看作是一个列表!
# user_list = models.User.objects.filter()
user_list = models.User.objects.all()
# <QuerySet [<User: egon>, <User: dc>, <User: ly>]>
# -- 若models.py中的User类没写__str__方法,打印结果是这样的:
# <QuerySet [<User: User object>, <User: User object>, <User: User object>]>
print(user_list)
return render(request, 'user_list.html', locals())
# -- 这里查询所有数据有两种方式,两者皆可. 相当于执行sql语句: select * from app01_user;
# 1.使用filter方法
# 2.使用all方法(推荐)
# -- 可通过models.User.objects.all().first()取出一条数据.
# 该操作等同于models.User.objects.all()[0].
# ★ 注意!!拿到的是 一个实例对象/一条表记录 , 该数据不是可迭代对象,不能循环!!
# 所以传递给对应的模版,在模版里对该数据进行for循环会报错!!
### --- ### --- ### --- ### --- ### --- ### --- ###
# -- 展示符合条件的数据!!
def user_show(request):
# -- filter里面的多个条件用 ","连接 表明是and的关系
# user_list = models.User.objects.filter(name='egon', pwd=123)
# -- 链式查询,只要调用方法的返回结果是QuerySet对象,就可以继续调用objects组件中的任一方法!!
print(models.User.objects.filter(name='egon')) # <QuerySet [<User: egon>]>
user_list = models.User.objects.filter(name='egon').filter(pwd=123)
return render(request, 'user_list.html', locals())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
user_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<title>Title</title>
</head>
<body>
<h1 class="text-center">用户列表</h1>
<div class="container-fluid">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>username</th>
<th>password</th>
</tr>
</thead>
<tbody>
{% for obj in user_list %}
<tr>
<td>{{ obj.id }}</td>
<td>{{ obj.name }}</td>
<td>{{ obj.pwd }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 修改数据
需求: 看edit_user函数的执行过程就晓得啦,不再过多陈述..
点击修改,携带当前用户的ID到修改表单页面,表单的action为空,修改后提交到依旧是当前表单页面网址
So,get请求到修改表单页面的地址和修改表单post提交的地址指向的都是同一个视图函数!!
该地址中携带着被操作用户的ID!!
user_list.html
对该页面添加一点代码.
<body>
<h1 class="text-center">用户列表</h1>
<div class="container-fluid">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>username</th>
<th>password</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for obj in user_list %}
<tr>
<td>{{ obj.id }}</td>
<td>{{ obj.name }}</td>
<td>{{ obj.pwd }}</td>
<td>
<a href="/edit_user/?id={{ obj.id }}" class="btn btn-primary">修改</a>
<a href="/delete_user/?id={{ obj.id }}" class="btn btn-danger">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
右键检查网页,可以看到修改、删除按钮都指向某个路由!!
通过get方式传递给路由唯一的id值,就知道对表中的哪条数据进行操作!!
demo.urls.py
urlpatterns = [
# ... ...
url(r'^edit_user/', views.edit_user), # -- 修改
]
2
3
4
app01.views.py
在该文件里添加edit_user函数用于修改用户信息!
★ 在用户列表界面点击某一条用户数据的修改按钮.. 会跳转到某一网址.
比如修改ID为3的数据,会跳转到 http://127.0.0.1:8000/edit_user/?id=3
.
先 以get请求的方式加载修改页面edit.html ,并将原本的数据加载到页面的修改表单中..
修改完成后,点击提交按钮,因为form表单中的action设置为空, 所以提交到的网址 依旧是
http://127.0.0.1:8000/edit_user/?id=3
. 但 此次提交的方式是post !!
可以debug打印出此次修改表单提交的request的值为 <WSGIRequest: POST '/edit_user/?id=3'>
可以通过request.GET.get取出提交网址?
后面的键值对!!
★ 修改数据有两种方式(一般都会用第一种!!):
1> models.User.objects.filter(id=edit_id).update(name=username, pwd=password)
2> user_obj = models.User.objects.filter(id=edit_id).first()
user_obj.name = username
user_obj.pwd = password
user_obj.save()
-- 执行改行代码才会操作数据库!!
def user_show(request):
user_list = models.User.objects.all()
return render(request, 'user_list.html', locals())
def edit_user(request):
# print(request)
edit_id = request.GET.get('id')
# -- 主键本身就是索引,查询起来会很快!!
user_obj = models.User.objects.filter(id=edit_id).first()
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
# -- update方法修改数据!!
# update user set name=username,pwd=password where id=edit_id;
models.User.objects.filter(id=edit_id).update(name=username, pwd=password)
# -- 修改成功后,跳转回用户列表界面
return redirect('/userlist/')
return render(request, 'edit.html', locals())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<title>Title</title>
</head>
<body>
<h1 class="text-center">修改页面</h1>
<div class="container-fluid">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<form action="" method="post">
<p>username:
<input type="text" name="username" class="form-control" \
value="{{ user_obj.name }}">
</p>
<p>password:
<input type="text" name="password" class="form-control" \
value="{{ user_obj.pwd }}">
</p>
<input type="submit" class="btn btn-success btn-block">
</form>
</div>
</div>
</div>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 删除数据
app01.views.py
在该文件里添加del_user函数用于删除用户信息!
★ 有两种方式, 物理删除 和 软删除 !!
物理删除 -- 不要这样做!!会直接删除在硬盘中对应的数据!
def delete_user(request):
# -- 要删除的数据记录ID
del_id = request.GET.get('id')
# -- sql: delete from user where id=del_id
models.User.objects.filter(id=del_id).delete()
return redirect('/userlist/')
2
3
4
5
6
软删除
1> 在app01.models.py的User类中添加一个is_delete字段!!
2> 给view.py的user_show函数中的查询语句加一个过滤!!让其只展示is_delete为False的字段!
3> 在del_user函数将需要删除用户的is_delete字段的值设置为True.
from django.db import models
class User(models.Model):
name = models.CharField(max_length=64)
pwd = models.CharField(max_length=64, null=True)
# -- 存0和1 ==> 0:False;1:True 默认为0代表不删除
# 或者直接设置is_delete的字段类型为IntegerField
is_delete = models.BooleanField(default=0)
def __str__(self):
return self.name
### --- ### --- ### --- ### --- ### --- ### --- ###
urlpatterns = [
# ... ...
url(r'^delete_user/', views.delete_user), # -- 删除
]
### --- ### --- ### --- ### --- ### --- ### --- ###
def user_show(request):
# -- 这里写is_delete=0也可以,因为True和False在数据库里对应存的是1和0.
user_list = models.User.objects.filter(is_delete=False)
return render(request, 'user_list.html', locals())
def delete_user(request):
# -- 要删除的数据记录ID
del_id = request.GET.get('id')
# -- 软删除!给user表添加一个is_delete字段!!
# 因为设置的is_delete字段是BooleanField,所以这里is_delete=True会存1
# 当然也可以直接is_delete=1
models.User.objects.filter(id=del_id).update(is_delete=True)
return redirect('/userlist/')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 创建表关系
一对多/多对一
publish = models.ForeignKey(to='Publish')
多对多authors = models.ManyToManyField(to='Author')
一对一detail = models.OneToOneField(to='AuthorDetail')
现有四张表,分析表与表之间的关系!
book 书籍表
id title price "publish_id"
publish 出版社表
id title addr
author 作者表
id name "detail_id"
author_detail 作者详情表
id phone email
★ 分析表与表之间的关系:
book:publish = n:1 # -- 我们规定一本书只能由一个出版社出版,不考虑联合出版的情况
book:author = n:n # -- 一本书可以有多个作者,一个作者可以写多本书
author:author_detail = 1:1 # -- 为了模拟一对一的关系设计成了两张表.两表是可以合二为一的.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
models.py
注意:
建立外键关系的ForeignKey和OneToOneField所在的字段,会在数据表中为该字段后面自动加上 _id
!!!
ManyToManyField所在的字段,是一个虚拟字段!!
from django.db import models
class User(models.Model):
name = models.CharField(max_length=64)
pwd = models.CharField(max_length=64, null=True)
is_delete = models.BooleanField(default=0)
def __str__(self):
return self.name
class Meta:
db_table = "user" # 设置这个后,该表在数据库里的表名就不是app名_表名小写了. 是"user". 其余的该咋还是咋.
# Ps:Meta还可以做联合索引,联合唯一索引等.
class Book(models.Model):
title = models.CharField(max_length=32)
# -- 相当于price decimal(8,2)
price = models.DecimalField(max_digits=8, decimal_places=2)
# id = models.BigAutoField(primary_key=True) # 主动设置自增的主键字段id
# 若没有手动设置主健字段..Django就会自动创建自增的主键ID字段id..
# Django是基于app01.apps.App01Config类中的这行代码创建的 default_auto_field = 'django.db.models.BigAutoField'
# -- 建立多对一关系
# -- Book表中的publish_id字段跟Publish表的默认主键id关联
# 若Publish表的主键不是id,是uuid,那么需要指定to_field的值
# publish = models.ForeignKey(to='Publish',to_field='uuid')
# 即默认to_field = "id" 这里表明与Publish表的主键字段进行关联.(表+主键) 当然可以设置to_field跟表的其它字段关联.
# -- 没必要写成publish_id,生成后会自动加上_id的后缀!
# -- Ps:可以写成to=Publish,但这样的话Publish类必须在Book类前面.
"""
on_delete字段表明删除Publish表中的数据后,与之关联的Book表数据如何处理.
publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE) # 级联删除
publish = models.ForeignKey(to="Publish", on_delete=models.SET_NULL, null=True, blank=True) # 设置为Null
publish = models.ForeignKey(to="Publish", on_delete=models.SET_DEFAULT, default=2) # 设置为默认值
"""
# 若数据不敏感的话,就直接级联删除
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
# -- 建立多对多关系 建在业务中查询多的这方
# Django会自动帮我们创建第三张表!!
# -- 注意!!authors是一个虚拟字段,在迁移时,Book表中不会生成authors字段!
# 它的存在就是为了操作第三张表!!
# 若第三张表有其它字段,那么得多对多的配置的手动生成第三张表,因为通过ManyToManyField创建的第三张表不能扩展字段.
authors = models.ManyToManyField(to='Author')
class Publish(models.Model):
title = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class AuthorDetail(models.Model):
# -- 建立一对一关系
# -- 没必要写成detail_id,生成后会自动加上_id的后缀! detail_id --> bigint
"""
OneToOneField本质上是ForeignKey来创建的
author = models.ForeignKey(to='Author', on_delete=models.CASCADE, unique=True)
"""
author = models.OneToOneField(to='Author', on_delete=models.CASCADE)
# -- 开发中,手机号是不会存整型的,要考虑手机号是0开头呢?存整型0不就没了嘛!
phone = models.CharField(max_length=32)
# -- EmailField本质存的是 email varchar(254) 不是邮箱类型也可以存进来
# EmailField在models里不会验证
# 但后面涉及forms组件验证的时候,碰到EmailField必须得符合邮箱格式!
email = models.EmailField()
"""
一对一的关系可以用用户注册和用户注册成功后完善自己的信息对应的用户表和用户详情表来构建场景感.
Author表有name字段; 用户详情表有外键字段author
因为用户注册时是没有额外信息的.
- 若没有详情表,将额外的用户信息字段加到Author表里,那么这些额外的字段都得设置成blank=True,null=True.
- 若有详情表,但将外键字段设置在Author表中,那么该外键字段得设置blank=True,null=True.
- 若有详情表,将外键字段设置在详情表中,那么该外键字段是无需设置blank=True,null=True的!!
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
执行数据库的两条命令之后,查看数据库中包含如下这些表:
# Django请求生命周期
Ps: 大致如图所示,细节方面还有缓存层等.
1> 首先浏览器/客户端发送请求到socket层
2> WSGI (web服务网关接口), 指的就是wsgiref模块,该模块的底层实现了socket!!
当有 请求/数据包 来的时候,WSGI可以解析封装数据!
3> 接着到中间件层,需要 依次经过 该层的7个默认中间件..
4> 接着到达路由层,它将进行路由匹配,将路由匹配到对应的函数..
5> 接着到达视图函数层,执行路由匹配到的函数..
在执行过程中,需要模版就去模版层,需要数据就通过数据库要数据.
6> 接着原路返回!!