• python manage.py runserver

Django MTV 框架的精髓:

M(Model):创建数据模型,并执行数据库迁移 T(Template):写出基本的 Django 模板,并从视图中传入数据 V(View):在视图中访问数据库,实现业务逻辑,渲染模板,并接入路由表

Django 后,我们用 Django 自带的脚手架工具 django-admin 创建项目:

django-admin startproject django_news cd django_news

生成的项目骨架及每个文件的作用如下所示:

django_news
├── django_news              // 项目全局文件目录
│   ├── __init__.py
│   ├── settings.py          // 全局配置
│   ├── urls.py              // 全局路由
│   └── wsgi.py              // WSGI服务接口(暂时不用纠结这个是神马)
└── manage.py                // 项目管理脚本

我们使用 manage.py 来运行开发服务器(Development Server):

python manage.py runserver

  • Django 是一个高度模块化的框架。具体而言,一个 Django 应用由多个子应用组成,我们一般称之为 App

Django App

Django App 一般分为三大类(根据来源):

  • 内置:即 Django 框架自带的应用,包括 **admin(后台管理)、auth(身份鉴权)、sessions(会话管理)**等等
  • 自定义:即用来实现我们自身业务逻辑的应用,这里我们将创建一个新闻展示应用
  • 第三方:即社区提供的应用,数量极其丰富,功能涵盖几乎所有方面,能够大大减少开发成本
  • 所有的 Django 应用都在 django_news/settings.pyINSTALLED_APPS 列表中定义:
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

实现自定义 App


python manage.py startapp news
# 生成的 news 应用文件夹结构如下所示:

news                     // news 应用目录
├── __init__.py          // 初始化模块
├── admin.py             // 后台管理配置
├── apps.py              // 应用配置
├── migrations           // 数据库迁移文件目录
│   └── __init__.py      // 数据库迁移初始化模块
├── models.py            // 数据模型
├── tests.py             // 单元测试
└── views.py             // 视图

Model(模型)+ Template(模板)+ View(视图)

# 将自定义 App 添加到全局配置
# 最后,我们在 settings.py 中将 news 应用加入 INSTALLED_APPS 中:

# ...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'news',
]

理解视图:业务逻辑的编写

  1. 在视图(View)中写一点业务逻辑
  2. 接入路由,使其能够被访问

Django 的路由系统

  • Django 的路由系统是由全局路由和子应用路由组成。
    • 简单来说,根据用户输入的 URL,全局路由表进行匹配并选择正确的子应用路由
    • 再由所选择的子应用路由匹配并选择正确的视图(View)。
  1. 用户访问 http://example.com/apple/buy
    1. 然后全局路由根据 /apple/buy 先选择 apple 的路由表
    2. 再从 apple 路由表中根据 /buy 选择 /buy 路由
    3. 然后执行 /buy 对应的 BuyView 视图,返回给用户结果。

编写视图

  • 对视图访问的流程大致了解之后,我们就可以开始动手了。
  • 首先打开 news/views.py,写一个简单的视图函数,返回一串 Hello World!:
from django.http import HttpResponse


def index(request):
    return HttpResponse('Hello World!')
  • Django 同时支持基于函数的视图 **(FBV,Function-based View)**和基于类的视图 (CBV,Class-based View)
  • 这里显然是 FBV,接收一个 request 请求对象作为参数,返回了一个 HttpResponse 对象。

视图接入路由

  • 要让路由系统能够访问到刚才写好的视图函数。因此先实现子应用 news 的路由表,创建 news/urls.py 文件如下:
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

  • 每一个 Django 路由表模块(urls.py)中都约定必须包含一个 urlpatterns
  • 列表用来存放路由映射表。列表中每个元素是一个用 django.urls.path 函数封装好的路由映射

通常接收以下三个参数:

  • route:必须,即实际的访问路由,空字符串等于 /,即空路由
  • view:必须,该路由将要访问的视图
  • name:可选,该路由的名称,方便后续在模板中使用

接入全局路由表

  • 将刚刚写好的 news 路由表接入全局路由表。由于我们希望新闻能够展示在首页(即通过 / 就能访问,无需 /news)
  • 因此 news 应用路由在全局路由中的 URL 是一个空字符串。在 django_news/urls.py 中修改如下:

注意

  • 添加路由规则时顺序是很重要的,因为在尝试匹配时会按照从上到下的顺序进行,因此应该把最模糊的路由(即空路由)放在最下面。

网页前端的实现

表达式插值

  1. 最常用的语法,没有之一。通过在一对花括号 {{}} 放入一个表达式,就能够在视图中传入表达式中变量的内容
  2. 并最终渲染成包含变量具体内容的 HTML 代码。
  3. 需要注意的是,所支持的表达式仅支持以下形式(可以自由组合):
<!-- 单个变量 -->
{{ variable }}

<!-- 获取字典的键或对象的属性 -->
{{ dict.key }}
{{ object.attribute }}

<!-- 获取列表中的某个元素 -->
{{ list.0 }}
例如,模板这样写:

<h1>{{ name }}</h1>
<p>{{ news.title }}</p>
<p>{{ news.visitors.0 }}</p>
如果我们在视图中传入以下上下文字典(Context Dictionary):
<script>
{
    'name': 'Tuture',
    'news': {
        'title': 'Hello World',
        'visitors': ['Tom', 'Marc'],
    }
}
</script>
那么最终渲染成的 HTML 代码就是:

<h1>Tuture</h1>
<p>Hello World</p>
<p>Tom</p>

条件语句

{% if is_true %}
  <h1>It is true!</h1>
{% else %}
  <h1>It is false!</h1>
{% endif %}

如果变量 is_true 为真,那么最终渲染出来的就是 <h1>It is true!</h1>,
否则就是 <h1>It is false!</h1>。
注意:整个条件语句必须以 {% endif %} 结束,
并且 {% else %} 是可选的。

循环语句

{% for elem in some_list %}
  <p>{{ elem }}</p>
{% endfor %}
// 如果传入的 some_list 为 ['Apple', 'Banana', 'Orange'],那么渲染出的 HTML 代码就是:

<p>Apple</p>
<p>Banana</p>
<p>Orange</p>

实现第一个 Django 模板

  • 在 news 目录中创建一个 templates 目录
    • 再在 templates 目录中创建一个 news 目录
    • 并在内层的 news 目录中创建 index.html 文件
mkdir -p news/templates/news
touch news/templates/news/index.html

  • 只创建 news/templates,然后把模板放里面不就好了,为什么还要再创建一个 news 目录?
  • 这是由于 Django 的模板查找机制会将所有应用里面的模板全部收集到一起
  • 如果两个模板的名字冲突,就会导致其中一个模板不能被正确访问。
  • 如果放在 news 子文件夹里面,就能够通过 news/index.html 访问,通过命名空间的机制避免了冲突。
{% if news_list %}
  <ul>
  {% for elem in news_list %}
    <li>
      <h3>{{ elem.title }}</h3>
      <p>{{ elem.content }}</p>
    </li>
  {% endfor %}
  </ul>
{% else %}
  <p>暂无新闻</p>
{% endif %}

视图中对其进行渲染

  • 打开 news/views.py 文件
from django.shortcuts import render
def index(request):
    context = {
        'news_list': [
            {
                "title": "图雀写作工具推出了新的版本",
                "content": "随随便便就能写出一篇好教程,真的很神奇",
            },
            {
                "title": "图雀社区正式推出快速入门系列教程",
                "content": "一杯茶的功夫,让你快速上手,绝无担忧",
            },
        ]
    }
    return render(request, 'news/index.html', context=context)
  • 调用 django.shortcuts.render 函数来渲染模板,这个函数通常接受三个参数(有其他参数,但是这里我们不关心):

    • request:请求对象,直接把视图的参数 request 传进来就可以
    • template_name:模板名称,这里就是我们刚刚创建的 news/index.html
    • context:传入模板的上下文对象,必须是一个字典,字典中的每个键对应模板中的变量。这里我们弄了些假数据,假装是从数据库里面取来的。
from django.shortcuts import render
from django.http import HttpResponse
import json

def index(request):


    context = {
        'news_list': [
            {
                'title': "图雀写作工具推出了新的版本",
                "content": "随随便便就能写出一篇好教程,真的很神奇",
            },
            {
                "title": "图雀社区正式推出快速入门系列教程",
                "content": "一杯茶的功夫,让你快速上手,绝无担忧",
            },
        ],
        'name':'lusifer',
        'age':17778179517
    }
    print(type(context))
    jd= json.dumps(context)
    # return HttpResponse(jd,content_type='application/json')

    return render(request,'news/idx.html',json.loads(jd))

取值

<body>
{{ news_list}}
{{ name}}
{{ age}}
<div class="ap">
    {% for item in news_list %}
    <span> {{item.title  }}</span> <br>
    {% endfor %}
</div>
</body>

Model

Django 在数据模型方面的设计堪称典范,列举一些闪光点:

  • 由于高度解耦的设计,可轻松切换各种关系型数据库(默认的 SQLite,可选 MySQL、PostgreSQL、Oracle 等等)
  • 强大的 ORM(Object Relation Mapping,对象关系映射)模块,使得用 Python 操作数据库非常轻松,免去了使用 SQL 的麻烦
  • 优秀的数据库迁移机制(Migration),修改数据模式(Schema)比较方便,能够适应不断变化的功能需求
# 查询所有模型
# 等价于 SELECT * FROM Blog
Blog.objects.all()

# 查询单个模型
# 等价于 SELECT * FROM Blog WHERE ID=1
Blog.objects.get(id=1)

# 添加单个模型
# 等价于 INSERT INTO Blog (title, content) VALUES ('hello', 'world')
blog = Blog(title='hello', content='world')
blog.save()
  1. 用 Django 定义了一个新的数据模型
  2. 用 makemigrations 命令创建迁移文件(存储在子应用的 migrations 目录里面)
  3. 用 migrate 命令执行迁移
  4. 在开发中发现第 1 步中定义的模型不完善,更新数据模型
  5. 跳转到第 2 步,反复循环

model设计时的总结

  1. 设计Model的时候,可以继承自Models.model或者AbstractUser
  2. AbstractUser是内置的用户类,当要继承内置的用户模型并进行扩展时,就使用它
  3. Models则是通用的模型类,自定义模型都需要继承这个.
  • AbstractUser要记得在setting.py里面加上AUTH_USER_MODEL = ‘users.UserProfile’
    • 设计表的时候,首先要分析需要哪些表,关键是分析关系:实体1 <对于关系> 实体2,表关系是一对多,还是多对一等等,要自行分析定义外键
    • 设计表的第二步是设计具体的字段,每个字段的类型是否是必填
from datetime import datetime
class BaseModel(models.Model):
	add_time = models.DateTimeField(default=datetime.now,verbose_name="添加时间"
	class Meta:
		# 设置成抽象基类,不然Django会帮我们创建这个表
		abstract = True
	class Course(BaseModel):
		'''这里继续写属性,已继承'''
	 	'''外键的设置'''
	class Lesson(BaseModel):
		course = models.ForeignKey(Course,on_delete=models.CASCADE)
		'''这里必须注意第一个参数是要关联的外键,
		第二个参数是一定要写的,用来设置当Course被删除的时候,
		外键怎么处理,CASCADE表示如果对应的课程被删除,那么外键的
		记录也会级联的删除,而一种SET_NULL属性是指课程被删除后
		这个外键这一列被置空,它必须和```null = True,blank = True```
		连用,否则都不能为空怎么置空呢

model创建语法

form django.db import models

class Modelname(models.Model):
	# 创建模型类并继承models.Model
	# 一个类就是数据库的一张表
	field1 = models.XXXfield(max_lenge=10,verbose_name='haha'
	field = models.XXXfield()
	class Meta():
		# 为这个类定义一个说明
		verbose_name = XXX
		# 不加这个的话在我们的verbose_name在admin里面会被自动加上s
		verbose_name_plural = XXX		 

models的属性与字段(field)

field类型

  • AutoField:一个自动递增的整形字段,通常用于主键
  • CharField:字符串字段,用于输入较短的字符,对应与HTML里面<input type='text'>
  • TextField:文本字段,用于输入较多的字符,对应html标签 <input type = "textarea">
  • EmailField:邮箱字段,用于输入带有Email格式的字符
  • DateFiled
  • TimeFiled
  • DateTimeField:日期字段,支持时间输入
  • ImageField:用于上传图片并验证图片合法性,需定义upload_to参数,使用本字段需安装python pillow等图片库
  • IntegerField:整数字段,用于保持整数信息

field属性

primary_key:设置True or False,定义此字段是否为主键 default:设置默认值,可以设置默认的文本、时间、图片、时间等 null:设置True or False,是否允许数据库字段为Null,默认为False blank:设置True or False,定义是否运行用户不输入,默认为False;若为True,则用户可以不输入此字段 max_length:设置默认长度,一般在CharField、TextField、EmailField等文本字段设置 verbose_name:设置该字段的名称,所有字段都可以设置,在Web页面会显示出来(例如将英文显示为中文) choices:设置该字段的可选值,本字段的值是一个二维元素的元祖;元素的第1个值为实际存储的值,第2个值为HTML页面显示的值 upload_to:设置上传路径,ImageField和FileField字段需要设置此参数,如果路径不存在,会自动创建

Meta类属性

verbose_name:设置对象名称(例如usecms),若没有设置,则默认为该类名的小写分词形式,例如类名为CamelCase会被转换为camel case; verbose_name_plural:设置对象名称复数(例如usercms),一般设置跟verbose_name一样,verbose_name_plural=verbose_name否则会默认加s; db_table:设置映射的数据表名,默认为“应用名_模型名”,即用该模型所在app的名称加本模型类的名称 proxy:设置True or False,设置本模型及所有继承本模型的子模型是否为代理模型; abstract:设置True or False,设置本模型类是否为抽象基类;如果是抽象基类,那么是不会创建这张表的,这张表用来作为基类被其他的表继承

model层的命令详解

python manage.py makemigrations+名字:# 生成数据库表的初始化文件initial.py文件 python manage.py migrate# # 基于数据库表初始化文件initial.py文件,正式生成数据表 python manage.py sqlmigrate polls 0001查看数据库的生成语句,因为initial.0001是数据库表的初始化文件

数据库的操作

1.查询操作:

modelname.object.all()取出所有的数据 modelname.object.get(pk=id)查询出符合筛选条件数据(一条),如果查到多条或者没有查到会报错 modelname.object.filter(....)查询出符合筛选条件的数据集(一条或者多条),如果对象不存在会返回一个空的列表,而不会报错。 ModelName.Objects.exclude( )返会不符合筛选条件的数据集