Django入门合集

392 阅读17分钟

前言

Django 是使用 Python 编写的一个开源 Web 框架,可以用它来快速搭建一个高性能的网站。

在阅读本文之前需要你有一定的python语言的基础,我也是先入门了python语言之后才开始进行Django学习的。虽然python可能和你目前正在使用的语言语法比较相似,其实所有的语言语法都有相似之处,只要你擅长某一种语言,都可以比较轻松的看懂其他语言,但是能看懂未必能写出来,所以这里建议还是先python再Django。

我之前也是完全没接触过python,所以本文比较适合和我一样的移动端开发,从零开始学习Django,因为我们都是从移动端的角度去接受Web框架,可能会产生更多的共鸣呢。

其实想要真正的去做Web开发,还需要熟悉前端的HTML 、CSS 、JavaScript等语言,以及数据库操作等,技术栈比较广泛,以后我们慢慢积累学习。本文以Django的学习使用为重点。

阅读完本文,你至少可以使用Django比较熟练的为前端提供接口、操作数据库等。

搭建开发环境

我自己的开发环境如下:

  • macOS Catalina 10.15.7,

  • python 3.7.3,

  • Django 3.1.2,

    为了更流畅的跟着下面的操作,尽量保持你的版本和我一致呢,可以避免一些版本间的差异性。

安装python

$ brew install python3

安装完成后,用python -V验证下是否安装完成,能正确打印版本号就是成功了。

虚拟环境Virtualenv

建议在Virtualenv下进行Django的开发。Virtualenv是一个python的工具,使用它可以创建一个独立的 Python 环境。比如说,创建一个供我们学习Django的虚拟环境,完全和你电脑上之前的环境隔离开来,互不影响。

安装使用Virtualenv

安装Virtualenv

$ pip3 install virtualenv 

指定一个目录,创建虚拟环境

$ virtualenv abc-env   虚拟环境名称

进入虚拟环境

$ source /Users/xxx/abc-env/bin/activate 

退出虚拟环境

$ deactivate

简化虚拟环境的操作 (可以跳过前面几个步骤 直接安装virtualenvwrapper 他会自动下载安装virtualenv)

$ pip3 install virtualenvwrapper

创建存放虚拟环境的文件夹

$ mkdir ~/.virtualenvs
$ cd ~/.virtualenvs

查看python3和virtualenvwrapper.sh的路径并保存下来

$ which python3
$ which virtualenvwrapper.sh

配置环境变量

$ vim ~/.bash_profile

添加如下代码使用上面保存的路径:
export WORKON_HOME='~/.virtualenvs'  //创建的虚拟环境会保存到这个路径
export VIRTUALENVWRAPPER_PYTHON='Python3路径'
source virtualenvwrapper.sh路径

保存环境变量

$ source ~/.bash_profile

创建虚拟环境

$ cd ~/.virtualenvs
$ mkvirtualenv my_env  就会自动进入到虚拟环境中

查看安装了哪些虚拟环境

$ lsvirtualenv

安装Django

在虚拟环境中安装

pip install django==3.1.2

这里指定了版本号,如果不指定,会安装最新版本。

创建Django工程

Django project是整个项目的代码容器,会包括我们自己写的python代码以及Django自动生成的所有配置文件和代码。下面主要说一下通过命令创建的过程,也可以通过pycharm的图形界面创建,就不一步步截图展示了,比较简单。

创建Django工程命令行方法(进入到Django的虚拟环境中)

$ django-admin startproject first_project  //first_project是项目名称
$ cd first_project

运行项目

Django 为我们提供了一个用于本地开发的 Web 服务器。在命令行中运行 python manage.py runserver 命令就可以在本机开启一个 Web 服务器:

$ python manage.py runserver

在浏览器输入 http://127.0.0.1:8000/ 就可以看到页面了,提示It worked!

改变端口号

$ python manage.py runserver 9000

停止运行

$ control + c

同局域网的其他电脑访问

1.让项目运行的时候 host为0.0.0.0 python manage.py runserver 0.0.0.0:8000 2.在setting.py文件中,配置ALLOWED_HOSTS,将本机ip地址添加进去,比如 ALLOWED_HOSTS = ['10.100.4.10']

查看manage.py的子命令

$ python manage.py help

到这里一切准备就绪,开始进入Django的学习吧。

项目实践

第一个Django应用

已经创建了工程,也运行看到了页面,这些都是Django默认生成的。下面就要开始写我们自己的业务代码了。

我们自己的代码建议根据业务需求,写到不同的应用中,这样以后工程结构明了,也好维护。

继续进入到虚拟环境中,创建应用,这里应用是比上面说的“工程”小一级的一个单位,一个工程中可以包含多个应用。根据业务需求,可以为“工程”创建多个负责不同功能的“应用”。

python manage.py startapp blog

刷新目录就会看到新增了一个blog的文件夹,已经创建好了需要在工程目录的settings.py中设置一下才会生效,

找到INSTALLED_APPS设置项,把blog添加进去

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

请求与响应

Web应用的交互过程就是HTTP请求和响应的过程,可以简单理解为,浏览器打开一个URL,然后返回响应数据的过程。

Django中有一套成熟的处理HTTP请求和响应的机制,只需要两步就可以完成一个对外的接口开发了。

  1. 视图函数

在views.py文件中编写视图函数:

from django.http import HttpResponse
# Create your views here.

def index(request):
    return HttpResponse("我的第一个接口")

这个视图函数体现了,Web 服务器接收来自用户的 HTTP 请求,根据请求内容作出相应的处理,并把处理结果包装成 HTTP 响应返回给用户的过程。它首先接受了一个名为 request 的参数,这个 request 就是 Django 为我们封装好的 HTTP 请求,它是类 HttpRequest 的一个实例。然后我们便直接返回了一个 HTTP 响应给用户,这个 HTTP 响应也是 Django 帮我们封装好的,它是类 HttpResponse 的一个实例,只是我们给它传了一个自定义的字符串参数。

  1. 绑定URL和视图函数

将URL和视图函数绑定,用户发起的HTTP请求才能准确的定位到响应的函数中去。

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

下面要介绍一个比较重要的知识点,URL命名。

url 命名

因为URL是经常变化的,如果代码中写死URL需要经常改代码,因此给URL命名,使用URL的时候,使用名字进行反转,不需要使用写死的URL了。 使用方法就是在path中添加一个name参数:

	# url 命名
	path('signin/', views.login, name='login'),

应用命名空间

防止多个App当中出现同名的URL,避免反转URL的时候产生混淆,可以使用应用命名空间来做区分 在App的urls.py中定义一个app_name的变量

# 应用命名空间
app_name = 'front'

urlpatterns = [
    path('', views.index),
    path('login/', views.login),
    # url 命名
    path('signin/', views.login, name='login'),
]

反转URL:

login_url = reverse('front:login')

路由分级

最后在工程的urls.py 中通过include,导入各个App中的url,这里要注意URL的拼接方式,最终业务请求的URL是外层的path+内层的path,比如:

# 外层path
from django.contrib import admin
from django.urls import path, include

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

# 内层path
urlpatterns = [
    path('orders/', views.listorders)
]

最终业务请求的URL是sales/orders/

数据库

数据库的知识,按理来说不是本文应该着重说的内容,但是又是不得不说的东西,没有数据也没办法往后继续做项目,所以还是得把数据库的内容放在比较前面的位置来。也为后面的学习做一个数据的基础。

网上Django方面的实战练习项目有很多,我自己学习的时候也是通过“B站”,以及各位大佬的博客,把几个实战的项目都自己手敲了一遍,比如有图书管理系统,校园管理系统等等,然后才开始做公司内部的项目。感觉必须经历这么个过程,才能心里有底的用自学的知识去给公司服务~

后面的文章中会选择一个实战项目,作为例子,分享一下Django中我们必须掌握的一些API和使用技巧,并且贴上关键代码。下面先来学习一下Django中为我们提供的ORM系统,看看他是如何帮助开发者简化数据库开发工作的。

ORM介绍

我们知道,日常的数据库开发中,sql语句的重复性很高,利用率很低,并且存在SQL注入的隐患。 Object Relational Mapping 对象关系映射,不用写原生的SQL语句,一个模型对应一张表,属性对应表中的字段,操作对象可以自动执行数据库语句。

在Django中

  • 定义一张数据库表,就是定义个继承自django.db.models.Model的类
  • 定义该表中的字段(列),就是定义该类里面的一些属性
  • 类的方法就是对该表中的数据的处理方法,包括数据的增删改查

这样做,极大简化了我们应用中的数据库开发,无需使用SQL语句提高了开发效率;其次,屏蔽了不同数据库访问的底层细节,如果需要换数据库,几乎不需要修改代码,修改几个配置项就可以做到了。

ORM使用

创建模型

在上面创建的应用blog中的models.py中定义表:

from django.db import models
from django.contrib.auth.models import User
# Create your models here.

class Category(models.Model):
    """
    Django 内置的全部类型可查看文档:
    https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types
    """
    name = models.CharField(max_length=100)


class Tag(models.Model):
    name = models.CharField(max_length=100)


class Post(models.Model):
    title = models.CharField(max_length=70)
    # 存储比较短的字符串可以使用 CharField,但对于文章的正文来说可能会是一大段文本,因此使用 TextField 来存储大段文本。
    body = models.TimeField()
    created_time = models.DateTimeField()
    modified_time = models.DateTimeField()
    # blank=True 允许为空
    excerpt = models.CharField(max_length=200, blank=True)
    # 这是分类与标签,分类与标签的模型我们已经定义在上面。
    # 我们在这里把文章对应的数据库表和分类、标签对应的数据库表关联了起来,但是关联形式稍微有点不同。
    # 我们规定一篇文章只能对应一个分类,但是一个分类下可以有多篇文章,所以我们使用的是 ForeignKey,即一对多的关联关系。
    # 而对于标签来说,一篇文章可以有多个标签,同一个标签下也可能有多篇文章,所以我们使用 ManyToManyField,表明这是多对多的关联关系。
    # 同时我们规定文章可以没有标签,因此为标签 tags 指定了 blank=True。
    # 如果你对 ForeignKey、ManyToManyField 不了解,请看教程中的解释,亦可参考官方文档:
    # https://docs.djangoproject.com/en/1.10/topics/db/models/#relationships

    # on_delete指定了当删除外键指向的主键记录时,系统的行为
    # CASCADE  删除主键时 把层层依赖的数据库记录全部删除
    # PROTECT 删除时  发现有关联 不允许删除
    # SET_NULL 删除之后 关联的字段 值设置为null  前提是关联表的主键允许为空blank=True, null=True
    category = models.ForeignKey(Category, on_delete=models.PROTECT)
    tags = models.ManyToManyField(Tag, blank=True)
    # 文章作者,这里 User 是从 django.contrib.auth.models 导入的。
    # django.contrib.auth 是 Django 内置的应用,专门用于处理网站用户的注册、登录等流程,User 是 Django 为我们已经写好的用户模型。
    # 这里我们通过 ForeignKey 把文章和 User 关联了起来。
    # 因为我们规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关联关系,和 Category 类似。
    author = models.ForeignKey(User, on_delete=models.PROTECT)

此处CharField对应数据库中的varchar类型,还有其他的类型,可以自己了解一下,都和数据库中的类型一一对应。

注意:

  • 表名由Django自动生成,默认格式为“项目名称+下划线+小写类名”,你可以重写这个规则。
  • Django会自动创建自增主键id,当然,你也可以自己指定主键。
  • 上面的SQL语句基于PostgreSQL语法。

生成表

创建模型之后,使用之前需要在settings文件中注册一下models.py所属的app,注册的方式在前面说过,这里不再赘述。

执行下面两个命令 ,就会去blog模块中去查找是否有models相关的修改。

1.makemigrations生成迁移脚本文件

$ python manage.py makemigrations  会自动检测哪些模型发生了修改会自动生成脚本

2.映射到数据库 , 第一次执行,会把Django默认install_apps中的其他表也映射进去

$ python manage.py migrate

如果创建好的model需要修改,增加字段等,则需要重新执行上面两条命令,每一次修改,都会在migrations文件夹中生成一个新的python文件,里面记录了每次的修改内容

你也可以运行下面的命令看看,执行了上面两条命令之后,Django 究竟为我们做了什么:

python manage.py sqlmigrate blog 0001

除此之外,你会发现项目根目录中,还增加了一个文件db.sqlite3,这是因为我们使用了python内置的sqlite3数据库,sqlite3是一个很轻量级的数据库,它仅有一个文件。数据库相关的配置在settings.py中

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

当然你也可以使用MySQL数据库,这个我们后面再说。暂时做一个简单的小项目,sqlite3也够用了。

模型介绍

上面创建模型中,使用了很多字段,定义不同类型的属性,还可以为模型定义方法,下面都来简单介绍一下。

字段

模型中的每一个字段都是Field类的实例,字段决定了数据库表中,改列的数据类型。

常用字段类型
  1. BooleanField 布尔值类型,默认None。
  2. CharField字符串类型,是最常用的类型,参数max_length是个必填参数,表示字符串长度。
  3. DateField日期类型
  4. DateTimeField日期时间类型,比DateField多了时分秒
  5. EmailField邮箱类型,可以使用Django内置的邮箱合法性校验。
  6. FileField上传文件类型
  7. IntegerField整数类型,也是最常用的类型
  8. TextField文本类型,用于存储大量的文本内容。

方法和属性

模型的方法,就是python类的实例方法。对于一些对数据库单条数据进行的重复的操作,我们可以为模型添加自定义的方法。

除了定义方法,还可以为模型定义属性,这里的属性和上面说的,每一个属性就是数据库表中的一列是有区别的,这里说的属性,不会作为表中的一列,只是作为这个python类的一个属性。

下面贴一段示例代码给大家做参考,理解模型方法和属性的定义:

class Category(models.Model):
   
    name = models.CharField(max_length=100)

    # 是否是热门分类
    # 实例以执行函数的方式调用
    def name_is_hot(self):
        if self.name == 'hot':
            return True
        else:
            return False

    # 实例以属性的方式调用
    @property
    def full_name(self):
        "Return the category full name"
        return '%s %s' % ('full', self.name)

    def __str__(self):
        return self.name + self.full_name

关系类型字段

Django还定义了一组关系类型字段,用来表示模型和模型之间的关系。

一对多

常见的外键,就是一对多的关系。下面用客户,药品和订单的表来说明一下一对多的表关系。

class Customer(models.Model):
    # 客户名称
    name = models.CharField(max_length=200)

    # 联系电话
    phonenumber = models.CharField(max_length=200)

    # 地址
    address = models.CharField(max_length=200)

    # qq  可以为空 可以是空字符串
    qq = models.CharField(max_length=100, null=True, blank=True)


# 药品
class Medicine(models.Model):
    name = models.CharField(max_length=200)
    sn = models.CharField(max_length=200)
    desc = models.CharField(max_length=200)

而订单中,一个客户可以有多个订单,客户做订单表中的外键。如果一个表中的某个字段是外键,那么,这个外键字段的记录的取值,只能是他关联的表的主键某个记录的值。

上面定义的两个表中看到其实并没有定义主键,在执行migrate的时候Django会为该Model对应的数据库表自定生成一个id字段,作为主键。

设计Order表,migrate后会看到数据库中的字段名为customer_id 外键关联Customer表

class Order(models.Model):
    name = models.CharField(max_length=200, null=True, blank=True)

    create_date = models.DateField(default=datetime.datetime.now)
    # 外键  on_delete指定了当删除外键指向的主键记录时,系统的行为
    # CASCADE  删除主键时 把层层依赖的数据库记录全部删除
    # PROTECT 删除时  发现有关联 不允许删除
    # SET_NULL 删除之后 关联的字段 值设置为null  前提是关联表的主键允许为空blank=True, null=True
    customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
一对一

一对一的关系就比较容易理解了。比如Django默认生成的用户表auth_user,当我们为了业务而需要对用户信息进行扩展的时候,可以在auth_user表中扩展字段,也可以依赖于auth_user表再创建一张新的表,比如sales销售员表,而每个销售员都是一个auth_user,所以两张表是一对一的关系,而销售员相关的其他信息,仅存在sales表中就可以了,不需要修改auth_user表的原始结构。

一对一的结构,其实本质还是外键,Django中用OneToOneField表示一对一的外键关系。

多对多

除了上面的两种,还有多对多的关系。上面例子中,订单和药品的关系就是多对多的关系。

class Order(models.Model):
    name = models.CharField(max_length=200, null=True, blank=True)

    create_date = models.DateField(default=datetime.datetime.now)
    # 外键  on_delete指定了当删除外键指向的主键记录时,系统的行为
    # CASCADE  删除主键时 把层层依赖的数据库记录全部删除
    # PROTECT 删除时  发现有关联 不允许删除
    # SET_NULL 删除之后 关联的地方 值设置为null
    customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
    # 多对多的关系
    medicines = models.ManyToManyField(Medicine, through='OrderMedicine')


class OrderMedicine(models.Model):
    order = models.ForeignKey(Order, on_delete=models.PROTECT)
    medicine = models.ForeignKey(Medicine, on_delete=models.PROTECT)
    # 订单中药品数量
    amount = models.PositiveIntegerField()

Django中多对多的关系,其实是通过另外一张表来实现的,就是through参数指定的表。如果不指定through参数,系统也会自定生成一张表来管理多对多的关系。我们这里指定了一张表OrderMedicine来实现,可以再添加新的字段供业务使用。

字段参数

模型的字段都可以接受一些参数,有些是某个字段类型特有的,下面这些是所有字段都可以设置的,下面列了一些常用的供大家参考:

  • null 在数据库中用null保存空值
  • blank 字段允许为空
  • choices 类似枚举
  • default 字段的默认值
  • primary_key 主键,会自动生成,也可以自己设置

元数据Meta

模型的元数据,是除了定义的字段外其他的内容,比如排序方式,数据库表名等。

class Tag(models.Model):
    name = models.CharField(max_length=100)
		created_time = models.DateTimeField()
     class Meta:
        ordering = ['-created_time']
  • ordering 最常用的元数据之一,指定该模型生成的所有对象的排序方式,如果在字段名前加上字符“-”则表示按降序排列,如果使用字符问号“?”表示随机排列。

  • db_table 指定该模型在数据库中生成的表名

操作数据库

常见问题合集

在操作数据库是可能遇到的问题,都记录在这里,以后还会持续更新~

  1. Django 报错django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module

NameError: name '_mysql' is not defined

python3 中django连接mysql使用的包是pymysql, 所以第一步先安装 pymysql pip install pymysql, 安装了并不代表就可以了, 还需要在项目的__init__.py添加以下代码:

import pymysql
pymysql.install_as_MySQLdb()

** mysql_config not found **

解决方法

执行:

ln -s /usr/local/mysql/bin/mysql_config /usr/local/bin/mysql_config

原因:

找不到mysql_config一般是由于通过lnmp.org或者其他方式安装mysql以后mysql_config是在/usr/local/mysql/bin/里面,这里面的文件不是在任意位置都可以访问的,而指令是

将mysql_config链接到/usr/local/bin目录下

  1. 错误:Forbidden (403) CSRF verification failed. Request aborted.

You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.

If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for “same-origin” requests.

解决方案

缺省创建的项目,Django会启用一个CSRF的安全防护机制,防止伪造跨站请求,这样所有的post,put类型的请求都必须在HTTP请求头中鞋带用于校验的数据,这里我们学习为了简单,先取消掉CSRF的校验机制,在配置文件settings.pyMIDDLEWARE配置项里注释掉django.middleware.csrf.CsrfViewMiddleware即可。

读取数据库数据

继续依赖前面创建的表Customer,我们来实现一个功能:浏览器访问sales/customers/,我们服务端返回系统中所有的customer记录给浏览器。

首先,先来实现一个函数,来处理sales/customers/的访问请求,返回数据库中表Customer的所有记录。

def listcustomers(request):
    # 返回一个QuerySet对象,包含所有的表记录
    # 每条记录都是一个dict对象
    qs = Customer.objects.values()

    retStr = ''
    for customer in qs:
        for name,value in customer.items():
            retStr += f'{name}:{value} |'
        retStr += '<br>'
    return HttpResponse(retStr)

继续升级一下需求,如果URL中带了一个参数,要如何获取呢?http://127.0.0.1:8000/sales/customers/?phonenumber=18970992909

def listcustomers(request):
    # 返回一个QuerySet对象,包含所有的表记录
    # 每条记录都是一个dict对象
    qs = Customer.objects.values()

    # 检查URL中是否有参数
    ph = request.GET.get('phonenumber', None)

    if ph:
        qs = qs.filter(phonenumber=ph)

    retStr = ''
    for customer in qs:
        for name,value in customer.items():
            retStr += f'{name}:{value} |'
        retStr += '<br>'
    return HttpResponse(retStr)

session机制:

服务端在数据库中存储了一张session表,记录一次用户登录的相关信息。每次用户登录,调用login,就会产生一条session数据出入到session表中,除此之外,还会在登录成功的响应的消息头中,Set-Cookie中填入sessionid数据,就是session表中的session key字段。

Set-Cookie:sessionid=eruio3789r09fjoiuro2

客户端收到这个响应之后,会把sessionid存在客户端的cookie中,具体根据客户端不同的浏览器存的位置不同,这就是浏览器的工作了。随后访问服务端的时候,在HTTP请求消息中必须带上这些cookie数据。

Cookie:sessionid=eruio3789r09fjoiuro2

服务端收到该请求后,只需要到session表中查看是否有该sessionid对应的记录,就可以判断这个请求是否是前面已经登录过的用户发出的,如果不是,就可以拒绝服务,重定向HTTP请求到登录页面。

Token:

对session的支持,是Django缺省就支持的。

ORM对关联表的操作

反向查询

已经获取到一个Country对象,访问所有属于这个国家的学生,通过表model名转化为小写,后面加上_set来获取所有的反向外键关联对象。

cn = Country.objects.get(name='中国')
cn.student_set.all()

还可以在定义model的时候,外键字段使用related_name参数

class Student(models.Model):
    name = models.CharField(max_length=100)
    grade = models.PositiveSmallIntegerField()
    country = models.ForeignKey(Country, on_delete=models.PROTECT, related_name='students')

就可以更直观的进行反向查询了

cn = Country.objects.get(name='中国')
cn.students.all()

反向过滤

查找年级为1的学生所在的国家,使用distinct去重

Country.objects.filter(students__grade=1).values().distinct()

关联表插入

继续使用上面说的Order表,来处理添加订单的请求。这里我们添加一条订单数据,需要在两张表里(Order、OrderMedicine)添加记录。两种表的插入,就是需要操作数据库两次。上面我们一直默认数据库插入操作都是成功的,但是实际使用中,肯定会遇到插入失败的情况。

对于我们这里一条订单数据,插入两次数据库的操作,就可能会存在一次插入成功,第二次插入失败的情况,从而产生脏数据。了解数据库的同学肯定会知道,我们应该用数据库的事务机制来解决这个问题。

把一批数据库操作放在事务中,如果该事务中的任何一次数据库操作失败了,那么整个事务就会发生回滚,撤销前面的操作,数据库回滚到这次事务操作之前的状态。

下面我们看看Django是如何实现事务操作的呢?使用transaction.atomic()原子操作实现事务机制。

from django.db import transaction

def addorder(request):
    info = request.params['data']

    with transaction.atomic():
        new_order = Order.objects.create(
            name=info['name'],
            customer_id=info['customer_id']
        )
        batch = [OrderMedicine(order_id=new_order.id, medicine_id=mid, amount=1)
                 for mid in info['medicineids']]
        OrderMedicine.objects.bulk_create(batch)  # bulk_create创建多条数据
    return JsonResponse({'ret': 0, 'id': new_order.id})

查询操作

  1. exact:在底层会被翻译成’=‘
  2. iexact:在底层会被翻译成’LIKE‘
    • like和=大部分情况都是等价的,exact和iexact的区别就是like和=的区别
  3. QuerySet.query 可以用来查看ORM语句最终被翻译成的SQL语句,不能用在普通的ORM模型上,比如通过get来获取的,就不可以使用,通过filter等查询返回的数据就可以使用 4.contains: 使用大小写敏感的判断,某个字符串是否再指定的字段中出现,被翻译成LIKE BINARY %hello world% 5.icontains: 不区分大小写的判断,被翻译成LIKE %hello world%。在MySQL层面,like是不区分大小写的 6.contains和icontains在被翻译成SQL的时候使用的是’% %‘,只要整个字符串汇总出现了百分号中间的字符就能被找到,而exact翻译之后是没有百分号的,必须完全相等才会被找到。

常见问题: 'django.core.exceptions.ImproperlyConfigured: mysqlclient 1.4.0 or newer is required; you have 0.9.3.' 解决方案: init.py中: import pymysql pymysql.version_info = (1, 4, 13, "final", 0) pymysql.install_as_MySQLdb()

聚合函数

Avg:求平均值

聚合函数不能单独运行,需要放在一些可以执行聚合函数的方法下面去执行,比如aggregate,聚合函数执行完成后会给聚合函数的值命名,默认是field__+聚合函数名,比如下面的示例代码形成的名字是price_avg,名字可以自定义,示例代码如下

result = Book.objects.aggregate(price_avg=Avg("price")) # price_avg别名

aggregate会返回一个字典,key就是自定义的名字,或者聚合函数的默认名字,值是聚合函数得到的值

QuerySet API

模型.objects

这个对象是django.db.models.manager.Manager的对象,这个类是一个抽象类,他上面的所有方法都是从QuerySet上copy过来的。

QuerySet常用方法

filter

过滤

exclude

排除满足条件

annotate

将获取的字段值进行重命名

Student.objects.annotate(
    countryname='country__name',
    studentname='name'
).filter(grade=1, country__name='中国').values('studentname', 'countryname')
order_by

将查询结果按某个字段排序

values

表单

创建超级管理员

python manage.py createsuperuser

创建管理员,根据提示输入密码,邮箱,密码等,创建成功后查看数据库中的user表,就可以看到刚才创建的用户的数据。

浏览器中打开http://127.0.0.1:8000/admin/,可以看到Django内置的登录页面,输入刚才创建用户的用户名和密码可以成功登录。登录后就可以在后台操作添加新用户等操作了。

到这里你可能会好奇,为什么我们自己创建的表Customer没有显示在后台呢?怎么样可以在后台操作Customer表呢?其实也很简单,只需要修改配置文件common/admin.py,注册我们定义的Customer类

from django.contrib import admin
from .models import Customer

admin.site.register(Customer)

刷新http://127.0.0.1:8000/admin/,就可以看到操作`Customer`表的入口了。添加一条数据,再查看数据库有没有添加成功。

Django模板

HTTP请求的过程看明白了之后,我们可以再稍微进阶一下,对于移动端来说,其实已经够了,只需要响应数据,但是对于浏览器来说,可能还想展示个好看的页面,Django在这方面也做了很多工作,可以让开发者用简单的代码,快速展示出一个页面。那就是Django的模板系统。

首先在项目根目录中,有一个templates文件夹,这就是存放模板的目录了,在这个目录里创建一个index.html文件,然后在工程的settings.py中修改’DIRS': [], 为:'DIRS': [os.path.join(BASE_DIR, 'templates')],

渲染模板

django提供了两种渲染模板的方式,分别是render_to_stringrender用法如下:

from django.shortcuts import render
from django.template.loader import render_to_string

def index2(request):
    html = render_to_string("index.html")
    return HttpResponse(html)

def index3(request):
    return render(request, 'index.html')

页面肯定都不只是静态页面,所以还可以在渲染的时候通过context给模板传入参数

from django.shortcuts import render

def index(request):
    return render(request, 'index.html', context={
        'title': '我的博客',
        'welcome': '欢迎来到我的博客'
    })

那么在HTML的模板中,如何获取到这个入参呢?就是下面我们要说的模板变量。

模板变量

1.在模板中使用变量,需要将变量放到{{ 变量 }}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ welcome }}</h1>
</body>
</html>

2.访问变量属性,通过对象.属性名来访问,测试代码如下:

class Person(object):
    def __init__(self, username):
        self.username = username
        
def prjectIndex(request):
    p = Person('zhangsan')
    context = {
        'username': "xiaoming",
        'person': p,
        'personDic': {
            'username': 'hhh'

        },
        'persons': [
            'aaaa',
            'bbbbb'
        ]
    }
    return render(request, 'index.html', context=context)

HTML模板中获取方式如下:

<h1>{{ person.username }}</h1>

3.字典类型变量,通过字典.key访问,HTML模板中获取方式如下:

<h1>{{ personDic.username }}</h1>

4.列表或元组也是通过.访问,HTML模板中获取方式如下:

<h1>{{ persons.0 }}</h1>

if标签

除了上面说的从视图函数中获取变量,模板中还可以写一些逻辑代码。Django也为我们提供了一些逻辑标签。

if标签相当于python中的if语句,elif和else if对应,所有标签都需要在{% %}中进行包裹。 if标签中可使用==, !=, <=, >=, >, <, in, not in, is ,is not等判断符

   {% if age < 18  %}
        <p>未成年</p>
    {% elif age == 18 %}
        <p>满18</p>
    {% else %}
        <p>成年人了</p>
    {% endif %}

    {% if '鲁班' in heros %}
        <p>鲁班在</p>
    {% else %}
        <p>鲁班不在</p>
    {% endif %}

for标签

<!--   for标签的使用 遍历字典的keys -->
    <ul>
        {% for key in person.keys %}
        <li>{{ key }}</li>
        {% endfor %}
    </ul>

for...in...标签

for...in...标签中没有continue和break,这一点要注意

!--   for标签的使用 遍历字典的values -->
    <ul>
        {% for value in person.values %}
        <li>{{ value }}</li>
        {% endfor %}
    </ul>

<!--   for标签的使用 遍历字典的键值对 -->
    <ul>
        {% for key,value in person.items %}
        <li>{{ key }}/{{ value }}</li>
        {% endfor %}
    </ul>

过滤器

在DTL中不支持函数的调用形式(),不支持给函数传递参数,过滤器其实就是一个函数,并且可以接收参数(最多接收两个参数),过滤器左边和右边的都是参数

add

数字直接相加 列表合并 字符串拼接

    {{ "1"|add:"2" }}
    {{ value1|add:value2 }}

cut 剪切目标字符串

    {{ "hello world"|cut:" " }}

date 日期格式化

 {{ today|date:"Y/m/d" }}
    {{ today|date:"d-m-Y" }}
{#    n 月份1-9前面没有0 #}
    {{ today|date:"d-n-Y" }}
{#   j 天1-9前面没有0 #}
    {{ today|date:"j-n-Y" }}

    {{ today|date:"Y/m/d g" }}

    {{ today|date:"Y/m/d H:i" }}

模板继承

必须放在子模板中的第一行(前面可以加注释行)

    {% extends 'base.html' %}

只能加载block标签中的代码

    {% block content %}
        {{ block.super }}
        这是首页
    {% endblock %}

    block外面的代码 不加载不渲染

父模板中可以读取子模板中定义的变量