django学习(一)
1.django版本的选择问题
在学习django之前,我们先做一个基本问题的讨论,这个问题是关于django版本的问题。我们进入官网,可以查看django版本的情况。
关于django的版本的问题,现在公司一般用的是1.11.X和2.2.X两种,3.X是2020年8月出来的,所以比较的新,所以我们现在可以忽略3.X的版本学习。等之后过两年左右我们可以转为3.X版本的学习。
1.x 2.x 3.x(目前直接忽略) 我们这次学习使用的是2.2.15的长期支持稳定的版本
1.x和2.x本身差距也不大,我们学习可以建立在之前的1.11.X版本,然后再讲解于2.2.X的区别
公司之前用到的是1.8.X,然后转到1.11.x,然后现在目前的一些项目用到了2.2.X版本。
2.django的安装
pip3 install django==2.2.15
pip3 install django==2.2.15,如果已经安装了其他的版本,无需自己卸载,直接重新的安装,会自动的卸载安装新的。如果是报错了,看看是不是timeout,如果是那么只是网速的波动,重新安装即可,那么就可以了。
验证是否安装成功的方式:
- 终端输入django-admin看看有没有反应,在终端输入django-admin,查看是否有反应,有反应表示的是django已经安装成功了。
- 在终端运行python,然后导入django,就可以查看django的版本。
3.django的基本操作
命令行的操作
你可以先切换到对应的位置(盘),然后在创建项目。
-
创建django项目:django-admin startproject 项目名字
-
创建好了项目之后,我们看一下项目的结构
启动django项目,在启动django项目之前,我们一定要先切换到项目的目录下,然后输入命令python manager.py runserver,默认是本机的ip和端口默认是8000。然后我们在浏览器去访问这个ip和端口。就会出现django的页面。
- python manager.py runserver运行整个项目
创建django应用
- python manager.py startapp 应用名字
django是一款专门用来开发app应用的web框架。但是这里的app和我们手机上的app是不一样的,是有差别的。django框架就类似于一所大学,django的app应用类似于大学里面的各个学院。比如开发淘宝,有订单相关,用户相关,投诉相关等等。创建不同的app应用对应不同的功能。一个app就是一个独立的功能模块。应用名应该做到见名知意,例如user、order、web
创建的应用一定要去配置文件中注册
# Application definition
# 注册的app应用(功能模块)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config', # 全写
'app02', # 简写
]
ps:你在pycharm创建项目的时候,pycharm可以帮你创建一个app应用并且自动注册,但是我们需要注意的是,pycharm最多只是帮你创建和注册一个应用,剩下的应用都需要自己去创建和注册。
整个项目的结构分析:
pycharm操作
创建项目
- new project 选择左侧第二个django即可
启动项目
-
还是用命令行启动。
-
用pycharm的运行的键来启动,点击绿色小箭头即可。
创建应用
-
在终端直接输入python manager.py startapp 应用的名称即可
-
pycharm的tools run manager.py task提示【前期不要用,不介意使用,我们写完整的命令】。ps:你在pycharm创建项目的时候,pycharm可以帮你创建一个app应用并且自动注册,但是我们需要注意的是,pycharm最多只是帮你创建和注册一个应用,剩下的应用都需要自己去创建和注册。
修改端口号
我们先点击如下第一张图的edit configurations,然后我们可以修改一下host的主机ip和端口号port。
创建server
我们先点击如下第一张图的edit configurations,然后我们可以点击‘+’号,添加我们需要的服务,选择django server。如果我们要删除所创建的server,选中所要删除的server,然后再点击‘-’号。
4.命令行与pycharm创建项目的区别
1)命令行创建不会自动有templates文件夹,需要你自己手动的创建templates文件夹。而pycharm会自动帮你创建,并且还会自动在配置文件中配置对应的路径。所以总结来说,如果我们使用命令来创建的时候,我们既要手动的创建templates文件夹并且要在setting文件中配置templates文件夹的路径。[os.path.join(BASE_DIR,'templates')]。
pycharm创建的
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
命令行创建的
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [] # 命令行创建的没有templates文件夹的路径。没有加入os.path里面。需要我们自己写
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
上面的两点我们可以得出来,也就是说用命令创建项目的时候,不单单需要创建templates文件夹,而且还需要我们要手动的添加templates的文件的路径。还需要去配置文件中的配置路径。'DIRS': [os.path.join(BASE_DIR, 'templates')]
2)创建的应用一定要去配置文件中注册
创建出来的应用第一步先去配置文件中注册,其他的先不要做,什么都不要干。因为应用不注册,django是无法识别的,所以我们写的应用就会没有用。
5.django小白必会三板斧
1)HttpResponse返回字符串
def user_test(request):
return HttpResponse('hello')
2)render 返回html页面
def register(request):
return render(request, 'register.html')
3)redirect重定向链接地址,表示可以重定向到任何一个路由链接上。
可以重定向到别人的地址上,也可以重定向到自己的视图上。
return redirect('http://www.baidu.com')
重定向到别人的地址上,需要说明ip和端口号。
return redirect('/home/')
重定向到自己的地址上,不需要说明ip和端口号。
django框架不难,但是django 的学习前期记忆的东西很多,所以我们在学习django的时候,我们应该记忆一部分的东西。当我们记忆熟悉了之后,我们应该学会如何去用django框架。
6.静态文件的配置
我们将html文件默认都放在templates文件夹下。
我们将网站所使用的静态文件默认都放在static文件夹下。静态文件---前端已经写好了的,能够直接调用使用的文件都可以叫做是静态文件。例如网站写好的js文件和css文件,网站用到的图片文件,以及第三方前端框架。拿来就可以直接使用的我们都可以称为静态文件。
django默认是不会为你创建static文件夹的,需要你自己手动的创建。
一般情况下,我们在static文件夹内还会进一步的划分处理---解耦合处理。这样我们可以将我们前端的静态文件放到我们static文件夹下面所对应的目录下面。这样的处理可以进一步的解耦合。
在浏览器中输入url能够看到对应的资源,是因为后端提前开设了该资源的接口。如果浏览器中输入url,无法访问资源,没有看到对应的资源,表示的是后端没有提前开设该资源对应的接口。
static静态文件的配置
static静态文件的配置是我们在setting文件中,添加下面的代码即可:
# 访问静态文件的接口,令牌
STATIC_URL = '/static/' # 类似于访问静态文件的令牌,门牌。我们这里不一定是写的是'/static/',可以写成别的。
"""
如果你想要访问静态文件,你就必须已static开头
"""
"""
静态文件夹的路径的配置,为什么这里是列表呢,因为路径不只是一个,可以有多个静态文件的路径
/static/ 令牌
拿到令牌后,开始去列表里面从上往下依次的查找,如果第一个有,那么就会去第一个查找,
如果第一个没有,那么就开始去第二个查找,
如果第二个有,那么就从第二个查找。
如果第二个没有,那么就开始从第三个查找,依次类推
如果查找到最后一个都没有查找到,那么就会显示报错,无法找到文件404的错误。
"""
# 静态文件的配置
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
os.path.join(BASE_DIR, 'static1'),
os.path.join(BASE_DIR, 'static2')
]
我们知道,我们配置好了静态文件以后,那么我们在我们的前端的页面就可以使用这个static静态文件中的资源。例如:
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css">
<script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
假如我们的项目中有几百个甚至更多个html文件,这个时候我们的项目经理提出了一个请求将令牌改为/statics/
,而不是/static/
,这个时候我们需要更改的是几百个甚至更多个页面,这样的话我们就会很麻烦。当我们辛辛苦苦的花了很多的时间把每一个文件给更改好了,但是我们的项目经理这个时候又来说,不好意思,我们应该将我们的静态文件的请求的令牌的改为/staticstatic/
。这个时候我们有要辛苦的去改。那么有没有一种方法使得我们的结果变得更加的简单,毕竟每次的改动真的太麻烦了,耗时耗力。
这个时候我们提出了一个解决办法就是静态文件的动态解析。例如下面所示:
静态文件的动态解析
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
通过静态文件的动态解析,我们项目经理提出来的上面的请求,我们就会很好的改变,每次就要相应的改变static令牌就可以,省时省力。
当你在写django项目的时候,可能会出现后端代码修改了,但是前端的页面没有变化的情况。
-
你在同一个端口开了好几个django项目,但是一直在跑的其实是第一个django项目。
-
是浏览器的缓存问题。清楚浏览器的缓存setting--network--disable cache 勾选上
-
或者在浏览器的设置选项中,然后选择清除浏览数据,这样的话,我们浏览器的缓存就会撤销。
通过上面的措施,我们基本上就可以实现,后端代码修改了,实现前端页面的变化和修改,我们遇见这个问题的时候,我们就可以通过一下几个方式做就可以了。
7.request对象方法(最基本的三个方法)
form表单默认的提交方式是get方式提交数据,所以我们在写项目登陆页面的时候,我们应该说明请求的方式,只有说明请求的方式,那么form表单才会去这样的请求,否则都认为是get的方式。
http://127.0.0.1:8000/login/?username=zhoquian&password=zhoquian
但是我们在写登陆页面应该改为post请求方式,因为用户名和密码都是隐私的数据,如果用get请求,那么就会将用户名和密码都暴露在get请求的后面。from表单action参数:1)不写,默认朝当前所在的url提交数据。2)全写,指名道姓。3)只写后缀 /login/
当我们用post请求去点击按钮提交的时候,这个时候我们显示forbidden,csrf verification failed,request aborted。
在前期我们使用django提交post请求的时候,需要取配置文件中注释掉一行代码。后期我们在讲解csrf机制的应用。
# django中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',在前期的时候我们需要将这一行代码给注释掉。
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
我们这里讲解一下request对象,由下图所示,我们知道request对象有下面这么多的属性和方法。
1)print(type(request.method))
# 返回请求方式 并且是全大写的字符串的形式 <class 'str'>
,这里我们返回的是‘POST’和‘GET’两种请求方式。
def login(request):
"""
get请求和post请求应该有不同的处理机制
:param request: 请求相关的数据对象 里面有很多简易的方法
:return:
"""
if request.method == 'POST':
username = request.get('username')
password = request.get('password')
if not all((username, password)):
return '请输入用户名和密码!'
return render(request, 'login.html')
2.获取POSt请求方式携带的参数,print(request.POST)
,打印出来的是: <QueryDict: {'username': ['zhoquian'], 'password': ['1'],'hobbies':['游泳','健身','跑步']}>
。这里是字典的情况。键和值:键是我们使用的关键字。值是列表的形式。
request.POST
:获取用户post请求提交的普通数据不包含文件。
request.POST.get()
:只获取列表的最后一个元素,返回的是字符串的类型。获取的是键所对应值中的列表的最后一个元素。
request.POST.getlist()
:获取列表中的所有的元素,返回的是列表的类型,直接列表取出来。获取的是键所对应值中列表的全部元素。
if request.method == 'POST':
# 获取用户数据
print(request.POST) # 获取用户提交的POST请求数据(不包含文件)
username = request.POST.get('username')
password = request.POST.get('password')
print(username,type(username)) # zhouqian <class 'str'>
hobby = request.POST.get('hobby')
print(hobby,type(hobby)) # 跑步 <class 'str'> 健身 <class 'str'>
'''
我们必须知道一个东西是:get只会获取到列表中的是最后一个元素,并且返回的是字符串
'''
return HttpResponse('收到了,宝贝')
# 如果我们想获取的是全部的元素,那么我们就不可以用get了,我们使用的是getlist的方法,并且返回的是列表。
username = request.POST.getlist('username')
password = request.POST.getlist('password')
print(username,type(username)) # ['zhouqian'] <class 'list'>
hobby = request.POST.getlist('hobby') # ['111','222','333'] <class 'list'>
print(hobby,type(hobby))
'''
我们必须知道一个东西是:getlist获取到列表中的是全部元素,并且返回的是列表
'''
3.获取get请求方式携带的参数。----获取url后面携带的参数。http://127.0.0.1:8000/login/?username=zhoquian&password=zhoquian
print(request.GET) # 获取用户get请求提交的普通数据。<QueryDict: {'username': ['zhoquian'], 'password': ['ada'],'hobby':['游泳','健身','跑步']}>
和我们的print(request.post)打印出来的结果是一模一样。
print(request.GET)
print(request.GET.get('hobby'))
print(request.GET.getlist('hobby'))
"""
<QueryDict: {'username': ['zhoquian'], 'password': ['ada'],'hobby':['游泳','健身','跑步']}>
222
['111','222']
"""
在这里我们得出一个结论就是request.POST
和request.GET
得出来的规律是一模一样的,打印出来的规律也是一模一样的。所以我们这里request.GET.get()
方法只获取列表的最后一个元素,返回的是字符串的类型。request.GET.getlist()
:获取列表中的所有的元素,返回的是列表的类型,直接列表取出来。
区别:get请求携带的数据是有大小限制的,大概好像只有4KB左右。而post请求方式携带的数据是没有大小限制的。
8.pycharm链接mysql数据库
三个位置查找数据库相关
- 右上方databases
- 左下方databases
- 配置里面的plugins插件搜索安装
如果再没有,那么卸载掉pycharm重新安装。
pycharm可以充当很多款数据库软件的客户端。我们使用pycharm来链接mysql数据库。需要提前创建好库,并且还要安装数据库文件的driver,数据库文件的驱动。
9.django链接mysql数据库
django默认的数据库是sqllite3,所以我们在这里将默认使用的sqlite3改为mysql数据库。我们去setting配置文件中将我们django默认的sqlite3数据库改为我们现在要使用的mysql5.7数据库。django链接mysql数据库的链接的地址。
1)第一步在配置文件中配置,这里我们要说明ENGINE,NAME,USER,PASSWORD,HOST,PORT,CHARSET。
# mysql数据库的配置,配置的信息如下所示。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'imooc',
'USER': 'root',
'PASSWORD': '$ZHOU04200926yu',
'HOST': '192.168.73.129',
'PORT': 3306,
'CHARSET': 'utf8'
}
}
2.代码申明
django默认用的是mysqldb模块链接MySQL,但是mysqldb模块的兼容性不好,也有很多的bug,所以需要我们手动改为pymysql链接MySQL数据库。你需要告诉django不要用默认的mysqldb,而是要用pymysql。在项目名下的__init__
或者任意的应用名下的__init__
文件下书写以下代码都可以。
import pymysql
pymysql.install_as_MySQLdb() # 看这一段话就觉得是见名知意。将pymysql作为django默认的mysqldb,不再使用django默认用的mysqldb模块链接MySQL数据库。
10.django的ORM的前戏
我们学会了用pycharm和django链接数据库了,然后我们还需要学会怎么样去操作数据库,只有学会如何去操作数据库,我们才会从数据库中得到精髓。我们操作数据库之前,我们要学习一下django的ORM(对象关系映射object relationship model)的真正的含义。
django的orm框架的简介
orm:对象关系映射object relationship model
作用:能够让一个不会用sql语句的小白也能够通过python代码或者是面向对象的代码简单快捷的去操作数据库。
不足之处:封装程度太高,有时候sql语句的效率偏低,需要你自己写sql语句。
models | tables |
---|---|
类 | 表 |
对象 | 记录 |
对象属性 | 记录中某个字段对应的值或者方法 |
操作应用下面的models.py文件
-
先去models.py中书写一个类
# Create your models here. class user(models.Model): # id int primary key auto_increment nou null unique verbose_name 这个关键字参数表示的意思是解释说明该字段的含义/名字。 id = models.AutoField(primary_key=True, verbose_name="用户主键") # username varchar(20) username = models.CharField(max_length=20, verbose_name="用户名") # password int password = models.IntegerField(verbose_name="密码")
-
数据库迁移命令,数据库的迁移命令是两条:
python3 manage.py makemigrations
或者python3 manager.py migrate
,这两条命令必须牢记于心。python3 manage.py makemigrations 将操作记录记录到小本本上(或者说django框架的ORM框架将操作的记录在migrations文件夹)
class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='user', fields=[ ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='用户主键')), ('username', models.CharField(max_length=20, verbose_name='用户名')), ('password', models.IntegerField(verbose_name='密码')), ], ), ]
python3 manage.py migrate 将操作真正的同步到数据库中
只要你修改了models.py中跟数据库相关的代码,就必须重新执行上述的两条命令。
# 我们再次创建了一个Author模型类
class Author(models.Model):
"""
由于一张表中必须要有一个主键字段,并且一般的情况下都叫id字段。
所以orm当你不再定义主键字段的时候 orm会自动的帮你创建一个名为id的主键字段
也就是意味着后续我们在创建模型类的时候,如果主键字段没有额外的叫法,那么主键字段可以省略不写
"""
# CharField必须要指定max_length参数,不指定会直接报错。
# verbose_name 该参数是所有字段都有的,就是用来对字段的解释。
# username varchar(32)
username = models.CharField(max_length=32, verbose_name="用户名")
# password int
password = models.IntegerField(verbose_name="密码")
我们修改了models.py文件,所以我们必须重新的执行上面两条数据库的迁移命令。记住:这里再次的强调一遍,只要是我们修改了models.py中跟数据库相关的代码,我们就必须重新的执行上述的两条命令,重新生成数据库的迁移文件,重新将数据操作同步到数据库中。
11.字段的更、删、改、查
我们的models文件已经定义好原来的类,但是我们想在表中增加一个字段的话,那么我们必须在类中写。例如我们增加了info=mdoels.CharField(max_length=32,verbose_name='个人简介')
。原来的表中已经有数据了,这个时候我们如果要在页面增加字段,那么我们原来的数据表中增加了一个字段,那么这个字段该怎么处理呢?
字段增加的三种处理方式:
-
可以在终端内直接给出默认值
-
可以在新的字段默认设置为空,该字段可以为空。
info=models.CharField(max_length=32,verbose_name='个人简介',null=True)
-
可以直接给字段设置默认值
hobby=models.CharField(max_length=32,verbose_name='兴趣爱好',default='study')
字段的修改:
直接修改代码然后执行数据库迁移的两条命令即可!
字段的删除:
直接注释对应的字段然后执行数据库迁移的两条命令即可!执行完毕之后字段对应的数据也都没有了。所以字段的删除一定一定要谨慎。
字段的查询:
直接点击models页面,查看对应类的字段即可!这样就可以查看到自己想查的字段。
在操作models.py的时候一定要细心,千万不要注释一些字段的代码,执行迁移命令之前最好先检查一下自己写的代码。
个人建议:当你离开你的计算机之后,一定要锁屏。
12.数据的查询
1)查询数据库数据的操作
user=models.User.objects.filter(username=username)
这个user
是一个QuerySet:<QuerySet [<user: user object (1)>, <user: user object (3)>]>
。QuerySet我们可以理解为是列表里面套对象。
res=models.User.objects.filter(username=username) # <QuerySet [<user: user object (1)>, <user: user object (3)>]>
"""
返回值你先看成是列表中套数据对象的格式
它也支持索引取值 切片操作 但是不支持负数索引
但是django是不会推荐你使用索引的方式和切片的方式取值
django推荐我们使用的是:user_obj=models.User.objects.filter(username=username).first()
这个类似于数据库的sql语句是:select * from User where username = username;
"""
我们这里讲解一下filter()的用户,filter相当于sql语句中的where关键字,表示筛选,在什么条件下。filter括号内的参数可以携带一个或者多个参数,参数和参数之间默认是and关系。
user_obj=models.User.objects.filter(username=username,password=password).first()这个相当于sql语句的操作为:select * from User where username=username and password=password limit 1;
这里还有一个数据库的查询操作:
user=models.User.objects.filter()
和user=models.User.objects.all()
是一样的,返回的都是一个QuerySet
。列表中套数据对象的数据类型。
13.数据的增加
数据的增加有两种的方式,这两种方式都可以在数据库中增加数据:
from app01 import models
# 第一种:调用的是objects.create()的方法
res = models.User.objects.create(username=username,password=password) #返回值就是当前被创建的对象本身
# 第二种:调用的是对象.save()方法
user_obj = models.User(username=username,password=password) # 创建User()对象
user_obj.save() # 保存数据
14.数据展示
我们先讲将数据库中的数据全部展示到前端,然后给每一个数据两个按钮,一个编辑按钮,一个删除按钮。
# user_list.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h3 class="text-center">数据展示</h3>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table table-striped table-hover">
<thead>
<tr>
<td>ID</td>
<td>USERNAME</td>
<td>PASSWORD</td>
<td>ACTION</td>
</tr>
</thead>
<tbody>
{% for foo in user_queryset %}
<tr>
<td>{{ foo.id }}</td>
<td>{{ foo.username }}</td>
<td>{{ foo.password }}</td>
<td>
<a href="/user_edit/?user_id={{ foo.id }}" class="btn btn-primary btn-xs">编辑</a>
<a href="/user_delete/?user_id={{ foo.id }}" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
def user_list(request):
# 来到这个页面我们就把这个页面给显示出来
user_queryset = models.user.objects.all()
return render(request, 'user_list.html', {'user_queryset': user_queryset})
path('user_list/', views.user_list),
15.数据编辑
点击编辑按钮朝后端发送编辑数据的请求。但是我们的编辑按钮怎么会知道我们应该编辑的是哪一个用户的信息呢?每一个编辑按钮都是一样的,编辑按钮怎么识别要编辑哪一个用户的信息呢?
"""
如何告诉后端用户想要编辑哪条数据?
将编辑按钮所在的那一行数据的主键值发送给后端。
利用url携带参数的方式
"""
后端根据主键查询出用户想要编辑的数据对象,展示到前端页面提供给用户查看和编辑。
def user_edit(request):
user_id = request.GET.get('user_id')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user_obj = models.user.objects.filter(id=user_id).update(username=username, password=password)
# 将filter查询出来的所有数据对象全部更新,批量更新。
return redirect('/user_list')
user_obj = models.user.objects.filter(id=user_id).first()
return render(request, 'user_edit.html', {'user_obj': user_obj})
或者用下面的方式来实现数据的编辑:
def user_edit(request):
user_id = request.GET.get('user_id')
user_obj = models.user.objects.filter(id=user_id).first()
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user_obj.username = username
user_obj.password = password
user_obj.save()
# 这个是单个数据对象的更新
# 这个方法字段特别多的时候效率会非常的低
# 从头到尾将数据对象的所有字段更新一遍,无论该字段是否被修改。
return redirect('/user_list')
return render(request, 'user_edit.html', {'user_obj': user_obj})
path('user_edit/', views.user_edit),
16.数据删除
我们删除也是一样的,点击删除按钮,我们必须知道我们删除的是哪一个用户。我们删除的话,可以将删除按钮所在的那一行的主键值传递给后端,可以利用url携带参数的方式传递给后端。
# 我们后端的删除功能的代码如下
def user_delete(request):
user_id = request.GET.get('user_id')
user_obj = models.user.objects.filter(id=user_id).delete()
# 这里的删除也是批量的删除
return redirect('/user_list')
path('user_delete/', views.user_delete),
Note:1.真正的删除功能应该是需要二次确认,因为删除数据是一个谨慎的操作,所以我们在删除的时候,我们需要二次确认的过程。
2.删除数据的内部其实不是真正的删除,因为数据很值钱,不会轻易的删除掉数据。我们会给数据添加一个标识字段用来表示当前数据是否被删除了,如果是删除了,我们仅仅只是将字段修改一个转态的值。
如下所示的情况一样,我们将is_delete作为删除的标志位:
username password is_delete
zhouqian zhouqian 1
AndreasZhou AndreasZhou 0
17.orm创建表关系
我们知道表的关系有三种的基本转态,这三种基本转态分别是多对一,多对多,一对一三种关系。那么这三种关系在models.py文件中怎么写呢?我们的orm(对象关系映射)该怎么创建表之间的关系呢?我们以下面的表关系为例子,来实现orm如何创建表之间的关系的。
在创建表关系之前,我们要做的第一步就是先将基表创建出来,然后再添加外键的字段。基表的创建如下所示:
class Book(models.Model):
# 如果我们的主键是id,那么我们可以自己不用创建id主键,让django默认给我们创建id主键
title = models.CharField(max_length=32,verbose_name='书名')
# 这个表示的是创建了一个小数,总位数有8位,小数点后面占两位。
price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name='价格')
# 建立一对多的关系
'''
图书和出版社是一对多的关系,并且书是多的一方 所以外键字段放在书表里面
'''
publish = models.ForeignKey(to='publish',on_delete=models.CASCADE)
'''
如果字段对应的是ForeignKey,那么orm会自动在字段的后面加_id,如果你自作聪明在字段后面加了_id,那么orm框架还是会在后面加上_id。在定义ForeignKey的时候,我们就不要自己去加_id。
'''
# 建立多对多的关系
'''
图书和作者是多对多的关系,外键字段放在任意一方均可,但是推荐你建在查询频率较高的一方
'''
author = models.ManyToManyField(to='Author')
'''
多对多的author字段是是虚拟的字段,主要是用来告诉orm,书籍表和作者表是多对多的关系
让orm自动帮你创建第三章表。
'''
class Publish(models.Model):
# 这里的id默认让django帮我们创建,所以我们在使用的时候,就可以不用自己创建id主键字段
name = models.CharField(max_length=32,verbose_name='出版社名')
add = models.CharField(max_length=32,Verbose_name='出版社地址')
class Author(models.Model):
name = models.CharField(max_length=32,verbose_name='作者名')
age = models.IntegerField(verbose_name='作者年龄')
# 建立一对一的关系
'''
作者和作者详情是一对一的关系,外键字段建在任意一方都是可以的,但是推荐你建在查询频率较高的一方中
'''
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
'''
OneToOneField也会自动给字段加_id后缀,所以你也不要自作聪明的自己加上_id。
'''
class AuthorDetail(models.Model):
phone = models.IntegerField(verbose_name='作者手机号码') # 手机号也可以直接是CharField
addr = models.CharField(max_length=32,verbose_name='作者地址')
在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错:
TypeError: **init**() missing 1 required positional argument: ‘on_delete’
举例说明:
user=models.OneToOneField(User)
owner=models.ForeignKey(UserProfile)
需要改成:
user=models.OneToOneField(User,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
参数说明:
on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值
CASCADE:此值设置,是级联删除。
PROTECT:此值设置,是会报完整性错误。
SET_NULL:此值设置,会把外键设置为null,前提是允许为null。
SET_DEFAULT:此值设置,会把设置为外键的默认值。
SET():此值设置,会调用外面的值,可以是一个函数。
一般情况下使用CASCADE就可以了。
我们修改了models文件,那么这样子我们必须重新执行以下数据库迁移文件的命令,python manager.py makemigrations和python manager.py migrate。
18.django请求生命周期流程图
19.路由匹配
django1.11.11的路由匹配的解释如下所示:
url(r'test',views.test)
url(r'testadd',views.testadd)
'''
url方法第一个参数是正则表达式
只要第一个参数正则表达式能够匹配到内容,那么就会立刻停止往下匹配,直接执行对应的视图函数。
你在输入url的时候会默认加斜杠。django内部帮你做到重定向,一次匹配不行,url后面会加斜杠再来一次。
'''
# 取消自动加斜杠 默认情况下下面这个参数是True,默认自动添加斜杠。我们了解一下即可。
# APPEND_SLASH = False
django2.2.15的路由匹配如下所示:
urlpatterns = [
# django2.2.15 与 django1.11.11在视图函数上是有差别的
# django2.2.15的path是默认加了r防止字符转义,^正则匹配以什么开始,和$正则匹配以什么结束。
# 而django1.11.11版本是没有的,url是需要自己添加r、^、$符号的。
# 写我们自己的路由与视图函数对应的关系
path('', views.index),
path('index/', views.index),
path('home/', views.home),
path('login/', views.login),
path('register/', views.register),
path('user/', views.user_test),
path('user_list/', views.user_list),
path('user_edit/', views.user_edit),
path('user_delete/', views.user_delete),
path('admin/', admin.site.urls),
]
20.无名分组和有名分组
1)无名分组
'''
分组:就是给某一段正则表达式用小括号扩起来
'''
django1.11.11的无名分组怎么写?
# 无名分组
url(r'^test/(\d+)/$',views.test)
def test(request,test):
print(test)
return HttpResponse('test')
django2.2.15的无名分组怎么写呢?
# 无名分组
re_path(r'test/(\d+)/', views.test)
def test(request,test):
print(test)
return HttpResponse('test')
无名分组就是将括号内正则表达式匹配到的内容当做位置参数传递给后面的视图函数。
2)有名分组
django1.11.11有名分组怎么写呢?
url(r'^test/(?p<year>\d+)/',views.test)
def test(request, year):
print(year)
return HttpResponse('test!')
django2.2.15有名分组怎么写呢?
# 有名分组
re_path(r'test/(?P<year>[0-9]{4})/', views.test),
def test(request, year):
print(year)
return HttpResponse('test!')
有名分组就是将括号内正则表达式匹配到的内容当做关键字参数传递给后面的视图函数。
3)无名分组和有名分组是否可以混合使用
我们直接给出答案:我们的无名分组和有名分组是不能混合使用的。
django1.11.11
url(r'^index/(\d+)/(?P<year>\d+)/', views.index)
django2.2.15
re_path(r'test2/(\d+)/(?P<year>[0-9]{4})/', views.test2)
那么有名和无名分组是否可以混合使用呢?在这里我们就不啰嗦了,直接给出答案,有名和无名分组是不能混合使用的。
4)单个分组是否可以使用多次
我们这里也直接给出答案,单个分组可以多次的使用。我们看下面的代码即可。
# django1.11.11单个无名分组可以使用多次
url(r'^index/(\d+)/(\d+)/(\d+)/', views.index)
# django2.2.15单个无名分组可以使用多次
re_path(r'test3/(\d+)/(\d+)/(\d+)/', views.test3)
# django1.11.11单个有名分组可以使用多次
url(r'^index/(?P<year>\d+)/(?P<year>\d+)/(?P<year>\d+)/',views.index)
# django2.2.15单个有名分组可以使用多次
re_path(r'test4/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{3})', views.test4),