【Django】Django配置文件和设计模式详解

一、安装和卸载

安装:pip install django[==版本]
卸载:pip uninstall django

二、创建和启动项目

django-admin startproject 项目名称,如:
django-admin startproject project1

启动命令如下:

cd project1
python manage.py runserver [port]
port为可选,默认8000
如:python manage.py runserver 5000

创建好后,目录结构如下:

project1
	project1
		__init__.py
		settings.py
		urls.py
		wsgi.py
	manage.py

各文件作用如下:

  • manage.py:项目管理主程序,用于管理整个项目的开发运行的调式。
  • project1:项目包文件夹
    • __init__.py:包初始化文件,当项目包被导入(import)时此文件会自动运行。
    • settings.py:项目配置文件,启动服务时自动调用
    • urls.py:基础路由配置文件
    • wsgi.py:Web Server Gateway Interface,WEB服务网关接口,部署项目时使用

三、配置文件settings.py说明

  • BASE_DIR:自动生成的当前项目的绝对路径

  • DEBUG:True为调试模式,生产环境中设置为False

  • ALLOWED_HOSTS:允许访问该项目的地址列表,设置示例如下:

    • []:仅127.0.0.1和localhost可访问
    • [‘*’]:所有网络地址均可访问
    • [‘192.168.11.11’, ‘192.168.22.22’]:仅列表中的两个地址可访问
  • INSTALLED_APPS:应用列表

  • MIDDLEWARE:中间件

  • ROOT_URLCONF:根级url配置

  • TEMPLATES:模板配置

  • DATABASES:数据库配置

  • LANGUAGE_CODE:语言配置,中文"zh-Hans";英文"en-us"

  • TIME_ZONE:时区配置,“UTC"为格林威治时间,中国时区为"Asia/Shanghai”

四、Django框架模式

Django采用MTV设计模式,MTV和MVC设计模式区别如下:

  • MVC(Model-View-Controller)

    在这里插入图片描述

    • M模型层:主要用于数据库层的封装
    • V视图层:主要用于用户交互和界面展示
    • C控制层:处理请求、获取数据、返回数据
  • MTV(Model-Template-View)

    在这里插入图片描述

    • M模型层:主要用于数据库层的封装
    • T模板层:主要用于用户交互和界面展示
    • V视图层:处理请求、获取数据、返回数据

五、View视图层

5.1、视图函数view

  • 作用:接收浏览器请求,通过HttpResponse对象返回数据。

  • 创建视图函数:

    在项目包下(settings.py同级)下创建views.py

  • 视图函数编写:

    def xxx_view(request[, params]):
      return HttpResponse对象
    

5.2、路由配置urls

settings.py中指定了根路由位置:ROOT_URLCONF = ‘project1.urls’,客户端发送请求时。通过路由配置urls.py找到对应的视图函数处理并返回。urls.py初始配置文件如下:

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

需要配置路由时,只需要在urlpatterns加入响应的配置即可。

配置路由时,需要用到url()函数,url()描述了路由和视图函数的对应关系,用法如下:

from django.conf.urls import url
url(regex, view, kwargs=None, name=None)

'''
regex	字符串类型,匹配的请求路径,支持正则
view	该路由对应的视图函数名
name	地址的别名
'''

URL的格式如下:

protocol://hostname[:port]/path[?query][#fragment]

protocol:协议,如http、https
hostname:主机名
port:端口
path:路由地址
query:参数参数,多个参数用&隔开
fragment:信息片段

如:http://127.0.0.1:8000/test/page?name=aaa&passwd=bbb#xxx

5.2.1、不带分组的路由

该方式为最基本的路由函数,不带参数,直接通过路由找到视图函数。

5.2.2、带分组的路由

在regex参数中,用正则分组提取参数后,传递给视图函数。

5.2.3、带命名分组的路由

在regex中,为分组起一个别名:(?P),参数传递时,使用键值对传参。

示例如下:

urls.py:

from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^test1$', views.test1_view),
    url(r'^(\w+)/test2/(\w+)', views.test2_view),
    url(r'^test3/(?P<p1>\w+)/(?P<p2>\w+)',views.test3_view),
]

views.py:

from django.http import HttpResponse

def test1_view(request):
    html = '<h3>This is my first test page.</h3>'
    return HttpResponse(html)

def test2_view(request, str1, str2):
    # 路由中第一个分组传递给str1,第二个传递个str2
    html = '<h3>The recieved data is %s and %s</h3>' % (str1, str2)
    return HttpResponse(html)

def test3_view(request, p1, p2):
    # 参数名p1,p2需要和路由中的命名分组一致
    html = '<h3>The recieved data is %s and %s</h3>' % (p1, p2)
    return HttpResponse(html)

访问对应url,结果分别如下:

http://127.0.0.1:8000/test1
This is my first test page.

http://127.0.0.1:8000/abc/test2/def
The recieved data is abc and def

http://127.0.0.1:8000/test3/ab/cd
The recieved data is ab and cd

5.3、HttpRequest请求对象

参考:https://yiyibooks.cn/xx/Django_1.11.6/ref/request-response.html

视图函数的第一个参数是HttpRequest对象,服务器接收http协议请求后,会根据请求报文创建HttpRequest对象

5.3.1、对象常用属性

HttpRequest.scheme			字符串,表示请求方法,常为http或https
HttpRequest.body			字节串,HTTP请求正文
HttpRequest.path			请求页面的完整路径的字符串,如/test1
HttpRequest.path_info		URL字符串,如/test1
HttpRequest.method			请求使用的HTTP方法,大写,如GET,POST
HttpRequest.encoding		字符串,提交的数据的编码方式,None表示默认设置,可手动设置该属性置
HttpRequest.content_type	从CONTENT_TYPE头解析的请求的MIME类型的字符串,如:text/plain
HttpRequest.content_params	包含在CONTENT_TYPE标题中的键/值参数字典
HttpRequest.GET				类字典对象,含GET所有参数,如:<QueryDict:{'a':['1'],'b:['2']}>
HttpRequest.POST			类字典对象,含POST所有数据。
HttpRequest.FILES			类字典对象,含所有上传的文件。
HttpRequest.COOKIES			包含所有Cookie的字典。 键和值都为字符串。
HttpRequest.META			包含所有可用HTTP标头的字典。
HttpRequest.session			可读写的,类似字典的对象,表示当前会话。

5.3.2、对象常用方法

get_host()		主机和端口,如:127.0.0.1:8000
get_port()		字符串格式,端口,如:8000
get_full_path()	返回path,含查询字符串,如:/test1?a=1&b=2&c=3
build_absolute_uri()	返回绝对URI,如:http://127.0.0.1:8000/testrequest?a=1&b=2
is_secure()		如果请求安全(HTTPS),则返回True,否则False

5.4、QueryDict对象

request.GET和request.POST属性都是QueryDict对象的实例。QueryDict是字典的子类,实现了字典的所有标准方法。

QueryDict对象常用方法如下:

QueryDict['key']					key不存在,则报错
QueryDict.get(key,default=None)		key存在,则返回对应值,不存在,返回默认值
QueryDict.getlist(key,default=None)	key存在,返回值列表,不存在,返回默认值,默认为[]

5.5、HttpResponse对象

5.5.1、参数说明

继承自HttpResponseBase,用于向客户端返回响应内容,格式如下:

HttpResponse(content, content_type=None, status=None)

'''
content					响应体
content_type			返回数据的MIME类型,常用如下:
	text/html			默认,html文件
	text/plain			纯文本
	text/css			css文件
	text/javascript		js文件
	multipart/form-data	表单提交
	application/json	json传输
	application/xml		xml文件
status					状态码,默认200
'''

5.5.2、HttpResponse常用子类

子类						    	作用			状态码		
HttpResponseRedirect	         重定向		 302		
HttpResponseNotModified			 未修改		 304			  
HttpResponseBadRequest			 错误请求   	 400		
HttpResponseNotFound			 无资源		 404
HttpResponseForbidden 			 请求被进制		403
HttpResponseServerError			 服务器错误		500

六、Template模板层

模板的作用是根据视图函数中传递的字典数据,生成动态变化的html页面。

官方参考文档:https://docs.djangoproject.com/en/4.0/topics/templates/

6.1、模板创建

  • 在项目根路径下,创建templates文件夹

  • 修改配置文件settings.py:

    # 初始配置
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [],
            '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',
                ],
            },
        },
    ]
    
    '''
    BACKEND		模板引擎
    DIRS		模板搜索路径
    APP_DIRS	是否在应用的templates文件夹中搜索模板文件
    '''
    
    # 修改如下:
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')],
            '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',
                ],
            },
        },
    ]
    

6.2、模板传参

  • 方式一:通过loader加载

    from django.template import loader
    
    temp = loader.get_template('模板文件名')
    html = t.render(字典数据)
    return HttpResponse(html)
    
  • 方式二:使用render直接加载(常用)

    from django.shortcuts import render
    
    return render(request, '模板文件名', 字典数据)
    

6.3、模板语言

6.3.1、变量

{{ 变量名 }}			直接通过变量名获取变量
{{ 变量名.index }}		获取列表项,index从0开始
{{ 变量名.key }}		根据key,获取字典value
{{ 方法名 }}			调用方法
{{ 对象.方法名 }}	   调用类方法		    		

示例如下:

路由urls.py:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^testtemplate$', views.test_template_view)
]

视图views.py:

from django.shortcuts import render

def test_template_view(request):
    str1 = 'abc'
    lst1 = ['abc', 123]
    dic1 = {"key1": "value1", "key2": "value2"}
    func1 = say("hello")
    func2 = say()
    cls1 = Student().get_name('Lilei')
    cls2 = Student()
    return render(request, 'test_template.html', locals())

def say(p1="byebye"):
    return 'say ' + p1

class Student:
    def get_name(self, name='Gaohui'):
        return "My name is {}".format(name)

模板templates:test_template.html:

<h3>str1:{{ str1 }}</h3>
<h3>lst1:{{ lst1 }}, lst1[0]:{{ dic1.0 }}, lst1[1]:{{ lst1.1 }}</h3>
<h3>dic1:{{ dic1 }}, dic1['key1']:{{ dic1.key1 }}, dic1['key2']:{{ dic1.key2 }}</h3>
<h3>func1:{{ func1 }}, func2:{{ func2 }}</h3>
<h3>cls1:{{ cls1 }}, cls2:{{ cls2.get_name }}</h3>

运行结果如下:

在这里插入图片描述

6.3.2、标签

标签的作用是在模板中可以使用服务端的一些功能,如判断,循环等。

参考:https://docs.djangoproject.com/en/4.0/ref/templates/builtins/#ref-templates-builtins-tags

6.3.2.1、if标签

语法:

{% if 条件1 %}
...
{% elif 条件2 %}
...
{% else %}
...
{% endif %}

注意:if标签中使用()无效。

6.3.2.2、for标签

语法:

{% for 变量 in 可迭代对象 %}
	...
{% empty %}
	可迭代对象为空时执行的语句
{% endfor %}

内置变量forloop:

forloop.counter		从1开始计数的当前迭代
forloop.counter0	从0开始计数的当前迭代
forloop.revcounter	从len开始降序计数的当前迭代
forloop.revcounter0	从len-1开始降序计数的当前迭代
forloop.first		第一次循环,为真,否则为假
forloop.last		最后一次循环,为真,否则为假
forloop.parentloop	嵌套循环的外层循环

示例如下:

视图函数:

def test_label_view(request):
    lst1 = ['东邪', '西毒', '南帝', '北丐']
    lst2 = []
    return render(request, 'test_label.html', locals())

模板html:

<h3>the lst1 is:{{ lst1 }}</h3>

{% for i in lst2 %}
<h3>{{ forloop.counter0 }}:{{ i }}</h3>
{% empty %}
<h3>lst2为空</h3>
{% endfor %}

{% for i in lst1 %}
{% if forloop.first %}
<h3>#################</h3>
{% endif %}
<h3>{{ forloop.counter }}   {{ i }}</h3>
{% if forloop.last %}
<h3>$$$$$$$$$$$$$$$$$</h3>
{% endif %}
{% endfor %}

运行结果:

在这里插入图片描述

6.3.2.3、extends和block标签
  • extends标签:子模版继承父模板,生成和父模板完全相同的一个页面,一般写在第一行。语法如下:

    {% extends '父模板名称' %
    
  • block标签:在父模板中定义可由子模板覆盖的块,语法如下:

    {% block block_name %}
    	该部分内容可以被子模块中的同名模块覆盖
    {% endblock block_name %}
    
6.3.2.4、url反向解析标签

在urls.py中,定义路由时,可以指定name参数,给url起一个别名,在模板中,可以通过该别名反向解析出url路径。语法如下:

{% url '别名' %}
{% url '别名' '参数1' '参数2' %}

示例:一个网站包含注册登录栏、导航栏、内容栏、和网页底部;主页和导航栏中的条目整体页面使用相同的框架,具体页面内容不同,此时即可使用模板继承和覆盖,如下:

路由urls.py:

from django.conf.urls import url
from django.contrib import admin
from . import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', views.index_view),
    url(r'^sport$', views.sport_view),
    url(r'^music$', views.music_view, name='music'),
    url(r'^page(\d+)$', views.pagen_view, name='pagen')
]

视图views.py:

from django.http import HttpResponse
from django.shortcuts import render

def index_view(request):
    return render(request, 'base.html')

def sport_view(request):
    return render(request, 'sport.html')

def music_view(request):
    return render(request, 'music.html')

def pagen_view(request, n):
    return HttpResponse('第' + n + '页')

模板:

base.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    {% block page_title %}
    <title>主页</title>
    {% endblock page_title %}
</head>
<body>
    <h1 style="background-color:blue;width:300px">注册,登录</h1>
    <a href="/">主页</a>
    <a href="/sport">体育</a>
    <a href="{% url 'music' %}">音乐</a>

    {% block page_info %}
    <h2 style="background-color:orange;width:300px;height:200px">网站主页</h2>
    <a href="{% url 'pagen' 1 %}">第一页</a>
    <a href="{% url 'pagen' 2 %}">第二页</a>
    {% endblock page_info %}

    <h1 style="background-color:gray;width:300px">联系我们</h1>
</body>
</html>

sport.html:

{% extends 'base.html' %}

{% block page_title %}
<title>体育</title>
{% endblock page_title %}

{% block page_info %}
<h2 style="background-color:green;width:300px;height:200px">体育专题页</h2>
{% endblock page_info %}

music.html:

{% extends 'base.html' %}

{% block page_title %}
<title>音乐</title>
{% endblock page_title %}

{% block page_info %}
<h2 style="background-color:red;width:300px;height:200px">音乐专题页</h2>
{% endblock page_info %}

说明:在base.html中定义了两个block:page_title和page_info,这两个block在子模板中可以用同名block替换掉,起到部分继承主页面的效果。pagen使用了url反向解析,通过反向解析标签{% url ‘pagen’ 1 %}定位到url中name='pagen’的路由,并完成参数传递。运行结果如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.3.2.5、static标签

作用:访问静态文件。静态文件是指不和服务器做动态交互的文件,如图片、视频、静态html文件等等。

访问静态文件有两种方式,一是通过{% static %}标签访问,二是通过STATIC_URL访问。

先在项目根目录下建static文件夹,在settings.py中做如下配置:

STATIC_URL = '/static/'							# 静态文件url地址
STATICFILES_DIRS =(
    os.path.join(BASE_DIR, 'static'),			# 静态文件存储路径
)
  • 通过路径访问:

    <img src="/static/xxx.jpg" alt="">
    <img src="http://127.0.0.1:8000/static/吴建剑.jpg" alt="">
    
  • 通过{% static %}标签访问:

    {% load static %}
    {% static '静态资源路径' %}
    

    如:

    {% load static %}
    <img src="{% static 'xxx.jpg' %}" alt="">
    
6.3.2.6、comment标签

标签中的内容被注释,用法如下:


{% comment %}
    ...
{% endcomment %}

6.3.3、过滤器

参考文档:https://docs.djangoproject.com/en/4.0/ref/templates/builtins/#ref-templates-builtins-filters

在变量输出之前对其进行处理。语法格式如下:

{{ 变量|过滤器1:参数1|过滤器2:参数2... }}

常用过滤器如下:

add				将参数和值相加,如:{{ value|add:"2" }},4->6
addslashes		在引号前添加斜杠,如:{{ value|addslashes }},"I'm using"->"I\'m using"
capfirst		将值的第一个字符大写,如:{{ value|capfirst }},"django"->"Django"
...
详见参考文档。

七、应用app

一个项目可以包含多个应用,每个应用都是一个独立的模块,有自己的视图、路由、模板和模型。通过app的使用,可以降低模块之间的耦合性。

7.1、创建应用

首先,用如下命令创建一个应用:

python manage.py startapp 应用名
如:
python manage.py startapp movie

然后在settings.py中配置应用:

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

创建完成后,会在项目根目录下生成一个应用包。

7.2、应用的分布式路由

每个应用都可以有自己的路由,由基础路由配置文件分发到应用,应用自行处理各自的请求。基础路由可以通过include函数将路由转发到各个应用进行分布式处理。函数格式如下:

from django.conf.urls import include
include('应用名.url模块名')

如:新建两个应用movie和music,并为两个应用各自单独建一个urls.py路由文件。基础路由中配置如下:

url(r'^movie/', include('movie.urls')),
url(r'^music/', include('music.urls')),

movie应用:

urls.py:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^index$', views.index),
]

views.py:

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

def index(request):
    return HttpResponse("this is movie's index.")

music应用:

urls.py:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^index$', views.index),
]

views.py:

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

def index(request):
    return HttpResponse("this is music's index.")

前端访问url:

http://127.0.0.1:8000/movie/index
http://127.0.0.1:8000/music/index

显示结果分别如下:

this is movie's index.
this is music's index.

八、Model模型层

模型层主要用于数据库层的封装,通过封装可以以类、对象的方式来操作数据库,避免了通过sql语句直接操作。

8.1、ORM映射

ORM框架全称Object Relational Mapping,对象关系映射。通过ORM映射可以建立模型类和数据表之间的映射关系。对数据库的操作都转化为对类和对象的操作,可以避免不同数据库之间操作的差异。

ORM映射关系如下:

类			数据表
属性		   字段
对象	   	   数据行

模型类继承自django.db.models.Model。

以MySQL数据库为例,需先安装 pymysql包:pip install pymysql

  • 第一步:注册一个app:movie

  • 第二步:创建数据库:

     create database myweb character set utf8 collate utf8_general_ci;	-- 大小写不敏感
    
  • 第三步:配置settings.py:

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'myweb',
            'USER': 'xxx',
            'PASSWORD': 'xxx',
            'HOST': '127.0.0.1',
            'PORT': 3306,
        }
    }
    
  • 第四步:配置项目的__init__.py:

    import pymysql
    pymysql.install_as_MySQLdb()
    
  • 第五步:在app中添加模型类:movie/models.py

    from django.db import models
    
    # Create your models here.
    
    class Movie(models.Model):
        name = models.CharField(max_length=30, verbose_name='电影名')
        country = models.CharField(max_length=30, verbose_name='国家')
        price = models.DecimalField('票价', max_digits=6, decimal_places=2, default=0.0)
    
  • 第六步:数据库迁移

    数据库迁移的作用是将模型类同步到数据库。在app下有一个mi’grations的文件夹,用来存放迁移文件。迁移分两步完成,首先生成/更新迁移文件;然后再执行迁移脚本,完成迁移。

    生成迁移文件:

    python manage.py makemigrations
    
    提示信息如下:
    Migrations for 'movie':
      movie\migrations\0001_initial.py
        - Create model Movie
        
    查看movie/migratins文件夹下成功生成了迁移文件:
    0001_initial.py
    

    此时数据库中还未真正的创建表,需要执行迁移脚本:

    python manage.py migrate
    
    提示信息如下:
    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, movie, sessions
    Running migrations:
      Applying contenttypes.0001_initial... OK
      Applying auth.0001_initial... OK
      Applying admin.0001_initial... OK
      Applying admin.0002_logentry_remove_auto_add... OK
      Applying admin.0003_logentry_add_action_flag_choices... OK
      Applying contenttypes.0002_remove_content_type_name... OK
      Applying auth.0002_alter_permission_name_max_length... OK
      Applying auth.0003_alter_user_email_max_length... OK
      Applying auth.0004_alter_user_username_opts... OK
      Applying auth.0005_alter_user_last_login_null... OK
      Applying auth.0006_require_contenttypes_0002... OK
      Applying auth.0007_alter_validators_add_error_messages... OK
      Applying auth.0008_alter_user_username_max_length... OK
      Applying auth.0009_alter_user_last_name_max_length... OK
      Applying auth.0010_alter_group_name_max_length... OK
      Applying auth.0011_update_proxy_permissions... OK
      Applying auth.0012_alter_user_first_name_max_length... OK
      Applying movie.0001_initial... OK
      Applying sessions.0001_initial... OK
    

    迁移完成后,可以查看表已生成,生成的表名默认为app名_类名:

    mysql> desc movie_movie;
    +---------+--------------+------+-----+---------+----------------+
    | Field   | Type         | Null | Key | Default | Extra          |
    +---------+--------------+------+-----+---------+----------------+
    | id      | bigint       | NO   | PRI | NULL    | auto_increment |
    | name    | varchar(30)  | NO   |     | NULL    |                |
    | country | varchar(30)  | NO   |     | NULL    |                |
    | price   | decimal(6,2) | NO   |     | NULL    |                |
    +---------+--------------+------+-----+---------+----------------+
    

8.2、字段类型和数据类型对应

参考官方文档:https://docs.djangoproject.com/en/4.0/ref/models/fields/#model-field-types

常见的对应关系如下:

BooleanField(**options)											tinyint(1)
CharField(max_length=None, **options)							varchar
DateField(auto_now=False, auto_now_add=False, **options)		date
	auto_now:保存对象时,自动设置当前时间
	auto_now_add:创建对象时,自动设置当前时间
DateTimeField(auto_now=False, auto_now_add=False, **options)	datetime
DecimalField(max_digits=None, decimal_places=None, **options)	decimal
	max_digits:总位数
	decimal_places:小数位数
EmailField(max_length=254, **options)							varchar
FloatField(**options)											double
IntegerField(**options)											int
TextField(**options)											longtext
TimeField(auto_now=False, auto_now_add=False, **options)		time
URLField(max_length=200, **options)								varchar(200)

8.3、字段选项

null						默认False,True时表示该字段可以为空
blank						默认False,True时表示该字段可以为空
db_column					列名,未指定时使用属性名作为列名
db_index					默认False,True时表示为该字段创建索引
default						设置默认值,null=False时建议加上
primary_key					True时表示设置该字段为主键
unique						True时,表示该字段不可重复
verbose_name				别名,在admin上以该名称显示

8.4、数据库操作

数据库的基本操作通过管理器对象来实现。继承自models.Model的模型类,都会继承一个objects对象,这个对象就是管理器对象。

数据库的CRUD可以通过模型的管理器对象来实现。

8.4.1、创建Create

  • 方式1:通过管理器对象创建

    from movie import models
    models.Movie.objects.create(name='十面埋伏',country='中国')
    
  • 方式2:创建Movie实例对象,调用save()保存

    from movie import models
    mv = models.Movie(name='月光宝盒',country='中国')
    mv.save()
    

8.4.2、查询Read

8.4.2.1、查询方法
  • all():查询全量数据,返回QuerySet数组
  • get():查询指定条件的单一数据,返回结果对象,满足条件的数据不存在或超过一条都会报错
  • filter():查询符合条件的多条记录,返回QuerySet数组
  • exclude():查询不符合条件的全部记录,返回QuerySet数组
  • values():查询返回指定字段(字典)。objects对象和QuerySet均支持该属性,结果对象不支持
  • values_list():查询返回指定字段(元素)。objects对象和QuerySet均支持该属性,结果对象不支持
  • order_by():排序查询,默认升序,降序在字段前加‘-’。objects对象和QuerySet支持该属性,结果对象不支持

示例如下:

数据库中初始共3条数据:

+----+----------+---------+-------+--------+
| id | name     | country | price | isOpne |
+----+----------+---------+-------+--------+
|  1 | 十面埋伏 | 中国    |  0.00 |      0 |
|  2 | 大圣娶亲 | 中国    |  0.00 |      1 |
|  3 | 月光宝盒 | 中国    |  0.00 |      0 |
+----+----------+---------+-------+--------+
from movie import models

mvs = models.Movie.objects.all()
# <QuerySet [<Movie: Movie object (1)>, <Movie: Movie object (2)>, <Movie: Movie object (3)>]>

for mv in mvs:
    print(mv.name, mv.country)
# 十面埋伏 中国
# 大圣娶亲 中国
# 月光宝盒 中国

models.Movie.objects.get(id=1)
# <Movie: Movie object (1)>

models.Movie.objects.filter(id__gt=1)
# <QuerySet [<Movie: Movie object (2)>, <Movie: Movie object (3)>]>

models.Movie.objects.exclude(id__gt=1)
# <QuerySet [<Movie: Movie object (1)>]>

mvs = models.Movie.objects.values('id','name')
# <QuerySet [{'id': 1, 'name': '十面埋伏'}, {'id': 2, 'name': '大圣娶亲'}, {'id': 3, 'name': '月光宝盒'}]>

for mv in mvs:
    print(mv["name"], mv["id"])
# 十面埋伏 1
# 大圣娶亲 2
# 月光宝盒 3

models.Movie.objects.filter(id__gt=2).values('id','name')
# <QuerySet [{'id': 3, 'name': '月光宝盒'}]>

models.Movie.objects.get(id__exact=1).values('id','name')
# 报错:AttributeError: 'Movie' object has no attribute 'values'

models.Movie.objects.filter(id__exact=1).values_list('id','name')
# <QuerySet [(1, '十面埋伏')]>

models.Movie.objects.filter(id__gt=1).order_by('-id')
# <QuerySet [<Movie: Movie object (3)>, <Movie: Movie object (2)>]>

models.Movie.objects.order_by('-id')
# <QuerySet [<Movie: Movie object (3)>, <Movie: Movie object (2)>, <Movie: Movie object (1)>]>
8.4.2.2、查询谓词

描述查询条件。如下:

id_exact=1				id=1
name__contains='ab'		name like '%ab%'		
name__startswith('ab')	name like 'ab%'
name__endswith('ab')	name like '%ab'
id__gt=1				id>1
id__gte=1				id>=1
id__lt=1				id<1
id__lte=1				id<=1
id__in=[1,2]			id in (1,2)
id__range=[10,20]		id between 10 and 20

8.4.2.3、聚合查询

聚合函数包含:Sum、Avg、Count、Max、Min。

  • 不带分组的聚合

    查询语法如下:

    models.模型类名.objects.aggregate(结果变量名=聚合函数('字段'))
    
    # 返回值为结果变量名和值组成的字典。
    
  • 分组聚合

    查询语法如下:

    models.模型类名.objects.values('字段').annotate(结果变量名=聚合函数('字段'))
    models.模型类名.objects.values_list('字段').annotate(结果变量名=聚合函数('字段'))
    
    # 返回值为QuerySet
    
    

示例如下:

数据库数据:

+----+----------+---------+-------+--------+
| id | name     | country | price | isOpne |
+----+----------+---------+-------+--------+
|  1 | 十面埋伏 | 中国    | 50.00 |      0 |
|  2 | 大圣娶亲 | 中国    | 60.00 |      1 |
|  3 | 月光宝盒 | 中国    | 70.00 |      0 |
|  4 | 蝴蝶效应 | 美国    | 35.00 |      0 |
|  5 | 黑客帝国 | 美国    | 45.00 |      0 |
+----+----------+---------+-------+--------+
from movie import models

models.Movie.objects.aggregate(avgPrice=Avg('price'))
# {'avgPrice': Decimal('52.000000')}

models.Movie.objects.values_list('country').annotate(max_price=Max('price'))
# <QuerySet [('中国', Decimal('70.00')), ('美国', Decimal('45.00'))]>

models.Movie.objects.values('country').annotate(max_price=Max('price'))
# <QuerySet [{'country': '中国', 'max_price': Decimal('70.00')}, {'country': '美国', 'max_price': Decimal('45.00')}]>

8.4.3、更新Update

  • 单条更新:通过get查出结果对象,对结果对象进行更新

    from movie import models
    mv = models.Movie.objects.get(id__exact=1)
    mv.price=80
    mv.isOpne=1
    mv.save()
    
  • 批量更新:通过filter查出QuerySet对象,对其更新,或者针对objects对象,更新所有值:

    from movie import models
    mvs = models.Movie.objects.filter(id__gte=3)
    mvs.update(price=50)						# 将id>=3的记录price字段更新为50
    
    models.Movie.objects.update(price=35)		# 将所有记录的price更新为35
    

8.3.5、删除Delete

不论是单条,还是结果集,都用delete()方法删除,objects对象无delete方法。

示例如下:

models.Movie.objects.get(id__exact=1).delete()		# 删除id为1的记录
models.Movie.objects.filter(id__gt=3).delete()		# 删除id>3的记录
models.Movie.objects.all().delete()					# 删除全部记录				

8.5、F对象和Q对象

8.5.1、F对象

F对象用于不获取字段值的情况下操作字段值,或两个字段间的比较。

用法如下:

F('列名')

如:

from django.db.models import F
from movie import models

# 将所有电影价格下降70%
models.Movie.objects.all().update(price=F('price')*0.7)

8.5.2、Q对象

在条件中实现或、与、非操作,语法如下:

Q(条件1)|Q(条件2)		条件1或条件2成立
Q(条件1)&Q(条件2)		条件1和条件2都要成立
~Q(条件1)				  条件1不成立

示例如下:

from django.db.models import Q

models.Movie.objects.filter(Q(country__exact='中国')|Q(price__gt=45))
# 查找country为中国,或price>45的记录

models.Movie.objects.filter(Q(country__exact='美国')&Q(price__lt=45))
# 查找country为美国,且price小于45的记录

models.Movie.objects.filter(~Q(country__exact='美国'))
# 查找country不为美国的记录

8.6、原生数据库语句

8.6.1、查询

使用objects的raw()方法,返回RawQuerySet对象。示例如下:

from movie import models

mvs = models.Movie.objects.raw("select * from movie_movie where country='中国'")
# <RawQuerySet: select * from movie_movie where country='中国'>

for mv in mvs:
    print(mv.name, mv.price)
# 霸王别姬 24.50
# 藏龙卧虎 21.00
# 英雄本色 28.00

8.6.2、增删改

增删改通过游标cursor实现。游标在django.db.connection包中,使用前需先导入,示例如下:

from django.db import connection

connection.cursor().execute('update movie_movie set price=80 where id=10')
# 更新id=10的记录price为80

为防止出现异常未释放cursor资源,一般用with语句进行操作:

from django.db import connection

with connection.cursor() as c:
	c.execute('delete from movie_movie where id=10')
    # 删除id=10的记录。

8.7、关联关系映射

关联关系映射分为1对1,1对多,多对多。

8.7.1、一对一映射

一对一表示两个表的对象有唯一的对应关系,如一个丈夫对应一个妻子。一对一通过models.OneToOneField()实现,具体语法如下:

class M1(models.Model):
	xxx
    
class M2(models.Model):
    xxx
    attr = models.OneToOneField(M1)

示例:

在应用music的models.py下建两个类,一个是Musician,一个是Spouse,二者为1对1关系。

from django.db import models

# Create your models here.

class Musician(models.Model):
    name = models.CharField('音乐家', max_length=30)
    class Meta:
        db_table = 'musician'		# 内置类,将表名修改为musician

class Spouse(models.Model):
    name = models.CharField('配偶', max_length=30)
    musician = models.OneToOneField(Musician, on_delete=models.CASCADE)
    class Meta:
        db_table = 'spouse'

完成数据迁移后,查看表结构:

mysql> desc musician;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | bigint      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(30) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+

mysql> desc spouse;
+-------------+-------------+------+-----+---------+----------------+
| Field       | Type        | Null | Key | Default | Extra          |
+-------------+-------------+------+-----+---------+----------------+
| id          | bigint      | NO   | PRI | NULL    | auto_increment |
| name        | varchar(30) | NO   |     | NULL    |                |
| musician_id | bigint      | NO   | UNI | NULL    |                |
+-------------+-------------+------+-----+---------+----------------+

数据插入:

from music import models

mus1 = models.Musician.objects.create(name='周杰伦')
mus2 = models.Musician.objects.create(name='陈奕迅')
mus3 = models.Musician.objects.create(name='王心凌')

sp1 = models.Spouse.objects.create(name='昆凌',musician=mus1)
sp2 = models.Spouse.objects.create(name='陈夫人',musician=mus2)

查看插入结果:

mysql> select * from musician;
+----+--------+
| id | name   |
+----+--------+
|  1 | 周杰伦 |
|  2 | 陈奕迅 |
|  3 | 王心凌 |
+----+--------+

mysql> select * from spouse;
+----+--------+-------------+
| id | name   | musician_id |
+----+--------+-------------+
|  1 | 昆凌   |           1 |
|  3 | 陈夫人 |           2 |
+----+--------+-------------+

数据查询:

通过musician查spouse,查询方式为:实例对象.映射对象小写类名:

mus1 = models.Musician.objects.get(name='陈奕迅')
mus2 = models.Musician.objects.get(name='王心凌')

print(mus1.spouse.name)
# 陈夫人
print(mus2.spouse.name)
# 无关联对象时,会抛异常:RelatedObjectDoesNotExist

通过spouse查musician,查询方式为:实例对象.属性名:

sp = models.Spouse.objects.get(name='陈夫人')

print(sp.musician.name)
# 陈奕迅

8.7.2、一对多映射

一对多映射表示一个对象关联多个对象,如一个音乐家有多首作品。一对多语法如下:

class M1(models.Model):
	xxx
    
class M2(models.Model):
    xxx
    attr = models.ForeignKey(M1)

示例:新建一个Music类,和Musician是一对多的关系:

from django.db import models

# Create your models here.

class Musician(models.Model):
    name = models.CharField('音乐家', max_length=30)
    class Meta:
        db_table = 'musician'

class Spouse(models.Model):
    name = models.CharField('配偶', max_length=30)
    musician = models.OneToOneField(Musician, on_delete=models.CASCADE)
    class Meta:
        db_table = 'spouse'

class Music(models.Model):
    title = models.CharField('歌名', max_length=30)
    musician = models.ForeignKey(Musician, null=True, on_delete=models.SET_NULL)
    class Meta:
        db_table = 'music'

表结构如下:

mysql> desc music;
+-------------+-------------+------+-----+---------+----------------+
| Field       | Type        | Null | Key | Default | Extra          |
+-------------+-------------+------+-----+---------+----------------+
| id          | bigint      | NO   | PRI | NULL    | auto_increment |
| title       | varchar(30) | NO   |     | NULL    |                |
| musician_id | bigint      | YES  | MUL | NULL    |                |
+-------------+-------------+------+-----+---------+----------------+

数据插入:

from music import models

m1 = models.Musician.objects.get(name='陈奕迅')

mus1 = models.Music.objects.create(title='K歌之王', musician=m1)
mus2 = models.Music.objects.create(title='十面埋伏', musician=m1)
mus3 = models.Music.objects.create(title='富士山下', musician=m1)

查看插入的数据:

mysql> select * from musician;
+----+--------+
| id | name   |
+----+--------+
|  1 | 周杰伦 |
|  2 | 陈奕迅 |
|  3 | 王心凌 |
+----+--------+

mysql> select * from music;
+----+----------+-------------+
| id | title    | musician_id |
+----+----------+-------------+
|  1 | K歌之王  |           2 |
|  2 | 十面埋伏 |           2 |
|  3 | 富士山下 |           2 |
+----+----------+-------------+

数据查询:

通过1查多,对象.关联对象小写_set,如:

m1 = models.Musician.objects.get(name='陈奕迅')

mus = m1.music_set.all()
# 支持all()、filter()、get()等方法
# <QuerySet [<Music: Music object (1)>, <Music: Music object (2)>, <Music: Music object (3)>]>

for mu in mus:
    print(mu.title)
# K歌之王
# 十面埋伏
# 富士山下

通过多查1:

mu = models.Music.objects.get(id=1)

mu.musician.name
# 陈奕迅

8.7.3、多对多映射

一个音乐家可以签约多个平台,一个平台可以有多个音乐家,音乐家和平台之间就是多对多的关系。多对多映射通过中间表实现。

示例如下:

from django.db import models

# Create your models here.

class Musician(models.Model):
    name = models.CharField('音乐家', max_length=30)
    class Meta:
        db_table = 'musician'

class Spouse(models.Model):
    name = models.CharField('配偶', max_length=30)
    musician = models.OneToOneField(Musician, on_delete=models.CASCADE)
    class Meta:
        db_table = 'spouse'

class Music(models.Model):
    title = models.CharField('歌名', max_length=30)
    musician = models.ForeignKey(Musician, null=True, on_delete=models.SET_NULL)
    class Meta:
        db_table = 'music'

class Platform(models.Model):
    platname = models.CharField('平台', max_length=30)
    musician = models.ManyToManyField(Musician)
    class Meta:
        db_table = 'platform'

数据库迁移完成后,查询表结构:

mysql> desc musician;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | bigint      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(30) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+

mysql> desc platform;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | bigint      | NO   | PRI | NULL    | auto_increment |
| platname | varchar(30) | NO   |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+

mysql> desc platform_musician;
+-------------+--------+------+-----+---------+----------------+
| Field       | Type   | Null | Key | Default | Extra          |
+-------------+--------+------+-----+---------+----------------+
| id          | bigint | NO   | PRI | NULL    | auto_increment |
| platform_id | bigint | NO   | MUL | NULL    |                |
| musician_id | bigint | NO   | MUL | NULL    |                |
+-------------+--------+------+-----+---------+----------------+

数据插入:

from music import models

m1 = models.Musician.objects.get(id=1)						# 周杰伦
m2 = models.Musician.objects.get(id=2)						# 陈奕迅

p1 = models.Platform.objects.create(platname='QQ音乐')	
p2 = models.Platform.objects.create(platname='网易云音乐')
p3 = models.Platform.objects.create(platname='咪咕音乐')

m1.platform_set.add(p1)										# m1关联p1
m1.platform_set.add(p2)										# m1关联p2
m1.platform_set.create(platname='酷我音乐')					 # 创建新的platform,并关联m1
p2.musician.add(m2)											# p2关联m2

查看结果:

mysql> select * from musician;
+----+--------+
| id | name   |
+----+--------+
|  1 | 周杰伦 |
|  2 | 陈奕迅 |
|  3 | 王心凌 |
+----+--------+

mysql> select * from platform;
+----+------------+
| id | platname   |
+----+------------+
|  1 | QQ音乐     |
|  2 | 网易云音乐 |
|  3 | 咪咕音乐   |
|  4 | 酷我音乐   |
+----+------------+

mysql> select * from platform_musician;
+----+-------------+-------------+
| id | platform_id | musician_id |
+----+-------------+-------------+
|  1 |           1 |           1 |
|  2 |           2 |           1 |
|  4 |           2 |           2 |
|  3 |           4 |           1 |
+----+-------------+-------------+

数据查询:

通过musician查platform:

m1 = models.Musician.objects.get(id=1)

m1.platform_set.all()		
# <QuerySet [<Platform: Platform object (1)>, <Platform: Platform object (2)>, <Platform: Platform object (4)>]>

通过platform查musician:

p1 = models.Platform.objects.get(id=2)

p1.musician.all()
# <QuerySet [<Musician: Musician object (1)>, <Musician: Musician object (2)>]>
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值