努力与运动兼备~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!
微信公众号: 啃饼思录
QQ: 2810706745(啃饼小白)
本篇笔记主要是简易留言簿的交互实现,笔记中本篇(第五篇)对应上传的仓库为:https://github.com/licheetools/djangoTest对应第五篇截止代码。好了,我们先来看一下我们上篇笔记的最终效果:
通常而言,为了实现我们数据意义上的增删改查,你可能会选择使用原生的数据库查询语句如:select * from database where id =8;
又比如下面一段代码:
import MySQLdb # 导入数据库驱动模块
def book_list(request): # 使用原生sql获取书的列表
# 创建一个数据库的连接: 指明用户名,数据库名,密码,服务器名
db = MySQLdb.connect(user = 'me', db='mydb', passwd='secret', host='localhost')
# 创建一个游标对象执行器
cursor = db.cursor()
# 书写我们需要的sql语句,可以在Navicat中执行
cursor.execute('SELECT name FROM books ORDER BY name')
# 对于fetchall()的结果做遍历,并将遍历回来的结果当做数组,再取出第0个值name(必须一一对应)。
names = [row[0] for row in cursor.fetchall()]
db.close()
尽管这种方法可以对数据进行操作,但是一旦需要进行操作的数据过多而且不仅仅是查询操作,这种方法就显得很捉襟见肘了。那么有没有一种简便的方法呢?答案是有的!这就用到了我们的ORM了。
***
对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换 。从效果上说,它其实是创建了一个可在编程语言里使用的--“虚拟对象数据库”。
***
你可以借鉴面向对象编程的思想这么理解,把数据当做对象(事实上在面向对象编程的思想里,一切都是对象,别忘了Python也就是面向对象编程语言),那么它就有方法和属性了。举个例子来说:car
,它的属性可以是颜色(color),大小(size),长度(length)等,可以这么表示为
car.color car.size car.length
;它的方法可以是启动(start),运行(run),停车(stop)等,也可以这样表示为:
car.start() car.run() car.stop()
从前面的描述中你就知道了,models.py这个文件就是用于数据库的操作,包括连接,访问,数据定义,修改等,接下来我们就开始定义数据,写我们的代码了。
在空白处输入以下代码:
class UserMessage(models.Model): # 继承于django.db.models.Model
# max_length设置最大长度,verbose_name在后台显示字段会用到,也就是中文显示文本内容
name = models.CharField(max_length=20, verbose_name="昵称")
email = models.EmailField(verbose_name="邮箱")
address = models.CharField(max_length=100 ,verbose_name="联系地址")
message = models.CharField(max_length=500, verbose_name="你的轨迹")
class Meta:
verbose_name = "用户留言信息"
# class Meta,内嵌于 UserMessage 这个类的定义中,主要是用于后台管理显示中文信息
前面说到,一旦数据发现变动就必须执行我们的makemigrations
和migrate
操作,我们运行一下:
为什么会这样呢,怎么一点反应也没有,其实因为你还没有把新创建的apps在setting中注册,所以才会出现这样的情况。
在djangoTest/settings.py 第33行的INSTALLED_APPS:的尾部加上一行代码'liuyan',
如下图所示:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'liuyan',
]
这时候我们再来重新运行Tools 菜单下 Run manage.py Task然后输入makemigrations
或者makemigrations liuyan
,因为这里只有一个app所以我就直接makemigrations
,多个可以根据自己的需要来选择makemigrations
的对象,如下图所示:
migrate
命令:提示生成了一些文件信息,我们打开Navicat去看一下:
看到没,我们的数据信息在数据库成功显示了。默认数据表的名称为app名称_类名(转换为小写),自动生成的id作为主键(数据查询必备,这里系统给我们自己添加了)。
前面我们用到了一些字段如CharField,EmailField等,还有一些这里没用到但是今后会用到的字段,我列举了一下:
models.TextField # 文本注意与CharField的区别,范围大小不一样
models.ForeignKey # 外键
models.DateTimeField # 时间
models.IntegerField # 整型
models.IPAddressField # IP地址
models.FileField # 文件
models.ImageField # 图片
1、CharField必须指明默认的最大长度(max_length)。null=True,blank=True指明字段可以为空,defalut = " "指定默认值为空。通常我们地址这一栏可以为空,所以修改如下:
address = models.CharField(max_length=100 ,null=True,blank=True,verbose_name="联系地址")
2、表的id是自动生成的,如果需要自定义主键,那么需要在models.py中添加字段:
object_id = models.CharField(primary_key=True,max_length=100 ,verbose_name="主键")
再来makemigrations
和migrate
一下:
我们发现出错了,其实是要求我们需要给object_id添加默认值,那我们默认为空:
object_id = models.CharField(primary_key=True,max_length=100,default="" ,verbose_name="主键")
重新运行一下,makemigrations
和migrate
:
我们现在再次刷新一下Navicat,发现系统默认生成的主键id没了,主键变成了我们自定义的object_id。右键liuyan_usermessage这个表,选择设计表打开:
1、在Meta信息中我们可以指定表的名称,如db_table:
db_table = "user_liuyan"
2、可以指定排序的字段,如ordering:
ordering = 'object_id'
这是以其升序的,倒序的话只需要这样ordering = '-object_id'
即可。
3、可以更改后台信息,如verbose_name_plural:
verbose_name_plural是verbose_name的复数形式,如果不改则会在其后面加s。
verbose_name = "用户留言信息"
则verbose_name_plural 会显示 "用户留言信息s",所以一般这2个的值都是相同的
即
verbose_name =verbose_name_plural="用户留言信息"
首先将我们的数据信息导入到我们的视图函数中来,在liuyan/views.py中:写上这行代码,完成导入(同一目录下的包的导入用.表示当前目录):
from .models import UserMessage
既然是要对数据进行操作,必然是先有数据,所以按照图所示,填入数据:
接下来我们完善一下我们的视图函数,修改代码如下:
def getstart(request):
all_message = UserMessage.objects.all()
# 我们这个函数直接继承最高类objects,并且调用它的all()方法,all()方法是将所有数据返回成一个queryset类型(这是django的一个内置的类型)
# 对取出的all_message进行遍历
for message in all_message:
# 每个message其实就是一个UserMessage对象
print(message.name) #打印名字
return render(request, 'start.html')
点击Run -> debug后:在浏览器里打开:
http://127.0.0.1:8000/start/
点击一下,就进入这个页面了
再一次次按图片上的调试按钮,我们的值被一步步的取出来了。
all_message = UserMessage.objects.filter(name="newbai", address="珠海")
我们修改之后再来debug一下,发现依然取到了数据:
我们尝试去一个不存在的数据,看有什么结果:
all_message = UserMessage.objects.filter(name="newbai", address="广州")
看,结果是什么也没取到,因为本来就没有符合条件的数据啊,不是空值才怪!!!
刚才是我们直接在数据库中添加了数据,如果现在我们要求不能直接在数据库中添加数据,这该怎么办呢?我们可以在文件里自己添加数据,django/db/models/base.py 中提供save方法:
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
所以我们在view.py中修改代码如下:(按住Ctrl+/键可以快速注释代码)
def getstart(request):
# all_message = UserMessage.objects.filter(name="newbai", address="广州")
# for message in all_message:
# print(message.name)
user_message = UserMessage() # 首先实例化一个对象
user_message.name = "newbee" # 为对象增加属性
user_message.message = "重新测试一下"
user_message.address = "深圳"
user_message.email = "2131247535@qq.com"
user_message.object_id = "liuyan.top"
user_message.save() # 调用save方法进行保存
return render(request, 'start.html')
我们在实例化对象的地方打上一个断点,开启debug模式:一次次按调试按钮,直到运行到蓝色return render(request, 'start.html')
语句为止,我们的值被一步步的取出来了。
到底数据是否存入到数据库中呢?我们打开Navicat并且刷新一下,看到确实在数据库里面:
那么现在就有一个问题了,我们从页面填入的数据如何保存到数据库中呢?
有过网页知识的小伙伴们就知道网页请求常见有get和post,但是post一般用于表单数据的提交,而get一般用于获取信息。打开我们的start.html,看到第10行,我们发现它的提交方式就是post.
这里我们修改代码如下:
<form action="/start/" method="post" class="smart-green">
action会指向我们在urls.py中配置的/start/,记住前面必须加斜杠,指根路径下的start。里面的input会自动把值传递给后台,这时我们就可以在getstart中取到刚才传递过来的值。method是post。
***
现在我们重新运行一下我们的项目:runserver。输入
http://127.0.0.1/start/
出现了错误,其实这是必然的,为了提高网站的安全性,防止CSRF攻击,django不允许没有进行crsf的验证的信息提交,这是它的一种安全机制。所以我们需要在html页面中加入{% csrf_token %}
,具体都是加在</form>标签的前面。记得把前面那行去掉:
现在我们将view.py文件中的信息都注释掉,只保留这4行代码:
from django.shortcuts import render
from .models import UserMessage
def getstart(request):
return render(request, 'start.html')
再次运行我们的项目,在出现的页面填入信息:
然后在最后一行return render(request, 'start.html')
打上一个断点,开启debug模式,最后再点击提交按钮(顺序很重要,不要弄错!)
可以看到信息出现了,POST里面的数据以dict(字典):key-value 形式存储的!
前面已经看到有数据进入POST里面了,现在是考虑如何从request.POST中取出数据,并存入user_message对象里面。从前面的分析中我们可以知道,数据提交的方式是POST,接下来我们就模拟一下数据的表单的提交。在views.py
文件修改如下:
from django.shortcuts import render
from .models import UserMessage
# Create your views here.
def getstart(request):
if request.method == "POST":
name = request.POST.get('name', '') # 根据dict里面的键值对(key对应value值)来取出相应的属性,取不到默认为空。
message = request.POST.get('message', '')
address = request.POST.get('address', '')
email = request.POST.get('email', '')
user_message = UserMessage() # 实例化对象
user_message.name = name # 将取到的html的数据传入我们实例化的对象(数据库对象).
user_message.message = message
user_message.address = address
user_message.email = email
user_message.object_id = "2333" # 随便写一个即可
user_message.save() # 调用save方法进行保存
return render(request, 'start.html')
然后先开启debug模式,在浏览器中输入http://127.0.0.1/start/,待页面正常显示以后,再在`if request.method == "POST":`左侧打上一个断点,重新刷新一下页面:
我们在这里发现此处我们的Method是GET,也就是说浏览器采用get方式获取数据:
现在我们返回页面,在页面输入一些信息:
然后点击提交,在Pycharm里面,我们每按一下单步运行按钮,注意右侧是否出现绿色的文字,表示已经成功获取到数据。然后继续按单步执行按钮,直到蓝色横栏出现在return render(request, 'start.html')
为止!
这说明,我们数据库的新增数据已经完成了!
有一个问题,那就是有时候你输入信息的时候未来得及检查就提交了,提交了不完整的信息,可是这时候数据库已经把你刚才提交的数据存进去了,所以接下来是如何对其进行删除操作。
正如你所知道的,我们删除只是删除一部分内容,也就是有选择性的删除,那么就要用到前面说过的filter过滤了。在views.py
文件,修改并注释一些代码:
from django.shortcuts import render
from .models import UserMessage
# Create your views here.
def getstart(request):
# if request.method == "POST":
# name = request.POST.get('name', '') # 根据dict里面的键值对(key对应value值)来取出相应的属性,取不到默认为空。
# message = request.POST.get('message', '')
# address = request.POST.get('address', '')
# email = request.POST.get('email', '')
#
# user_message = UserMessage() # 实例化对象
#
# user_message.name = name # 将取到的html的数据传入我们实例化的对象(数据库对象).
# user_message.message = message
# user_message.address = address
# user_message.email = email
# user_message.object_id = "2333" # 随便写一个即可
#
# user_message.save() # 调用save方法进行保存
# filter取出符合指定条件的值,逗号代表and ,必须同时满足两个(这里只设定了2个)条件才返回值,否则为空。
all_message = UserMessage.objects.filter(name='newbai', address='珠海') # 数据库里保存着可以匹配到该条数据的一行。
all_message.delete() # 删除操作:使用delete方法删除all_message
for message in all_message:
# 删除取到的message对象
message.detele()
# print message.name
return render(request, 'start.html')
如下图所示:
然后重新启动运行一下,在浏览器端口输入:
http://127.0.0.1/start/
,刷新一下页面,然后再次打开Navicat,查看一下数据库,我们发现那条数据被删除了!
看到这里,数据库的查询,增加,删除都介绍完了。
我们这里假定已经在数据库中已经存有一些数据(实际上就是前面已经存入的数据),如果存在一个叫newbee的人,就直接将他的数据修改回填到我们的HTML页面上来;如果没有就直接添加这个人的信息。(直接添加数据这种方式在前面就已经介绍完了,这里主要介绍前面一种如何将已经存在的信息进行回填的情况)
打开liuyan/views.py文件,将里面所有的信息都注释或者删除掉,然后写入以下代码:
from django.shortcuts import render
from .models import UserMessage
# Create your views here.
def getstart(request):
message = None # 开始时message是没有数据的
all_messages = UserMessage.objects.filter(name = "newbee") # 过滤查找是否存在我们想要的数据
if all_messages: # 如果存在的话,返回值是一个列表
message = all_messages[0] # 取出返回值的第一个元素
return render(request, 'start.html',{
"my_message": message # 将返回值显示给HTML页面
})
注意:字典里面的"my_message": message
是一个键值对,my_message这个变量可以随意命名,但是后面的message则是你前面返回的值。光这样还是不够的,我们接下来准备挖坑和填坑操作。
打开start.html文件,找到里面的一系列input标签,并添加value属性,我们这里以昵称这个属性为例:
<label>
<span>昵称 :</span>
<input id="name" type="text" name="name" value="" class="error" placeholder="请输入您的昵称"/>
<div class="error-msg"></div>
</label>
并将value的值修改为value="{{ my_message.name }}"
,其他的几个属性也是类似,这里就不细说了:
value="{{ my_message.email }}"
value="{{ my_message.address }}"
需要注意的是textarea这个标签,应该修改如下:
<label>
<span>留言 :</span>
<textarea id="message" name="message" placeholder="请输入你的轨迹">{{ my_message.message }}</textarea>
<div class="error-msg"></div>
</label>
现在我们重新运行一下这个项目,在浏览器地址中输入:http://127.0.0.1/start/,回车看一下:
数据库中存在的数据已经被回填回来了,棒棒的,至此我们这个简易留言簿的开发就到此为止了!
但是为了后面的开发需要,在这里有必要介绍一些Django用于模板渲染的常见方法。
先给大家传送官方关于Built-in template tags and filters的介绍。
如果你有编程背景或者您之前使用过一些在HTML中直接插入程序代码的语言,那么现在你需要记住,Django的模版系统并不是简单的将Python嵌入到HTML中(事实上,Django不允许我们在Template中写Python的语法)。 所以设计决定了模版系统示致力于表达外观,而不是程序逻辑。
模版是纯文本文件。它可以产生任何基于文本的的格式(HTML,XML,CSV等等)。 模版包括在使用时会被值替换掉的变量和控制模版逻辑的标签。
下面是一个小模版,它说明了一些基本的元素。后面的文档中会解释每个元素。
{% extends "base_generic.html" %}
{% block title %}{{ section.title }}{% endblock %}
{% block content %}
<h1>{{ section.title }}</h1>
{% for story in story_list %}
<h2>
<a href="{{ story.get_absolute_url }}">
{{ story.headline|upper }}
</a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}
为什么要使用基于文本的模版,而不是基于XML的(比如Zope的TAL)呢?我们希望Django的模版语言可以用在更多的地方,而不仅仅是XML/HTML模版。在线上世界里,我们在email、Javascript和CSV中使用它。你可以在任何基于文本的格式中使用这个模版语言。
变量看起来就像是这样: {{ variable }}。
点号(.)用来访问变量的属性。从技术上来说,当模版系统遇到点(“.”),它将以这样的顺序查询:
字典查询(Dictionary lookup) 属性或方法查询(Attribute or method lookup)
数字索引查询(Numeric index lookup)
过滤器看起来是这样的:{{ name|lower }}。这将在变量 {{ name }} 被过滤器 lower 过滤后再显示它的值,该过滤器将文本转换成小写。使用管道符号 (|)来应用过滤器。
过滤器参数包含空格的话,必须被引号包起来;例如,使用逗号和空格去连接一个列表中的元素,你需要使用 {{ list|join:”, ” }}。
常用的模版过滤器: default,如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:{{ value|default:"nothing" }}undefined length,返回值的长度。它对字符串和列表都起作用。例如:{{ value|length }}undefined filesizeformat,将该数值格式化为一个 “人类可读的” 文件容量大小 (例如 ‘13 KB’, ‘4.1 MB’, ‘102 bytes’, 等等)。例如:{{ value|filesizeformat }}标签
标签看起来像是这样的: {% tag %}。标签比变量复杂得多:有些用于在输出中创建文本,有些用于控制循环或逻辑,有些用于加载外部信息到模板中供以后的变量使用。
有些标签需要开始标签和结束标签(例如{% tag %} … tag contents … {% endtag %})。
常用的标签:
for if,elif,else
block和extend
要注释模版中一行的部分内容,使用注释语法 {# #}.
例如,这个模版将被渲染为 ‘hello’:{# greeting #}hello
如果想了解更多信息,可以参考这篇文章:Django-模板(模板语言)
在我们这个留言项目中,如果我们在djangoTest/urls.py里面为'start/'添加别名:
原来的路径:
path('start/', getstart)
现在的路径:
path('start/', getstart, name = "get_start")
然后在start.html中修改action地址为下面所示:
<form action="{% url "get_start" %}" method="post" class="smart-green">
这样做的好处就是,如果我们改动urls.py中的'start'不需要再去修改前端代码中url的指向地址。
现在我们在djangoTest/urls.py中对URL的配置进行修改一下:
urlpatterns = [
path('admin/', admin.site.urls),
path('start/', getstart, name="get_start"),
path('startt/', getstart, name="get_start")
然后我们在浏览器中输入:
http://127.0.0.1/startt/
(注意我们之前的地址为
http://127.0.0.1/start/
,一个是start,一个是startt)再次运行一下我们的项目:
这个地址竟然也可以访问我们的页面,还正常访问了,没有报错!!!所以小提示很有用的!
现在我们尝试这么一个问题,我们需要在path路径做一些修改如下:path('start/', admin.site.urls),
。
urlpatterns = [
path('admin/', admin.site.urls),
path('startt/', getstart, name="get_start"), # 新增加的一行,位置插到这里
path('start/', admin.site.urls),
这样运行的话,系统会报错
WARNINGS:
?: (urls.W005) URL namespace 'admin' isn't unique. You may not be able to reverse all URLs in this namespace
所以根路径很重要的,不能随意设置,要保证它的唯一性。
至此,所有Django的基础知识我们就回顾完了,下一篇正式开始在线教育网站的项目开发!
笔记中本篇(第五篇)对应上传的仓库为:https://github.com/licheetools/djangoTest对应第五篇截止代码。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。