Django学习
一、什么是web框架?
框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑架构,使用框架可以使你快速开发特定的系统,简单的来说你用别人搭建好的
舞台来做表演,对于所有的web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。
这个接口就是WSGI:Web Server Gateway Interface
二、MVC和MTV模式
著名的MVC模式:所谓MVC就是把web应用分为模型(M),控制器(C),视图(V)三层;他们之间以一种插件似的,松耦合的方式连接在一起。
模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器(C)接受用户的输入调用模型和视图完成用户的请求。

Django的MTV模式本质上与MVC模式没有什么差别,也是各个组件之间为了保持松耦合关系,只是定义上有些不同,Django的MTV分别代表:
Model(模型):负责业务对象与数据库的对象(ORM);
Template(模板):负责如何把页面展示给用户;
View(视图):负责业务逻辑,并在适当的时候调用Model和Template;
此外,Django还有一个URL分发器,他的作用是将一个个URL请求分发给不同的View处理,View再调用响应的Model和Template;

三、django的流程和命令行工具
django-admin.py 是Django的一个用于管理任务的命令行工具,manage.py是对django-admin.py的简单包装,每一个Django Project里都会有一个mannage.py。
(1)、创建一个django工程 : django-admin.py startproject mysite
- manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
- urls.py ----- 负责把URL模式映射到应用程序。
(2)、在mysite目录下创建blog应用: python manage.py startapp blog
(3)、启动django项目:python manage.py runserver 8080
(4)、生成同步数据库的脚本:python manage.py makemigrations
(5)、admin:我们需要为进入这个项目的后台创建超级管理员:python manage.py createsuperuser,设置好用户名和密码后便可登录啦!
(6)、清空数据库:python manage.py flush
(7)、查询某个命令的详细信息: django-admin.py help startapp
(8)、启动交互界面:python manage.py shell
(9)、终端上输入python manage.py 可以看到详细的列表,在忘记子名称的时候特别有用。
四、Django的配置文件:settings
五、Django URL(路由系统)
URL配置(URLconf)就像Django所支撑网站的目录。它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表;你就是以 这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码;
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
]
参数说明:
- 一个正则表达式字符串
- 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
- 可选的要传递给视图函数的默认参数(字典形式)
- 一个可选的name参数
1、简单的URLconf
urlpatterns = [
##路由全匹配
url('admin/', admin.site.urls),
url('showtime/', views.showtime),
url('index/', views.index),
##正则匹配
url(r'^articles$',views.articles),
url(r'^articles/(\d{4})/(\d{2})$',views.articles_date),
##多个项目进行路由分发 :在项目下新建urls.py,访问以blog开头的url就直接分发到该项目下;
url(r'blog/',include('blog.urls'))##工程下新建该路由分发
]
2、有名分组
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
3、路由分发
from django.conf.urls import include, url
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/', include('blog.urls')),
]
六、Django Views(视图函数)
在HTTP请求中产生亮哥核心的对象:
http请求:HttpRequest对象;
http响应:HttpResponse对象
所在位置:django.http
1、HttpRequest对象的属性和方法
# path: 请求页面的全路径,不包括域名 # # method: 请求中使用的HTTP方法的字符串表示。全大写表示。例如 # # if req.method=="GET": # # do_something() # # elseif req.method=="POST": # # do_something_else() # # GET: 包含所有HTTP GET参数的类字典对象 # # POST: 包含所有HTTP POST参数的类字典对象 # # 服务器收到空的POST请求的情况也是可能发生的,也就是说,表单form通过 # HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用 # if req.POST来判断是否使用了HTTP POST 方法;应该使用 if req.method=="POST" # # # # COOKIES: 包含所有cookies的标准Python字典对象;keys和values都是字符串。 # # FILES: 包含所有上传文件的类字典对象;FILES中的每一个Key都是<input type="file" name="" />标签中 name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys: # # filename: 上传文件名,用字符串表示 # content_type: 上传文件的Content Type # content: 上传文件的原始内容 # # # user: 是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果访问用户当前 # 没有登陆,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你 # 可以通过user的is_authenticated()方法来辨别用户是否登陆: # if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware # 时该属性才可用 # # session: 唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用。 #方法 get_full_path(), 比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的结果就是/index33/?name=123 req.path:/index33
2、HttpResponse对象
对于HttpResponse对象来说,是由django自动创建的,但是,Httpresponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象;
#页面渲染:render() render_to_response()
#页面跳转:redirect("路径")
# locals():可以直接将函数中的所有变量传给模板
备注:
-----------------------------------url.py
url(r"login", views.login),
url(r"yuan_back", views.yuan_back),
-----------------------------------views.py
def login(req):
if req.method=="POST":
if 1:
# return redirect("/yuan_back/")
name="yuanhao"
return render(req,"my backend.html",locals())
return render(req,"login.html",locals())
def yuan_back(req):
name="苑昊"
return render(req,"my backend.html",locals())
-----------------------------------login.html
<form action="/login/" method="post">
<p>姓名<input type="text" name="username"></p>
<p>性别<input type="text" name="sex"></p>
<p>邮箱<input type="text" name="email"></p>
<p><input type="submit" value="submit"></p>
</form>
-----------------------------------my backend.html
<h1>用户{{ name }}你好</h1>
#总结: render和redirect的区别:
# 1 if render的页面需要模板语言渲染,需要的将数据库的数据加载到html,那么所有的这一部分
# 除了写在yuan_back的视图函数中,必须还要写在login中,代码重复,没有解耦.
# 2 the most important: url没有跳转到/yuan_back/,而是还在/login/,所以当刷新后
# 又得重新登录.
七、Template基础
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
在上图的例子中,直接将html编码到你的python代码之中,不是一个好主意;
-
对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
-
Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
-
程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
1、模板的组成
组成:HTML代码+逻辑控制代码
2、逻辑控制代码的组成
(1)、变量(使用双大括号来引用变量):{{var}}
a、Template和Context对象(html就是模板,Context上下文就是{{"name":name}})
>>> python manange.py shell (进入该django项目的环境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'
# 同一模板,多个上下文,一旦有了模板对象,你就可以通过它渲染多个context,无论何时我们都可以
# 像这样使用同一模板源渲染多个context,只进行 一次模板创建然后多次调用render()方法渲染会
# 更为高效:
# Low
for name in ('John', 'Julie', 'Pat'):
t = Template('Hello, {{ name }}')
print t.render(Context({'name': name}))
# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
print t.render(Context({'name': name}))
Django模板解析非常快捷。大部分的解析工作都是在后台通过对简短正则表达式一次性调用完成的。这和基于xml的模板引擎形成鲜明对比,那些引擎承担了XML解析器的开销,且往往比Django模板渲染引擎要慢上几个数量级;
b、深度变量的查找(万能的句点号)
在到目前为止的例子中,我们通过 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)
#最好是用几个例子来说明一下。
# 首先,句点可用于访问列表索引,例如:
>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'
#假设你要向模板传递一个 Python 字典。 要通过字典键访问该字典的值,可使用一个句点:
>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'
#同样,也可以通过句点来访问对象的属性。 比方说, Python 的 datetime.date 对象有
#year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:
>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
>>> d.month
>>> d.day
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'
# 这个例子使用了一个自定义的类,演示了通过实例变量加一点(dots)来访问它的属性,这个方法适
# 用于任意的对象。
>>> from django.template import Template, Context
>>> class Person(object):
... def __init__(self, first_name, last_name):
... self.first_name, self.last_name = first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'
# 点语法也可以用来引用对象的方法。 例如,每个 Python 字符串都有 upper() 和 isdigit()
# 方法,你在模板中可以使用同样的句点语法来调用它们:
>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'
# 注意这里调用方法时并* 没有* 使用圆括号 而且也无法给该方法传递参数;你只能调用不需参数的
# 方法。
c、变量的过滤器(filter)的使用{{obj|filter:param}}
# 1 add : 给变量加上相应的值
#
# 2 addslashes : 给变量中的引号前加上斜线
#
# 3 capfirst : 首字母大写
#
# 4 cut : 从字符串中移除指定的字符
#
# 5 date : 格式化日期字符串
#
# 6 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值
#
# 7 default_if_none: 如果值是None,就替换成设置的默认值,否则就使用本来的值
#实例:
#value1="aBcDe"
{{ value1|upper }}<br>
#value2=5
{{ value2|add:3 }}<br>
#value3='he llo wo r ld'
{{ value3|cut:' ' }}<br>
#import datetime
#value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}<br>
#value5=[]
{{ value5|default:'空的' }}<br>
#value6='<a href="#">跳转</a>'
{{ value6 }}
{% autoescape off %}
{{ value6 }}
{% endautoescape %}
{{ value6|safe }}<br>
{{ value6|striptags }}
#value7='1234'
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|slice:":-1" }}<br>
#value8='http://www.baidu.com/?a=1&b=3'
{{ value8|urlencode }}<br>
value9='hello I am yuan'
====变量过滤器:https://www.cnblogs.com/open-yang/p/11221499.html
(2)、标签(tag)的使用(使用大括号和百分比的组合表示使用tag){% tags%}
a、标签{% if %}的使用
<h4>
{% if l_lenght < 10 %}
<h1>列表l:{{ l}}</h1>
{% endif %}
</h4>
#{% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量
#{% if %} 标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义,例如下面的标签是不合法的:
#{% if obj1 and obj2 or obj3 %}
b、标签{% for %}的使用
{% for %}标签允许你按顺序遍历一个序列中的各个元素,每次循环模板系统都会渲染{% for %}和{% endfor %}之间的所有内容
<ul>
{% for obj in list %}
<li>{{ obj.name }}</li>
{% endfor %}
</ul>
#在标签里添加reversed来反序循环列表:
{% for obj in list reversed %}
...
{% endfor %}
#{% for %}标签可以嵌套:
{% for country in countries %}
<h1>{{ country.name }}</h1>
<ul>
{% for city in country.city_list %}
<li>{{ city }}</li>
{% endfor %}
</ul>
{% endfor %}
#系统不支持中断循环,系统也不支持continue语句,{% for %}标签内置了一个forloop模板变量,
#这个变量含有一些属性可以提供给你一些关于循环的信息
1,forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1:
{% for item in todo_list %}
<p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}
2,forloop.counter0 类似于forloop.counter,但它是从0开始计数,第一次循环设为0
3,forloop.revcounter
4,forloop.revcounter0
5,forloop.first当第一次循环时值为True,在特别情况下很有用:
{% for object in objects %}
{% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
{{ object }}
</li>
{% endfor %}
# 富有魔力的forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了
# 如果你的模板context已经包含一个叫forloop的变量,Django会用{% for %}标签替代它
# Django会在for标签的块中覆盖你定义的forloop变量的值
# 在其他非循环的地方,你的forloop变量仍然可用
#{% empty %}
{{li }}
{% for i in li %}
<li>{{ forloop.counter0 }}----{{ i }}</li>
{% empty %}
<li>this is empty!</li>
{% endfor %}
# [11, 22, 33, 44, 55]
# 0----11
# 1----22
# 2----33
# 3----44
# 4----55
c、{% csrf_token %}:csrf_token标签
用于生成csrf_token的标签,用于防治跨站攻击验证。有两种方法可以解决该csrf_token报错;
- 在Django框架中setting文件中注释掉中间件csrf_token
- 在html文件中写入{% csrf_token %}
d、{% with %}:用更简单的变量名替代复杂的变量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
e、{% verbatim%}:禁止render
{% verbatim %}
{{ hello }}
{% endverbatim %}
f、{%load%}:加载标签库
- 自定义filter
- 自定义simple_tag
-----在app中创建templatetags模块;
-----在该模块下创建任意py文件tag_1.py;
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
###以上结构为固定不可随意修改
@register.filter
def filter_leraning(x,y): #自定义筛选器
return x*y
@register.simple_tag
def simple_tag_learning(x,y): #自定义标签
return x*y
@register.simple_tag
def simple_tag_input(id,arg):
result = "<input type='text' id='%s' class='%s' />" %(id,arg)
return mark_safe(result)
-----在使用自定义simple_tag和filter的html文件中导入之前创建的py文件:{% load tag_1 %};
-----在html文件中调用自定义filter和自定义simple_tag;
{% load tag_1 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>learning load <tag></tag></title>
</head>
<body>
{% for i in l %}
<h2>
第{{ forloop.counter }}条数据:{{ i|filter_leraning:10 }}
</h2>
{% endfor %}
{% for i in l %}
<h2>
This is {{ forloop.revcounter }} :{% simple_tag_learning 1 2%}
This is {{ forloop.revcounter }} :{% simple_tag_input "student" "middle" %}
</h2>
{% endfor %}
</body>
</html>
-----在settings中的INSTALLED_APPS配置当前app,不然Django无法找到自定义的simple_tag;
备注:自定义simple_tag不能使用在 控制语句后面;
g、extend模板继承
-----基本模板
<!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>
-----继承模板,重载{%block%} {%endblock%}
-----备注:
- 如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
- 一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此
你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越
多越好。
- 如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模
板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
- 不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。
也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个
相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
八、Models
1、数据库的相关配置
(1)、django默认支持sqlite,mysql,oracle,postgresql数据库
a、django默认使用的是sqlite的数据库,默认自带sqlite的数据库驱动,引擎名称:django.db.backends.sqlite3

b、mysql的引擎名称:django.db.backends.mysql

修改相关数据库的配置文件:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'books', #你的数据库名称
'USER': 'root', #你的数据库用户名
'PASSWORD': '', #你的数据库密码
'HOST': '', #你的数据库主机,留空默认为localhost
'PORT': '3306', #你的数据库端口
}
}
(2)、mysql的驱动程序
- MySQLdb(mysql python)
- mysqlclient
- MySQL
- PyMySQL(纯python的mysql驱动程序)
(3)、python3.8远程连接云服务器mysql数据库
a、settings配置文件修改databases配置文件
b、在项目的__init__.py文件中mysql的驱动程序
import pymysql ##不加该配置连接mysql会报错,提示mysqlclient版本不能低于1.3 pymysql.version_info = (1, 3, 13, "final", 0) pymysql.install_as_MySQLdb()
c、在python Terminal中执行
python manage.py makemigrations
将models.py的文件生成initial.py文件(migrations文件下)
python mange.py migrate
执行initial.py文件生成数据库表
2、ORM(对象关系映射)
ORM用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作;
优点:
(1)、ORM使得我们的通用数据库交互变得简单易行,而且完全不必考虑sql语句,快速开发,由此而来;
(2)、可以避免一些新手程序员些sql语句带来的性能问题,
缺点:
(1)、性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟登录加载来减轻这个问题;
(2)、对于个别复杂的查询,ORM任然力不从心,为了解决这个问题,ORM一般支持些raw sql;
(3)、通过QuerySet的query属性查询对应操作的sql语句;
3、单表操作ORM
(1)、表记录的增加
##新增记录方式一:通过实例化的方式是需要保存的
# add_b = Book1(name="python",price=32,pub_date="2020-04-02",auther="oldboy")
# add_b.save()
##新增记录方式二
Book1.objects.create(name="python",price=32,pub_date="2020-04-02",auther="oldboy")
# Book1.objects.create(**dic)
(2)、表记录的修改
###修改记录方式一,尽量使用方式一,通过底层sql语句可知
# Book1.objects.filter(auther="老猫").update(price = 999)
#修改记录方式二,通过类的属性进行进行修改,get方法获取的对象不是对象集合
b = Book1.objects.get(auther="alex") ##b是一个对象集合
# print(b,type(b))
b.price = 68
##通过类的对象赋值需要进行保存操作
b.save()
(3)、表记录的删除
Book1.objects.filter(auther="alex").delete()
(4)、表记录的查询
#对象集合
# book_list = Book1.objects.filter(id=2)
# book_list = Book1.objects.exclude(auther="yuan").values("name","price")
#
# book_list = Book1.objects.all()
# book_list = Book1.objects.all()[:2]
# book_list = Book1.objects.all()[:-1]
#first、last、get取到的都是一个实例对象,并非一个Queryset集合对象
# book = Book1.objects.first()
# book = Book1.objects.last()
# book = Book1.objects.get(id=2)##当符合条件只有一条记录的时候才不会报错;存在多条记录就会报错
#
# #
# ret1 = Book1.objects.filter(auther="oldboy").values("name")
# ret2 = Book1.objects.filter(auther="oldboy").value_list("name","price")
#
# #
# book_list = Book1.objects.all().values("name").distinct()
# book_accout = Book1.objects.all().values("name").distinct().count()
##模糊查询:万能的下划线
##万能的下划线
#id大于1的记录
# book_list = Book1.objects.filter(id__gt =1)
# book_list = Book1.objects.filter(id__in =[11,22,3,4])
# book_list = Book1.objects.exclude(id__in = [11,22,3,4]) ## not in
# book_list = Book1.objects.filter(name__contains="pyth")
# book_list = Book1.objects.filter(name__icontains="pyth") ##icontains 大小写不敏感
# book_list = Book1.objects.filter(id__range = [1,6])
4、多表操作一对多ORM
(1)、一对多关系创建
from django.db import models
# Create your models here.
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
class Book(models.Model):
name=models.CharField(max_length=20)
price=models.IntegerField()
pub_date = models.DateField()
#定义外键约束,增加级联约束条件
publish = models.ForeignKey("Publish",on_delete=models.CASCADE)
auther = models.ManyToManyField("Auther")
class Auther(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
def __str__(self):
return self.name
(2)、一对多新增
def addbook(request):
##直接对字段进行赋值,字段名必须和数据库字段名保持一致
# Book.objects.create(name="python",price=35,pub_date="2019-4-25",publish_id=2)
##查找对象然后将对象赋值给外键
publish_obj = Publish.objects.filter(name="人民出版社")[0]
Book.objects.create(name="Oracle",price=108,pub_date="2005-05-04",publish=publish_obj)
return HttpResponse("添加成功")
(3)、一对多查询
def multi_query(request):
###查询出出版python的出版社
#正向查询查询出书籍名为python的对象,然后通过书籍名为python的对象去查询出版社对象:通过多查询一
book_obj = Book.objects.get(name="python")
print(book_obj.publish.name)
##查询出出版社为人民出版社出版的所有书籍:通过一去查询多
pub_obj = Publish.objects.filter(name="人民出版社")[0]
ret = pub_obj.book_set.all().values("name","price") ##book_set专用语法
print(ret)
##查询出出版社为人民出版社出版的所有书籍
##方式二、通过filter、values的双下划线进行查询
ret = Book.objects.filter(publish__name="人民出版社").values("name","price")
print(ret)
return HttpResponse("一对多查询成功")
备注:通过filter、values的双下划线进行查询;
def multi_query(request):
###1、查询出出版python的出版社
#正向查询查询出书籍名为python的对象,然后通过书籍名为python的对象去查询出版社对象:通过多查询一
book_obj = Book.objects.get(name="python")
print(book_obj.publish.name)
##2、查询出出版社为人民出版社出版的所有书籍:通过一去查询多
pub_obj = Publish.objects.filter(name="人民出版社")[0]
ret = pub_obj.book_set.all().values("name","price") ##book_set专用语法
print(ret)
#3、查询出出版社为人民出版社出版的所有书籍
##方式二、通过filter、values的双下划线进行查询
ret = Book.objects.filter(publish__name="人民出版社").values("name","price")
print(ret)
#4、查询出出版python的出版社 4等价于1
ret = Publish.objects.filter(Book__name = "python").values("name")
print(ret)
return HttpResponse("一对多查询成功")
5、多对多查询
(1)、建立多对多的关系
from django.db import models
# Create your models here.
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
class Book(models.Model):
name=models.CharField(max_length=20)
price=models.IntegerField()
pub_date = models.DateField()
#定义外键约束
publish = models.ForeignKey("Publish",on_delete=models.CASCADE)
###对对多定义,可一个通过一对多的方式完成;ManyToMany方式是自动建立第三张关系表;一对多的方式
##是需要手动创建第三张关系表;
auther = models.ManyToManyField("Auther")
class Auther(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
def __str__(self):
return self.name
(2)、添加多对多的关系
def multi_add(request):
book_obj = Book.objects.get(id=4)
print(book_obj)
##这是book_obj对象的author one_multi.Auther.None
print("这是book_obj对象的author",book_obj.auther)
auther_obj = Auther.objects.all()
print(auther_obj)
book_obj.auther.add(*auther_obj)
return HttpResponse("多对多添加成功")
6、聚合函数和分组函数
(1)、聚合函数aggregate
通过对QuerySet进行计算,返回一个聚合值的字典。aggretate()中的每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合;
def count(request):
#aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的
#标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定
#一个名称,可以向聚合子句提供它:
avg_ret = Book.objects.filter(auther__name="alex").aggregate(avg_price=Avg("price"))
print(avg_ret)
return HttpResponse("查询成功")
(2)、分组函数:annotate
def count(request):
#aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的
#标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定
#一个名称,可以向聚合子句提供它:
avg_ret = Book.objects.filter(auther__name="alex").aggregate(avg_price=Avg("price"))
print(avg_ret)
##统计每个出版社出版书籍的总价格
group_ret = Publish.objects.values("name").annotate(Sum("book__price"))
print(group_ret)
##统计每个作者出版书籍的总价格
group_ret2 = Auther.objects.values("name").annotate(Sum("book__price"))
print(group_ret2)
return HttpResponse("查询成功")
7、F查询和Q查询
def F_func(request):
###F函数,使用查询条件的值,专门取对象中某列值的操作
Book.objects.all().update(price=F("price")+10)
print(Book.objects.all().values("name","price"))
###Q函数,构造查询条件
##a、Q函数可以对关键参数进行封装,从而更好的应用多个查询
q1 = Book.objects.filter(Q(name__startswith="py")).all()
q2 = Publish.objects.filter(Q(name__contains="人民")).first()
print(q1)
print(q2)
###b、可以组合使用&(且),|(或)操作符,当一个操作符作用于两个Q对象,产生一个新的Q对象
q3 = Book.objects.filter(Q(name__contains="python")&Q(price__lt=60)).all()
print(q3)
###c、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式结合
q4 = Book.objects.filter(Q(name__contains="python") |~Q(price__lt=60))
return HttpResponse("操作成功")
###d、Q对象可以与关键字参数查询一起使用,不过一定要把Q函数放在关键字参数查询的前面
q5 = Book.objects.filter(Q(name__contains="python"),price__lt=60)
print(q5)
8、QuerySet的详解
(1)、惰性机制
所谓惰性机制:Publist.objects.all()或者Publish.objects.filter()等都只是返回一个QuerySet集合(查询结果集对象),它并不会马上执行sql语句,而是调用QuerySet的时候才执行;
(2)、QuerySet的特点
- 可迭代
- 可切片
(3)、QuerySet的高效使用
<1>Django的queryset是惰性的
Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得
到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave")
上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数,
这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。
<2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql.
为了验证这些,需要在settings里加入 LOGGING(验证方式)
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i)
# if obj:
# print("ok")
<3>queryset是具有cache的
当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行
(evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,
你不需要重复运行通用的查询。
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i)
## models.Book.objects.filter(id=3).update(title="GO")
## obj_new=models.Book.objects.filter(id=3)
# for i in obj:
# print(i) #LOGGING只会打印一次
<4>
简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些
数据!为了避免这个,可以用exists()方法来检查是否有数据:
obj = Book.objects.filter(id=4)
# exists()的检查可以避免数据放入queryset的cache。
if obj.exists():
print("hello world!")
<5>当queryset非常巨大时,cache会成为问题
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统
进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法
来获取数据,处理完数据就将其丢弃。
objs = Book.objects.all().iterator()
# iterator()可以一次只从数据库获取少量数据,这样可以节省内存
for obj in objs:
print(obj.name)
#BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
for obj in objs:
print(obj.name)
#当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使
#用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询
总结:
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能
会造成额外的数据库查询。
九、admin的配置
admin是django强大功能之一,他能够从数据库中读取数据,呈现在页面中,进行管理。默认情况下,它的功能已经非常强大,如果你不需要复杂的功能,它已经够用,但是有些时候,一些特殊的功能
还需要定制,比如搜索功能等;
1、admin的预先配置
- 在admin.py文件中注册数据库表,不然admin管理台无法读取数据:admin.site.register(Book)
- 创建操作用户:python manage.py createsuperuser,不然无法登陆;
2、自定义admin的样式
- list_display: 指定要显示的字段
- search_fields: 指定搜索的字段
- list_filter: 指定列表过滤器
- ordering: 指定排序字段
- ……
from django.contrib import admin
from app01.models import *
# Register your models here.
# @admin.register(Book)#----->单给某个表加一个定制
class MyAdmin(admin.ModelAdmin):
list_display = ("title","price","publisher")
search_fields = ("title","publisher")
list_filter = ("publisher",)
ordering = ("price",)
fieldsets =[
(None, {'fields': ['title']}),
('price information', {'fields': ['price',"publisher"], 'classes': ['collapse']}),
]
admin.site.register(Book,MyAdmin)
admin.site.register(Publish)
admin.site.register(Author)

浙公网安备 33010602011771号