零基础使用Django2.0.1打造在线教育网站(五):简易留言簿交互实现

关于博主

努力与运动兼备~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!

                 微信公众号:  啃饼思录
                 QQ: 2810706745(啃饼小白)

写在前面

本篇笔记主要是简易留言簿的交互实现,笔记中本篇(第五篇)对应上传的仓库为:https://github.com/licheetools/djangoTest对应第五篇截止代码。好了,我们先来看一下我们上篇笔记的最终效果:

交互实现

对象关系映射ORM

通常而言,为了实现我们数据意义上的增删改查,你可能会选择使用原生的数据库查询语句如: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(数据库信息)

从前面的描述中你就知道了,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 这个类的定义中,主要是用于后台管理显示中文信息

前面说到,一旦数据发现变动就必须执行我们的makemigrationsmigrate操作,我们运行一下:

为什么会这样呢,怎么一点反应也没有,其实因为你还没有把新创建的apps在setting中注册,所以才会出现这样的情况。

在settings.py中注册我们新创建的apps

在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作为主键(数据查询必备,这里系统给我们自己添加了)。

关于Models的一些说明

前面我们用到了一些字段如CharField,EmailField等,还有一些这里没用到但是今后会用到的字段,我列举了一下:

    models.TextField    # 文本注意与CharField的区别,范围大小不一样
    models.ForeignKey     # 外键
    models.DateTimeField  # 时间
    models.IntegerField   # 整型
    models.IPAddressField # IP地址
    models.FileField      # 文件
    models.ImageField     # 图片
如果你想看全部的信息可以,将光标放在models上面,按住ctrl+然后单击,进入models/--init--.py文件,之后以同样的操作点击任意一个fields单词,就可以打开fields/--init--.py文件了,在最前面就可以看到所有字段:

字段参数的说明

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="主键")

再来makemigrationsmigrate一下:

我们发现出错了,其实是要求我们需要给object_id添加默认值,那我们默认为空:

  object_id = models.CharField(primary_key=True,max_length=100,default="" ,verbose_name="主键")

重新运行一下,makemigrationsmigrate

我们现在再次刷新一下Navicat,发现系统默认生成的主键id没了,主键变成了我们自定义的object_id。右键liuyan_usermessage这个表,选择设计表打开:

Meta的说明

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="用户留言信息"

models的增删改查

首先将我们的数据信息导入到我们的视图函数中来,在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')

断点调试

在view.py文件的for message in all_message的左侧双击(连续单击2次),就出现红点,然后开启debug模式

点击Run -> debug后:在浏览器里打开:

http://127.0.0.1:8000/start/

点击一下,就进入这个页面了

再一次次按图片上的调试按钮,我们的值被一步步的取出来了。

采用filter可以取出特定的值

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并且刷新一下,看到确实在数据库里面:

那么现在就有一个问题了,我们从页面填入的数据如何保存到数据库中呢?

html页面提交数据并存入数据库

有过网页知识的小伙伴们就知道网页请求常见有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')为止!

现在我们打开我们的Navicat,去数据库里看一下我们的数据是否已经存进去了。通过打开,我们发现数据已经进去了:

这说明,我们数据库的新增数据已经完成了!

数据的删除

有一个问题,那就是有时候你输入信息的时候未来得及检查就提交了,提交了不完整的信息,可是这时候数据库已经把你刚才提交的数据存进去了,所以接下来是如何对其进行删除操作。

正如你所知道的,我们删除只是删除一部分内容,也就是有选择性的删除,那么就要用到前面说过的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的介绍。

Django模板语言

如果你有编程背景或者您之前使用过一些在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-模板(模板语言)

URL的别名设置小贴士

在我们这个留言项目中,如果我们在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)再次运行一下我们的项目:

这个地址竟然也可以访问我们的页面,还正常访问了,没有报错!!!所以小提示很有用的!

URL的根路径设置小贴士

现在我们尝试这么一个问题,我们需要在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对应第五篇截止代码。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

WCF客户端运行时架构体系详解[下篇]

当基于某个终结点创建的ChannelFactory<TChannel>被开启的之后,位于服务模型层的客户端运行时框架被成功构建。站在编程的角度看ChannelF...

1816
来自专栏linux、Python学习

案例+解读,来自有道大神的17个常用Linux命令深度解析

命令后带(Mac)标记的,表示该命令在Mac OSX下测试,其它的在Debian下测试。

1566
来自专栏ascii0x03的安全笔记

SEED缓冲区溢出实验笔记

缓冲区溢出实验(Linux 32位) 参考教程与材料:http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software...

4515
来自专栏技术之路

go微服务框架go-micro深度学习(二) 入门例子

    上一篇帖子简单介绍了go-micro的整体框架结构,这一篇主要写go-micro使用方式的例子,中间会穿插一些go-micro的源码,和调用流程图,帮大...

1.7K5
来自专栏我叫刘半仙

手写一个简化版Tomcat

      Tomcat作为Web服务器深受市场欢迎,有必要对其进行深入的研究。在工作中,我们经常会把写好的代码打包放在Tomcat里并启动,然后在浏览器里就能...

4005
来自专栏程序员的碎碎念

ThinkPHP基础知识(二)

今天闲来无事,继续来看我们的tp下一个教程(勉强叫做这个吧)。看前面的博客文章我们知道: ? 那么,我们怎么创建控制器和方法呢? 一、创建控制器和方法 创建控制...

4026
来自专栏Java帮帮-微信公众号-技术文章全总结

SpringBoot中Spring-cache与Redis整合【面试+工作】

也是在整合redis的时候偶然间发现spring-cache的。这也是一个不错的框架,与spring的事务使用类似,只要添加一些注解方法,就可以动态的去操作缓存...

4393
来自专栏程序员互动联盟

【答疑释惑】菜鸟怎么调试简单程序?

首先公布昨天留的课后作业答案的宏定义函数的答案 #define f(a, b, x) a*x+b printf("%d, %dn", f(3,2,1), ...

3455
来自专栏Android先生

Android中极简的js与java的交互库-SimpleJavaJsBridge

最近接触android中js与java交互的东西很多,当然它们之间的交互方式有几种,但是我觉得这几种交互方式都存在一定的不足,这是我决定编写SimpleJava...

1403
来自专栏SDNLAB

基于OpenDaylight和OVSDB搭建VxLAN网络

1 简介 本文主要介绍基于OpenDaylight子项目OVSDB中的southbound组件来搭建VxLAN网络,包括初始环境搭建和southbound Re...

3477

扫码关注云+社区

领取腾讯云代金券