美文网首页零基础使用Django2.0.1打造在线教育网站python3Web
零基础使用Django2.0.1打造在线教育网站(五):简易留言

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

作者: 啃饼小白 | 来源:发表于2018-07-22 15:14 被阅读39次

    写在前面

    本篇笔记主要是简易留言簿的交互实现,笔记中本篇(第五篇)对应上传的仓库为: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" }}


    length,返回值的长度。它对字符串和列表都起作用。例如:{{ value|length }}


    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对应第五篇截止代码。

    相关文章

      网友评论

      • 水镜的先生:ordering = 'object_id' 报错,必须是list或者元组才行。。。
        啃饼小白:@水镜的先生 是的,里面必须是一个列表或者元组,但是我这里并没有在代码里添加排序后续代码里面对这个做了说明

      本文标题:零基础使用Django2.0.1打造在线教育网站(五):简易留言

      本文链接:https://www.haomeiwen.com/subject/wbltmftx.html