Django学习笔记(10)——Book单表的增删改查页面

一,项目题目:Book单表的增删改查页面

  该项目主要练习使用Django开发一个Book单表的增删改查页面,经过这个项目巩固本身这段时间学习Django知识。css

二,项目需求:

开发一个简单的Book增删改查页面

 要求:
    实现一个书籍的增删改查功能便可
    尽可能规范化代码

    查询操做:
        1,查找A出版社出版过的书籍价格大于100
        2,查询某月出版的全部python书籍名称
        3,查询价格为100,或者150的全部书籍名称及其出版社名称
        4,查询价格在100-200之间的全部书籍名称及其价格
        5,查询全部A出版社出版的书籍价格,(降序排列,去重)

  

三,编码规范需求:

编码规范需求:15%
1. 代码规范遵照pep8 (https://python.org/dev/peps/pep-0008/)
2. 函数有相应的注释
3. 程序有文档说明文件(README.md参考:https://github.com/csrftoken/vueDrfDemo)
4. 程序的说明文档必须包含的内容:程序的开发环境(django版本)、程序的实现的功能、程序的启动方式、登陆用户信息、程序的运行效果
5. 程序设计的流程图:

  

四,项目思路

1,建立项目及其APP

1,建立project

    django-admin  startproject  Book_single

2,建立APP

    python  manage.py  startapp  app01

3,settings配置设置模板
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'template')],
        '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',
            ],
        },
    },
]


4,将APP添加到settings.py里面
  
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01'
]


5,设置时区和语言
    Django默认使用美国时间和英语,在项目的settings文件中,以下图所示:
 
LANGUAGE_CODE = 'en-us'
  
TIME_ZONE = 'UTC'
  
USE_I18N = True
  
USE_L10N = True
  
USE_TZ = True
     
    咱们将其改成 亚洲/上海  时间和中文
 
LANGUAGE_CODE = 'zh-hans'
  
TIME_ZONE = 'Asia/Shanghai'
  
USE_I18N = True
  
USE_L10N = True
  
USE_TZ = False

  

2,配置URL

  这里将全局配置的urls.py文件和APP下面的urls.py分开写。(本身在APP下面建立一个urls.py)这样作的好处:html

  • 1,全局配置文件中的urls.py模型主要管理本项目的全局url配置(虽然目前此项目只有一个APP)
  • 2,每一个APP的urls.py模块管理本身APP下的url集。

2.1 全局配置文件(Book_single/urls.py)

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
]

  

2.2  APP下url配置文件(Book_single/app01/urls.py)

  name的做用就是对本身的URL进行命名,让本身可以处于Django的任意处,尤为是模板内显式的引用它,这是一个很是强大的功能,至关于给URL取了一个全局变量名,不会将url匹配地址写死。前端

from django.urls import path, re_path
from app01 import views

app_name = 'app01'

urlpatterns = [
    path('books/', views.books, name='books'),
    path('addbooks/', views.addbooks, name='addbooks'),
    path('<int:id>/delete', views.delbook, name='delbook'),
    path('<int:id>/change/', views.changebook, name='changebook'),
    path('query/', views.query, name='query'),
    path('test/', views.test, name='test')
]

  

  在二级路由(即APP的URls文件中),在urlpatterns后,应该加上app_name='app_name',不然有可能会发生报错。vue

 

3,设计数据库

3.1 数据库模式设计

  做为一个单表增删改查操做项目,很明显,咱们至少须要一个Book表,用来保存下面信息:图书标题,价格,出版日期,出版社等等,代码以下:html5

  Book_single/app01/models.pypython

from django.db import models

# Create your models here.

class Book_single(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32, unique=True)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    pub_date = models.DateField()
    publish = models.CharField(max_length=32)

    def __str__(self):
        return self.title

  各字段含义:mysql

  • id必填,自增字段,而且是主键
  • title,最长不超过32个字符,而且惟一,也就是不能有相同名字
  • price,设置了精度的十进制数字,数字容许的最大位数是8位,小数的最大位数是2.
  • 出版日期,设置出版日期
  • 出版社,最长不超过32位
  • 使用__str__帮助人性化显示对象信息

3.2  更改settings配置,设置数据库

  在settings中修改DATABASES:jquery

1,注销掉下面设置


# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#     }
# }

2, 新增下面设置


import pymysql

pymysql.install_as_MySQLdb()

DATABASES = {
    'default':{
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}

  

3.3  数据库迁移

python manage.py makemigrations
 
python manage.py migrate

  结果展现:git

D:\Django\Book_single>python manage.py makemigrations
Migrations for 'app01':
  app01\migrations\0001_initial.py
    - Create model Book_single



D:\Django\Book_single>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
  Applying app01.0001_initial... OK

  

4,编写视图

  因为是单表操做,并且视图函数比较简单,因此咱们不须要初步规划视图函数,直接写便可。github

   Book_single/app01/views.py

from django.shortcuts import render, HttpResponse, redirect

# Create your views here.
from app01 import models

def books(request):
    book_list = models.Book_single.objects.all()
    return render(request, 'app01/books.html', locals())

def addbooks(request):

    # title = 'python1'
    # price = 100
    # publish = '机械出版社'
    # pub_date = '2019-3-3'
    # book_obj = models.Book_single.objects.create(title=title, price=price,
    #                                publish=publish, pub_date=pub_date)
    # book_obj.save()
    # return HttpResponse("OK")
    if request.method == 'POST':
        title = request.POST.get('title')
        price = request.POST.get('price')
        pub_date = request.POST.get('pub-date')
        publish = request.POST.get('publish')
        if title == '' or price == '' or pub_date == '' or publish == '':
            return render(request, 'app01/addbook.html', {'ret': '全部选项不能为空'})

        models.Book_single.objects.create(title=title, price=price,
                                          pub_date=pub_date, publish=publish)
        return redirect('/app01/books')
    else:
        return render(request, 'app01/addbook.html')

def delbook(request, id):
    # return HttpResponse("OK")
    print(models.Book_single.objects.filter(id=id))
    models.Book_single.objects.filter(id=id).delete()

    # 两次请求
    return redirect('/app01/books/')

def changebook(request, id):
    book_obj = models.Book_single.objects.filter(id=id).first()
    print(book_obj)
    if request.method == 'POST':
        title = request.POST.get('title')
        price = request.POST.get('price')
        pub_date = request.POST.get('pub-date')
        publish = request.POST.get('publish')
        if title == '' or price == '' or pub_date == '' or publish == '':
            return render(request, 'app01/addbook.html', {'ret': '全部选项不能为空'})

        models.Book_single.objects.filter(id=id).update(title=title, price=price, pub_date=pub_date, publish=publish)
        return redirect('/app01/books/')
    else:
        return render(request, 'app01/change.html', {'book_obj': book_obj})

def query(request):
    # 1, 查询Publish1出版过价格大于100的书籍
    book_list = models.Book_single.objects.filter(publish='Publish1', price__gt=100)
    print(book_list)

    # 2,查询2019年1月出版的全部以py开头的数据名称
    book_list = models.Book_single.objects.filter(title__startswith='py', pub_date__year=2019,
                                                  pub_date__month=1).values('title')
    print(book_list)

    # 3,查询价格为50,100 或者150 的全部书籍的名称及其出版社名称
    book_list = models.Book_single.objects.filter(price__in=[50, 100, 150]).values('title', 'publish')
    print(book_list)

    # 4,查询价格在100到200之间全部书籍名称及其价格
    book_list = models.Book_single.objects.filter(price__range = [100, 200]).values('title', 'price')
    print(book_list)

    # 5, 查询全部Publish1出版的书籍的价格(由高到底排序,去重)
    book_list = models.Book_single.objects.filter(publish='Publish1').values('price').distinct().order_by('-price')
    print(book_list)

    return HttpResponse('OK')


def test(request):
    return HttpResponse("OK")

  

5,建立HTML页面文件

5.1,建立四个HTML文件

  先不填充内容,建立便可。在项目的根路径下建立一个template目录,而后在template目录下建立一个app01目录,在里面建立HTML,以下:

 

5.2,引入Bootstrap

  Bootstrap3.3.7的下载地址:请点击我

  JQuery 的下载地址:请点击我

  注意:{% static  '相对路径' %}  这个Django为咱们提供的静态文件加载方法,能够将页面与静态文件连接起来。

  根目录下新建一个static目录,并将解压后的bootstrap-3.3.7目录,总体拷贝到static目录中,并且因为Bootstrap依赖于JQuery,因此咱们须要引入JQuery,而且在static目录下,新建一个CSS和JS目录,做为之后的样式文件和JS文件的存放地,建立展现图以下:

  而后打开项目的settings文件,在最下面添加配置,用于指定静态文件的搜索目录:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

  

5.3  建立base.html模板

  既然要将前端页面作的像个样子,那么就要各写各的。一个网站要有本身的统一风格和公共部分,能够将这部份内容集中到一个基础模板base.html中。如今在根目录下的template中新建一个base.html文件做为站点的基础模板。

  在Bootstrap文档中,为咱们提供了一个很是简单并且又实用的基础模板,代码以下:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其余内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>
  
    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
  
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>你好,世界!</h1>
  
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
  </body>
</html>

  将其总体拷贝到base.html文件中。

  而后修改base内容,最后base.html文件的内容以下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其余内容都*必须*跟随其后! -->
    {% block title %}
        <title>base</title>
    {% endblock title %}
    <!-- Bootstrap -->
    <link href="/static/bootstrap-3.3.7/css/bootstrap.min.css" rel="stylesheet">

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:经过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起做用 -->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

    <link rel="stylesheet" href="/static/CSS/base.css">

</head>
<body>

<div class="container my-con">
    <div class="col-md-2">
        {% block operation %}
            <h2>operation</h2>
        {% endblock operation %}
    </div>

    <div class="col-md-10">
        {% block con %}
            <h2>content</h2>
        {% endblock con %}
    </div>

</div>

<!-- jQuery (Bootstrap 的全部 JavaScript 插件都依赖 jQuery,因此必须放在前边) -->
<script src="/static/JS/jquery-3.2.1.min.js"></script>
<!-- 加载 Bootstrap 的全部 JavaScript 插件。你也能够根据须要只加载单个插件。 -->
<script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script>



</body>
</html>

  

 5.4  完成本身编写的HTML内容

addbook.html
{% extends 'base.html' %}

{% block title %}
    <title>addbook</title>
{% endblock title %}

{% block operation %}
    <h2>添加书籍</h2>
{% endblock operation %}

{% block con %}
    <form action="" method="post">
        {% csrf_token %}
        <div class="form-group">
            <label for="title">书籍名称</label>
            <input type="text" class="form-control" id="title" name="title">
        </div>

        <div class="form-group">
            <label for="price">价格</label>
            <input type="text" class="form-control" id="price" name="price">
        </div>

        <div class="form-group">
            <label for="pub-date">出版日期</label>
            <input type="text" class="form-control" id="pub-date" name="pub-date">
        </div>

        <div class="form-group">
            <label for="publish">出版社</label>
            <input type="text" class="form-control" id="publish" name="publish">
        </div>

        <button type="submit" class="btn btn-success pull-right">提交</button>
    </form>

    <p style="color: red;">{{ ret }}</p>

{% endblock con %}

  

books.html
{% extends 'base.html' %}

{% block title %}
    <title>books</title>
{% endblock title %}

{% block operation %}
    <h2>查看书籍</h2>
{% endblock operation %}

{% block con %}
    <a href="{% url 'app01:addbook' %}" class='btn btn-primary' role='button'>添加书籍</a>

    <div class="table-responsive">
        <table class="table table-striped table-bordered table-hover">
            <thead>
                <tr class="active">
                    <td><strong>书籍名单</strong></td>
                    <td><strong>价格</strong></td>
                    <td><strong>出版日期</strong></td>
                    <td><strong>出版社</strong></td>
                    <td><strong>删除操做</strong></td>
                    <td><strong>编辑操做</strong></td>
                </tr>
            </thead>

            <tbody>
                {% for book in book_list %}
                <tr>
                    <td>{{ book.title }}</td>
                    <td>{{ book.price }}</td>
                    <td>{{ book.pub_date|date:'Y-m-d' }}</td>
                    <td>{{ book.publish }}</td>
                    <td><a href="/app01/{{ book.pk }}/delete" class="btn btn-danger" role="button" >删除</a></td>
                    <td><a href="/app01/{{ book.pk }}/change" class="btn btn-info" role="button" >编辑</a></td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>

{% endblock con %}

  

change.html
{% extends 'base.html' %}

{% block title %}
    <title>change</title>
{% endblock title %}

{% block operation %}
    <h2>编辑书籍</h2>
{% endblock operation %}

{% block con %}
    <form action="" method="post">
        {% csrf_token %}
        <div class="'form-group">
            <label for="title">书籍名称</label>
            <input type="text" class="form-control" id="title" name="title" value="{{ book_obj.title }}">
        </div>

        <div class="'form-group">
            <label for="price">价格</label>
            <input type="text" class="form-control" id="price" name="price" value="{{ book_obj.price }}">
        </div>

        <div class="'form-group">
            <label for="pub-date">出版日期</label>
            <input type="text" class="form-control" id="pub-date" name="pub-date" value="{{ book_obj.pub_date|date:'Y-m-d' }}">
        </div>

        <div class="'form-group">
            <label for="publish">出版社</label>
            <input type="text" class="form-control" id="publish" name="publish" value="{{ book_obj.publish }}">
        </div>

        <button type="submit" class="btn btn-success pull-right">提交</button>

    </form>
    <p style="color: red;">{{ ret }}</p>
{% endblock con %}

  

5.5 部分模板语法的解析

  从上面的HTML代码,咱们能够发现出现了许多模板语言。最多的就是使用大括号和百分比的组合来表示。

  下面学习一下标签的使用。标签看起来像下面的格式。可是标签比变量更加复杂:一些在输出中建立文本,一些经过循环或者逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模板中。一些标签须要开始和结束标签等等。

5.5.1  {%  csrf_token %}

  csrf_token 标签,用于生成csrf_token的标签,用于防治跨站攻击验证。注意若是你的view的index使用的是render_to_response方法,则不会生效。

  其实这只会生成一个input 标签,和其余表单标签一块儿提交给后台。

5.5.2  extend(继承)模板标签

  在实际应用中,咱们将用到Django模板系统来建立整个HTML页面。这就带来了一个常见的Web开发问题:在整个网站中,如何减小共有页面区域(好比站点导航)所引发的重复和冗余代码?

  解决该问题的传统作法是使用服务器端的includes,咱们能够在HTML页面中使用该指令将一个网页嵌入到另外一个中。事实上,DJango经过 {% include  %} 支持了这种方法。 

  可是Django解决此类问题的首先方法是使用更加优雅的策略——模板继承。

  本质上来讲,模板继承就是先构造一个基础框架模板,然后在其子模板中对它所包含站点共用部分和定义块进行重载。

  让咱们经过修改 current_datetime.html 文件,为 current_datetime 建立一个更加完整的模板来体会一下这种作法:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>The current time</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    <p>It is now {{ current_date }}.</p>
  
    <hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

  这看起来很好,可是若是咱们要为hours_ahead视图建立另外一个模板会发生什么呢?

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>Future time</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
  
    <hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

  很显然,咱们刚才重复了大量的HTML代码。详细一下,若是有一个更典型的网站,它由导航条,样式表,可能还会有一些JavaScript 代码,事情必将以向每一个模板填充各类冗余的HTML而了结。

  解决这个问题的服务器端include方案是找出两个模板中的共同部分,将其保存为不一样的模板片断,而后在每一个模板中进行include。也许你能够把模板头部的一些代码保存为header.html 文件:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>

  你可能也会将底部保存到文件 footer.html:

<hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

  对基于include 的策略,头部和底部的包含很简单。麻烦的是中间部分。在此范例中,每一个页面都有一个My helpful timestamp site</h1> 标题,可是这个标题不能放在 header.html 中,由于每一个页面的 <title> 是不一样的。 若是咱们将 <h1> 包含在头部,咱们就不得不包含 <title> ,但这样又不容许在每一个页面对它进行定制。 何去何从呢?

  Django的模板继承系统解决了这些问题,你能够将其视为服务器端include的逆向思惟版本。你能够对那些不一样的代码进行定义,而不是共同代码段。

  第一步是定义基础模板,该框架以后将由子模板所继承。如下是咱们目前所讲述范例的基础模板:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    {% block content %}{% endblock %}
    {% block footer %}
    <hr>
    <p>Thanks for visiting my site.</p>
    {% endblock %}
</body>
</html>

  这个叫作base.html 的模板定义了一个简单的HTML框架文档,咱们将本站点的全部页面中使用。子模板的做用就是重载,添加护着保留那些块的内容。(这个文件能够保存在template目录下,命令为base.html)

  咱们将使用模板标签{% block %} 。 全部的 {% block %} 标签告诉模板引擎,子模板能够重载这些部分。 每一个{% block %}标签所要作的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。

  如今咱们已经有了一个基本模板,咱们能够修改 current_datetime.html 模板来 使用它:

{% extends "base.html" %}
  
{% block title %}The current time{% endblock %}
  
{% block content %}
  <p>It is now {{ current_date }}.</p>
{% endblock %}

  再为hours_ahead 视图建立一个模板,看起来是这样的:

{% extends "base.html" %}
  
{% block title %}Future time{% endblock %}
  
{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}

  看起来很漂亮是否是?每一个模板只包含对本身而言独一无二的代码。无需多余的部分。若是想要对站点级进行设计修改,仅仅须要修改base.html,全部其余模板会当即反映出所作的修改。

如下是其工做方式:

  在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎当即装载其父模板,即本例中的 base.html 。此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。所以,引擎将会使用咱们在 block title %} 中定义的标题,对 {% block content %} 也是如此。 因此,网页标题一块将由{% block title %}替换,一样地,网页的内容一块将由 {% block content %}替换。

       注意因为子模板并无定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容老是被看成一条退路。继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板均可以访问到你传到模板中的每个模板变量。你能够根据须要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:

   <1> 建立 base.html 模板,在其中定义站点的主要外观感觉。 这些都是不常
修改甚至从不修改的部分。

   <2> 为网站的每一个区域建立 base_SECTION.html 模板(例如, base_photos.html
和 base_forum.html )。这些模板对base.html 进行拓展,并包含区域特定的风格与设计。

   <3> 为每种类型的页面建立独立的模板,例如论坛页面或者图片库。 这些模板拓展
相应的区域模板。

  这个方法可最大限度的重用代码,并使得向公共区域(如区域级的导航)添加内容称为一件轻松的工做。

5.5.3  模板继承的一些诀窍

<1>若是在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。
不然,模板继承将不起做用。
 
 <2>通常来讲,基础模板中的 {% block %} 标签越多越好。 记住,子模板没必要定义
父模板中全部的代码块,所以你能够用合理的缺省值对一些代码块进行填充,而后只对
子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。
 
 <3>若是发觉本身在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的
某个 {% block %} 中。若是你须要访问父模板中的块的内容,使用 {{ block.super }}
这个标签吧,这一个魔法变量将会表现出父模板中的内容。 若是只想在上级代码块基
础上添加内容,而不是所有重载,该变量就显得很是有用了。
 
 <4>不容许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因
为block 标签的工做方式是双向的。
    也就是说,block 标签不只挖了一个要填的坑,也定义了在父模板中这个坑所填充
的内容。若是模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知
要使用哪一个块的内容。

  

 

五,注意事项及其补充内容

5.1,django.db.utils.ProgrammingError: (1146, u"Table'' doesn't exist")解决办法

5.1.1  现象

  在数据库中删除了一张表,从新执行python  manage.py migrate 时报错,提示不存在这张表

5.1.2  缘由

  主要是由于Django通常在第一次迁移的时候新建表,后面都不会新建表,而是只检查字段等等的变化,因此咱们既然已经删除了这张表,django检查这张表的字段变化的时候天然报错了。

5.1.3  解决方法

  解决方法仍然是执行数据库迁移命令

python manage.py  makemigrations

python manage.py  migrate

  只不过在执行上面代码以前,把第一次执行迁移建立的那个记录删除掉,不然它检查到已经执行过第一次了,那么它后面就不会建立表了。

  在该APP模块下,有一个migtations文件夹,除了前两个文件外,其余的文件都删除,其实每一次若是有变化的话,这边就会生成一个文件,下面的这个001_initial.py看名字就知道是第一次迁移的时候生成的,也就是由于有它的存在,因此之后每次再执行就再也不建立表了。

  其次,在数据库里面也有相应的记录,也要删除。咱们仔细看看数据库里面存的是什么,在Django_migrations里面,这表里面存的都是每次迁移的记录,固然记录的是什么模块以及对应的文件名字,好比咱们这里的模块是dtheme,这里的文件名叫作001_initial,和咱们文件夹里面是一一对应的,一样,删除这条记录。

  而后在执行数据库迁移命令便可。

  注意:若是这个APP模块下面还有其余的model的话,那么其余的model建立的表也要删除掉,至关于咱们这样的解决方案是针对整个APP模块的,要执行就会所有从新生成,否则会提示部分表已经存在错误。

5.2 django.db.utils.InternalError: (1366, "Incorrect string value: '\\xE6\\x9C\\xBA\\xE6\\xA2\\xB0...' for column 'publish' at row 1")

   这个问题,在MySQL中修改配置文件。

  具体请参考博客:请点击我

5.2  补充1:正则匹配

  上面代码咱们用到了正则匹配,下面学习一下正则匹配的用法。

  (若是想全面学习正则表达式的使用:请点击我

  这里介绍与Django有关的知识点。咱们最经常使用的正则匹配就是下面两个:

    ^     表示会匹配行或者字符串的起始位置,有时还会匹配整个文档的起始位置


    $      表示匹配行或者字符串的结尾

  Django在检查URL模式以前,移除每个申请的URL开头的斜杠(/)。这意味着咱们写的URL模式不用包括斜杠(/)。

  举个例子:

from django.urls import path, re_path
from app01 import views


urlpatterns = [
    path('login/<int:id>/', views.login),
    #  上下两个是一样的效果
    re_path(r'login/(\d+)/', views.login),
]

  

5.3  补充2:re_path和path

  在新版Django2.X中,URL的路由表示用path 和re_path代替,模块的导入由Django1.X版本的url变为Django2.X中的path,re_path。

  看下面两幅图:

  并且该url() 函数传递了四个参数,两个必需:regex和view,以及两个可选:kwargs 和 name。也就是说正则表达式和视图是两个必填参数。

  函数path() 具备四个参数,两个必需参数 :route 和view,两个可选参数:kwargs 和 name。即路由和视图是必填参数。

  因此二者的主要区别就在于url() 是要写正则表达式(regex)的路由,而path()时写非正则路由(route),接下来主要看一下path() 函数的四个参数含义。

1,path() 参数: route

    route 是一个匹配URL的准则(相似于正则表达式),当Django响应一个
请求时,它会从urlpaterns的第一项开始,按顺序依次匹配列表中的项,直到
找到匹配的项。
    这些准则不会匹配GET 和 POST 参数或域名。例如:URLconf在处理请求
https://www.example.com.myapp/时候,它会尝试匹配myapp/。处理请
求https://www.example.com/myapp/?page=3时,也只会尝试匹配myapp/

2, path()参数: view
    
    当django找到一个匹配的准则,就会调用这个特定的视图函数,并传入一个
HttpRequest对象做为第一个参数,被“捕获”的参数以关键字参数的形式传入。

3, path()参数:kwargs

    任意个关键字参数能够做为一个字典传递给目标视图函数

4, path()参数: name

    为你的URL取名能使你在Django的任意地方惟一的引用它,尤为是在模板中。
这个有用的特性运行你只改一个文件就能全局的修改某个URL模式。

  

5.4  path()  的用法

  上面介绍的path中,第一个参数route使用的是非正则表达式能够表示的普通路由路径。

注意:

  • 要从URL捕获值,请使用尖括号
  • 捕获的值能够选择包括转换器类型。例如:用<int:name> 捕获整数参数。若是未包含转换器 / ,则匹配除字符以外的任何字符串。
  • 没有必要添加前导斜杠,由于每一个URL都有。例如,articles  不是 /articles。

默认状况下,如下路径转换器可用:

  • str- 匹配除路径分隔符以外的任何非空字符串‘/’。若是转换器未包含在表达式中,则这是默认值。
  • int- 匹配零或者任何正整数,返回一个int
  • slug- 匹配由ASCII字母或者数字组成的任何slug字符串,以及连字符和下划线字符。例如 : building-your-1st-django-site。
  • uuid- 匹配格式化的UUID,要防止多个URL映射到同一页面,必须包含短划线而且字母必须为小写。例如:0787878d3-4545-456a-d45ad6d4s5d4a65。返回一个UUID实例。
  • path- 匹配任何非空字符串,包括路径分隔符 ‘/’。这使得你能够匹配完整的URL路径,而不只仅是URL路径的一部分str。

  好比要匹配一个视图中的函数路由,该函数有两个形参:

def peopleList(request,book_id)

  第一个request是默认的,那么路由自动匹配该函数的第二个形参,匹配格式:<int:book_id>,并返回一个正整数或者零值。

urlpatterns = [
    path('booklist/', views.booklist, name='booklist'),
    path('<int:book_id>/', views.peopleList, name='peopleList'),
]

  

5.5  re_path() 的用法

  而若是赶上路径和转换器语法都不足以定义的URL模式,那么就须要使用正则表达式,这时候就须要使用re_path() ,而非path()。

from django.urls  import re_path

  在Python正则表达式中 ,命名正则表达式组的语法是(?P<name>pattern),组name的名称,而且pattern是要匹配的模式。

  仍是以上面的为例,采用正则表达式写下来,以下:

re_path(r'^(\d+)/$',views.peopleList,name='peopleList'),

# 相似于下面

path('<int:book_id>/', views.peopleList, name='peopleList'),

  这样也能够匹配到views视图中的peopleList函数的形参。

  因此这两种使用方式在使用上根据实际状况自行使用。

5.6  re_path和path的区别

  1,re_path和path的做用都是同样的。只不过re_path是在写url的时候能够用正则表达式,功能更增强大。

  2,写正则表达式都推荐使用原生字符串,也就是r开头的字符串

  3,在正则表达式中定义变量,须要使用圆括号括起来。这个参数是由名字的,那么须要使用(?P<参数的名字>)。而后在后面添加正则表达式的规则。

  4,若是不是特别需求,直接使用path就够了,省的将代码搞的很麻烦(由于正则表达式是很是晦涩的,特别是一些比较复杂的正则表达式,可能今天写的明天就忘了),除非是URL中确实是须要使用正则表达式来解决才使用re_path。

举个例子

urlpatterns = [
    re_path(r"^list/(?P<year>\d{4})/$", views.article_list),
    re_path(r"^list/(?P<month>\d{2})/$", views.article_list_month)
]

  第一个表达式以list开头,中间须要有4个数字,一个都不能多也不能少,再以'/'结尾。形如list/2019/这样的字符串才能被识别,同理,第二句是须要形如 list/02/这样的字符串才能被识别。

 

六,结果展现

6.1  项目树形图展现:

6.2  增长页面展现

  增长成功,直接跳转到查看界面

6.3 查看页面展现

6.4  编辑页面展现

 

6.5  建立的数据库以下:

 

七,代码展现及部分代码解析

7.1 完整代码

  传送门:请点击我(小编的GitHub)

 

7.2 部分注意写错的代码

  感受上面代码加上GitHub的代码,就不须要展现了。。。。。。

7.2.1  注意代码1

   算了,这里仍是放上APP的urls.py

from django.urls import path, re_path
from app01 import views

app_name = 'app01'

urlpatterns = [
    path('books/', views.books, name='books'),
    path('addbooks/', views.addbooks, name='addbooks'),
    path('<int:id>/delete', views.delbook, name='delbook'),
    path('<int:id>/change/', views.changebook, name='changebook'),
    path('query/', views.query, name='query'),
    path('test/', views.test, name='test')
]

  和views.py里面的删除和修改函数:

def delbook(request, id):
    # return HttpResponse("OK")
    print(models.Book_single.objects.filter(id=id))
    models.Book_single.objects.filter(id=id).delete()

    # 两次请求
    return redirect('/app01/books/')

def changebook(request, id):
    book_obj = models.Book_single.objects.filter(id=id).first()
    print(book_obj)
    if request.method == 'POST':
        title = request.POST.get('title')
        price = request.POST.get('price')
        pub_date = request.POST.get('pub-date')
        publish = request.POST.get('publish')
        if title == '' or price == '' or pub_date == '' or publish == '':
            return render(request, 'app01/addbook.html', {'ret': '全部选项不能为空'})

        models.Book_single.objects.filter(id=id).update(title=title, price=price, pub_date=pub_date, publish=publish)
        return redirect('/app01/books/')
    else:
        return render(request, 'app01/change.html', {'book_obj': book_obj})

  为何重点放这里呢,固然主要是由于本身犯错了,这里须要注意的。

  由于这里有一个匹配视图中函数路由的函数,里面又两个形参:

    path('<int:id>/delete', views.delbook, name='delbook'),
    path('<int:id>/change/', views.changebook, name='changebook'),

  第一个request是默认的,因此路由会自动匹配该函数的第二个形参,匹配格式为<int:id> , 并返回一个正整数或者零值。

(这里也能够用re_path进行匹配,这里附上两个相似的代码)

urlpatterns1 = [
    path('<int:id>/delete', views.delbook, name='delbook'),
    path('<int:id>/change/', views.changebook, name='changebook'),

    # 等同于下面

    re_path(r'^(\d+)/delete', views.delbook, name='delbook'),
    re_path(r'^(\d+)/change', views.changebook, name='changebook'),

]

  

7.2.2  注意代码2

  在settings.py中,添加配置,用于指定静态文件的搜索目录,注意代码以下:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

  注意  将STATICFILES  不能写错!!!

7.3  部分代码解析

7.3.1  获取全部数据显示在页面上

book_list = models.Book_single.objects.all()

  经过上面获取数据,拿到数据后,渲染给前端;前端经过for循环的方式,取出数据。

  目的就是经过Book_single(这个数据表)里面的字段拿到对应的数据。

7.3.2  删除功能的制做

models.Book_single.objects.filter(id=id).delete()

  经过上面的代码,找到数据库中对应的id字段,删除这个ID字段对应的这行数据。

  

7.3.3  编辑功能的制做

book_obj = models.Book_single.objects.filter(id=id).first()

  经过上面代码,拿到须要编译的ID,而后在数据库中找到对应的ID字段,而后对内容进行修改。

  修改完以后,对数据库对应字段的内容进行更新。

models.Book_single.objects.filter(id=id).update(title=title, price=price, 
pub_date=pub_date, publish=publish)

  

 7.3.4  查询功能的代码

  查询部分代码比较简单,这里回顾一下。

  若是不懂:请点击这里

def  query(request):
    # 1, 查询Publish1出版过价格大于100的书籍
    book_list1 = models.Book_single.objects.filter(publish='Publish1', price__gt=100)
    

    # 2, 查询2019年1月出版的全部以pyth开头的书籍名称
    book_list2 = models.Book_single.objetcs.filter(title__startwith='pyth', 
                                    pub_date__year=2019, pub_date__month=1).values('title')

    
    # 3, 查询价格为50, 100或者 150 的全部书籍的名称及其出版社名称
    book_list3 = models.Book_single.objects.filter(price__in[50, 100, 150]).values
                                    ('title, 'publish')



    #4, 查询价格在100到200之间全部书籍名称及其价格
    book_list4 = models.Book_single.objects.filter(price__range= [100, 200]).values
                                      ('title', 'price')

    
    #5,查询全部Publish1出版的书籍的价格(由高到低排序,去重)
    book_list5 = models.Book_single.objects.filter(publish="Publisher1").values
                                       ('price').distinct().order_by('-price')

  

 

参考文献:http://www.javashuo.com/article/p-fpnahete-dr.html

相关文章
相关标签/搜索