Django初识
Django的目录结构
命令创建了一个mysite的工程 django-admin.py startproject mysite
然后在创建好的mysite目录下使用命令创建一个blog应用python manage.py startapp blog
创建好的目录如下所示
需要注意的是 templatetags是自定义的标签,保存自己创建的.py的文件
Django MTV模式
Django即WEB框架的一种,即使用别人搭建好的东西来简化自己的操作。
Django的MTV模式本质上与MVC模式没有什么差别,也是各组件之间为了保持松耦合关系,只是定义上有些许不同,Django的MTV分别代表:
- Model(模型):负责业务对象与数据库的对象(ORM)
- Template(模版):负责如何把页面展示给用户
- View(视图):负责业务逻辑,并在适当的时候调用Model和Template
此外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
Django流程命令执行工具
django #安装: pip3 install django 添加环境变量 #1 创建project django-admin startproject mysite #2 创建APP python mannage.py startapp app01 #3 settings配置 TEMPLATES STATICFILES_DIRS=( os.path.join(BASE_DIR,"statics"), ) STATIC_URL = '/static/' # STATICFILES_DIRS是必须要有的,无论 STATIC_URL有没有设定 <script src="/statics/jquery-3.1.1.js"></script> <script src="/static/jquery-3.1.1.js"></script> 采用第二种方式 ,后端的更改不会影响前端的引入,避免造成前端大量修改 #4 根据需求设计代码 url.py view.py #5 使用模版 render(req,"index.html") #6 启动项目 python manage.py runserver 127.0.0.1:8090 #7 连接数据库,操作数据 model.py
常用的命令行使用
创建一个django工程 : django-admin.py startproject mysite 在mysite目录下创建blog应用: python manage.py startapp blog #blog是自己启动名字 启动django项目:python manage.py runserver 127.0.0.1:8080 #不写IP默认是127.0.0.1 生成同步数据库的脚本:python manage.py makemigrations 同步数据库: python manage.py migrate 清空数据库: python manage.py flush 查询某个命令的详细信息: django-admin.py help startapp 启动交互界面 :python manage.py shell 终端上输入 python manage.py # 可以看到详细的列表,在忘记子名称的时候特别有用。
Django URL(路由系统)
简洁、优雅的URL模式在高质量的WEB应用中是一个非常重要的细节。设计URL之前,需要创建一个python模块,通常称为URLconf(URLconfiguration)。这个模块是纯粹的python代码,包含URL模式(简单正则)到python函数(视图)的简单映射。基本格式如下
urlpatterns = [ url(正则表达式, views视图函数,参数,别名), ]
- 一个正则表达式字符串
- 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
- 可选的要传递给视图函数的默认参数(字典形式)
- 一个可选的name参数
需要注意的是在一个很大的网站下,我们不可能将所有的URL都放到一个目录下,这时我们需要用到解耦去完成,将同一模块下的URl放在一个目录下进行操作。
如下:需要用到 include
from django.conf.urls import include, url urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^blog/', include('blog.urls')), ] #输入URL会通过总的去blog下寻找URL完成操作
对于额外的参数给视图函数。
django.conf.urls.url() 函数可以接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。
例如
from django.conf.urls import url from blog import views urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ]
在这个例子中,对于 /blog/2005/ 请求,Django 将调用 view.year_archive(request,year='2005',foo='bar')
Django view(视图函数)
一个视图函数,或者简短来说叫做视图,是一个简单的python函数,它接受web请求,并且返回web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张照片等。
http请求中产生两个核心对象:
- http请求:HttpRequest对象
- http响应:HttpResponse对象
# 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支持时该属性才可用。
相对于HttpRequest对象来说,HttpResponse是由自己创建,每个VIEW请求必须返回一个HttpResponse对象。
扩展的几种对象有
页面渲染:render(request,"xx.html"), render_to_response("xx.html"), 页面跳转:redirect() locals(): 可以直接将函数中所有的变量传给模板 name=xx render(request,"xx.html",locals())#这时会把name这个变量传递过去,有几个传递几个
Template
模板基本语法
>>> from django.template import Context, Template >>> t = Template('My name is {{ name }}.') >>> c = Context({'name': 'Stephane'}) >>> t.render(c) 'My name is Stephane.
逻辑控制代码块的组成
- 变量:使用双大括号引用变量{{var_name}}
- 标签tag:大括号和百分号的组合 {% load staticfiles %}
- 过滤器filter的使用:
{{ship_date|date:
"Fj,Y"
}},ship_date变量传给data过滤器,date过滤器通过使用
"FJ,Y"
这几个参数来格式化日期数据。
"|"
代表类似Unix命令中的管道操作。
常用标签
{%if%} 的使用 {% endif %}表示结束
可以使用你的and,or,not来组织的逻辑。但不允许and和or同时出现在条件语句中。新版本中已经支持了{%elif%} 这样的用法。
{% for %}的使用 {% endfor %}表示结束
用来循环一个序列, 还可以使用reserser关键字来进行倒序遍历,一般可以先用if语句判断序列是否为空,再进行遍历;还可以使用empty关键字来进行为空时的跳转
{%csrf_token%}
用于生成csrf_token的标签,用于防治跨站攻击验证。只有在form表单提交时才会有效
<form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
如果是render_to_response时还需设置参数,所以最好用render
{% load %}: 加载标签库
{% url %}: 引用路由配置的地址
{% with %}:用更简单的变量名替代复杂的变量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
{% verbatim %}: 禁止render
{% verbatim %} {{ hello }} {% endverbatim %}
常用过滤器
- add : 给变量加上相应的值
- addslashes: 给变量中的引号前加上斜线
- capfirst : 首字母大写
- cut : 从字符串中移除指定的字符
- date : 格式化日期字符串
- default : 如果值是False,就替换成设置的默认值,否则就是用本来的值
- default_if_none:如果值是None,就替换成设置的默认值,否则就使用本来的值
举几个例子来看看如何去使用
{#value1="aBcDe" 定义一个变量 #} {{ value1|upper }} #结果为 ABCDE {# value2=5#} {{ value2|add:3 }} #结果为8 {# value6='<a href="#">跳转</a>'#} {{ value6 }} #结果<a href="#">跳转</a> {% autoescape off %} {{ value6 }} {% endautoescape %}#结果为HTML修饰的的跳转 {{ value6|safe }}#结果为HTML修饰的的跳转 #所以说这两种是告诉浏览器这两者是安全的,可以死去做渲染返回给用户
自定义标签
先创建templatetags模块,这样Django才能找到
再在templatetags下创建任意的py文件,这里我创建的是add100.py,里面内容如下
from django import template register = template.Library() #固定的格式必须这样写 @register.filter def my_add100(v1, v2): #最多接收两个参数,但能添加到if 条件语句下 return v1+100+v2 @register.simple_tag #不能添加到if 条件语句下 def my_add(v1, v2, v3): return v1+v2+v3
在通过URL连接的py文件去找到我们自己的创建的add100去操作
{% load add100 %} 必须写 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {#filter#} {{ num|my_add100:8 }} {#simple_tag#} {% my_add num 10 10 %} {% if num|my_add100:3 %} <p>{{ num|my_add100:3 }}</p> {% endif %} </body> </html>
extend(继承)
主要是在WEB开发中,会存在很多重复的代码,这就会在成代码冗余。所以就用到了继承去除不同部分代码来分开写,这就避免了这一问题。之前传统的方法是使用 {% include %} 方法,经过改进,现在有了更好的方法。先举个简单的例子
视图中的函数。
def ordered(req): return render(req,"ordered.html") def shopping_car(req): return render(req,"shopping_car.html")
共同HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .page-header{ height: 50px; background-color: rebeccapurple; } .page-body .menu{ height: 500px; background-color: antiquewhite; float: left; width: 20%; } .page-body .content{ height: 500px; background-color: cornflowerblue; float: left; width: 80%; } .page-footer{ height: 50px; background-color: darkcyan; clear: both; } </style> </head> <body> <div> <div class="page-header"></div> <div class="page-body"> <div class="menu"> <a href="/ordered/">订单</a><br> <a href="/shopping_car/">购物车</a> </div> <div class="content"> {% block content %} <div class="xxx" style="color: red">pppppp</div> {% endblock %} </div> </div> <div class="page-footer"></div> </div> </body> </html>
不同部分HTML
{% extends "base.html" %} {% block content %} {{ block.super }} <div>订单</div> {% endblock %}
{% extends "base.html" %} {% block content %} <div class="content"> 购物车2 </div> {% endblock %}
使用继承模板的时候需要注意的地方
- 如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
- 一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。
- 如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
- 如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
- 不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
( . )访问变量属性
从技术上讲,当模板遇到点,它将以这样的顺序查找
- 字典查询
- 属性或方法查询
- 数字索引查询
如果计算结果的值是可调用的,它将被无参数的调用,调用结果将成为模板的值。
>>> 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) u'Sally is 43 years old.'
也可用来引用对象的方法,例如
>>> from django.template import Template, Context >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}') >>> t.render(Context({'var': 'hello'})) u'hello -- HELLO -- False' >>> t.render(Context({'var': '123'})) u'123 -- 123 -- True'
Models
模型是你数据唯一、权威的数据源。它包含你所存储数据的必要字段和行为。通常,每个模型对应数据库中的唯一一张表。
- 每个模型都是django.db.models.Model的一个python子类。
- 模型中的每个属性都表示为数据库的一个字段。
- Django提供一套自动生成的,用于数据库访问的API。
在Django中默认使用splite数据库,在settings配置如下
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
如果想要引用其它数据库,可修改settings配置如下
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'django_com', 'USER':'root', 'PASSWORD':'', } }
注意如果启动数据库时报错,是因为导入的驱动有问题,需要我们重新导入,需要找到项目文件下的__init__文件写入即可解决问题。
import pymysql pymysql.install_as_MySQLdb()
ORM(对象关系映射)
用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。
优点:
- ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发,由此而来。
- 可以避免一些新手程序猿写sql语句带来的性能问题。
缺点:
- 性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果很显著。
- 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。
- 通过QuerySet的query属性查询对应操作的sql语句
创建表结构
from django.db import models # Create your models here. class Publish(models.Model): name = models.CharField(max_length=64) city = models.CharField(max_length=64) def __str__(self): return self.city class Book(models.Model): title = models.CharField(max_length=64) price = models.IntegerField() pagenum = models.IntegerField(null=True) publisher = models.ForeignKey(Publish) # 关联表的外键,一对多 #接受对象 author = models.ManyToManyField("Author") # 多对多的联系 def __str__(self): return self.title class Author(models.Model): name = models.CharField(max_length=64) def __str__(self): return self.name
from django.db import models # Create your models here. class Publish(models.Model): name = models.CharField(max_length=64) city = models.CharField(max_length=64) def __str__(self): return self.city class Book(models.Model): title = models.CharField(max_length=64) price = models.IntegerField() pagenum = models.IntegerField(null=True) publisher = models.ForeignKey(Publish) # 关联表的外键,一对多 # 接受对象 author = models.ManyToManyField("Author") # 多对多的联系 def __str__(self): return self.title class Author(models.Model): name = models.CharField(max_length=64) def __str__(self): return self.name
默认情况下,在多对多表操作时,第三张表是Django内部帮我们创建的,当然我们也可以自己创建。
class Book2Author(models.Model): author=models.ForeignKey("Author") book= models.ForeignKey("Book") class Meta: unique_together=["author","book"]
创建好后,记得同步数据库
生成同步数据库的脚本:python manage.py makemigrations 同步数据库: python manage.py migrate
# AutoField # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.(参阅 _自动主键字段) # BooleanField # A true/false field. admin 用 checkbox 来表示此类字段. # CharField # 字符串字段, 用于较短的字符串. # # 如果要保存大量文本, 使用 TextField. # # admin 用一个 <input type="text"> 来表示此类字段 (单行输入). # # CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数. # # CommaSeparatedIntegerField # 用于存放逗号分隔的整数值. 类似 CharField, 必须要有 maxlength 参数. # DateField # 一个日期字段. 共有下列额外的可选参数: # # Argument 描述 # auto_now 当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳. # auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间. # admin 用一个文本框 <input type="text"> 来表示该字段数据(附带一个 JavaScript 日历和一个"Today"快键. # # DateTimeField # 一个日期时间字段. 类似 DateField 支持同样的附加选项. # admin 用两上文本框 <input type="text"> 表示该字段顺序(附带JavaScript shortcuts). # # EmailField # 一个带有检查 Email 合法性的 CharField,不接受 maxlength 参数. # FileField # 一个文件上传字段. # # 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime formatting, 该格式将被上载文件的 date/time 替换(so that uploaded files don't fill up the given directory). # # admin 用一个``<input type="file">``部件表示该字段保存的数据(一个文件上传部件) . # # 在一个 model 中使用 FileField 或 ImageField 需要以下步骤: # # 在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. (出于性能考虑,这些文件并不保存到数据库.) 定义 MEDIA_URL 作为该目录的公共 URL. 要确保该目录对 WEB 服务器用户帐号是可写的. # 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django 使用 MEDIA_ROOT 的哪个子目录保存上传文件. # 你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). 出于习惯你一定很想使用 Django 提供的 get_<fieldname>_url 函数.举例来说,如果你的 ImageField 叫作 mug_shot, 你就可以在模板中以 {{ object.get_mug_shot_url }} 这样的方式得到图像的绝对路径. # FilePathField # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的. # # 参数 描述 # path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目. Example: "/home/images". # match 可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名. 注意这个正则表达式只会应用到 base filename 而不是路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif. # recursive 可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录. # 这三个参数可以同时使用. # # 我已经告诉过你 match 仅应用于 base filename, 而不是路径全名. 那么,这个例子: # # FilePathField(path="/home/images", match="foo.*", recursive=True) # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif # # FloatField # 一个浮点数. 必须 提供两个 参数: # # 参数 描述 # max_digits 总位数(不包括小数点和符号) # decimal_places 小数位数 # 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段: # # models.FloatField(..., max_digits=5, decimal_places=2) # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义: # # models.FloatField(..., max_digits=19, decimal_places=10) # admin 用一个文本框(<input type="text">)表示该字段保存的数据. # # ImageField # 类似 FileField, 不过要校验上传对象是否是一个合法图片.它有两个可选参数:height_field 和 width_field,如果提供这两个参数,则图片将按提供的高度和宽度规格保存. # # 该字段要求 Python Imaging Library. # # IntegerField # 用于保存一个整数. # # admin 用一个``<input type="text">``表示该字段保存的数据(一个单行编辑框) # # IPAddressField # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30"). # # admin 用一个``<input type="text">``表示该字段保存的数据(一个单行编辑框) # # NullBooleanField # 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项. # # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据. # # PhoneNumberField # 一个带有合法美国风格电话号码校验的 CharField``(格式: ``XXX-XXX-XXXX). # PositiveIntegerField # 类似 IntegerField, 但取值范围为非负整数(这个字段应该是允许0值的....所以字段名字取得不太好,无符号整数就对了嘛). # PositiveSmallIntegerField # 类似 PositiveIntegerField, 取值范围较小(数据库相关) # SlugField # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.它们通常用于URLs. # # 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. 在以前的 Django 版本,没有任何办法改变 50 这个长度. # # 这暗示了 db_index=True. # # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-populate the slug, via JavaScript, in the object's admin form: # # models.SlugField(prepopulate_from=("pre_name", "name")) # prepopulate_from 不接受 DateTimeFields. # # admin 用一个``<input type="text">``表示 SlugField 字段数据(一个单行编辑框) # # SmallIntegerField # 类似 IntegerField, 不过只允许某个取值范围内的整数.(依赖数据库) # # TextField # 一个容量很大的文本字段. # # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框). # # TimeField # A time. Accepts the same auto-population options as DateField 和 DateTimeField. # # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(附加一些JavaScript shortcuts). # # URLField # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应). # # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) # # USStateField # 一个两字母的美国州名缩写. # # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) # # XMLField # 一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema 的文件系统路径.
添加数据(create、save)
def login(req): # create方法1 # dic= {"title":"python","price":35} # Book.objects.create(**dic) # return HttpResponse("OK") # create方法2 # Book.objects.create( # title="python1", # price=45 # ) # save 方法 1 # obj = Book(title="python2",price=55) # obj.save() # save 方法 2 # obj = Book() # obj.title = "python3" # obj.price = 65 # obj.save() # return HttpResponse("ok")
向一对多或多对多关联表插入数据时需要注意的事项是:
pub = models.Publish.objects.filter(id=1) Book.objects.create( title="JAVA2", price=55, # 第一种,因为外键关系是一对多,publisher只能对应一个对象,所以值只能是一个对象,不能是一个集合 # publisher=pub[0], # 第二种直接根据ID匹配,也不用 pub = models.Publish.objects.filter(id=1) publisher_id=2 )
book = models.Book.objects.filter(id=6)[0] # authors = models.Author.objects.filter(id=1)[0] # 获取的是一个几个 authors = models.Author.objects.get(id=3) # get获取的是一个对象不是一个集合 book.author.add(authors) # 正向查询 # authors.book_set(book) # 反向查询
修改数据(update,save)
两种增加数据的实例
author = models.Author.get(id=2).name = 'flash' author.save() models.Publish.objects.filter(id=2).update(name="aaa")
这里你需要注意的是 update是QuerySet 对象的方法,所以不能更新返回是model对象的结果。相比于save方法,update放大效率更高。
因为,save去更新数据时,会将要更新的那列数据全部更细腻一遍,即使没有赋予新的值,也会将原来的值,重新去赋值一遍,这就是导致save效率低的原因。而update方法只会更新你要更新的那个列。
查找数据
常见查询
# 查询相关API: # <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 # <2>all(): 查询所有结果 # <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 #-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()-------- # <4>values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 # <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # <6>order_by(*field): 对查询结果排序 # <7>reverse(): 对查询结果反向排序 # <8>distinct(): 从返回结果中剔除重复纪录 # <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 # <11>first(): 返回第一条记录 # <12>last(): 返回最后一条记录 # <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。
单表查询和多表查询
#正向查找---属性 ret = models.Book.objects.filter(id=2)[0] print(ret.title) print(ret.publisher.name) #反向查找----属性 ret2=models.Publish.objects.last() print(ret2.name) print(ret2.city) print(ret2.book_set.all()) #ret2.book_set是一个queryset集合 #可以利用双下线去做条件判断 models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in不在的显示出来 models.Tb1.objects.filter(name__contains="ven") models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
#一对多 et4=models.Book.objects.filter(title='Python').values('publisher__city') print(ret4) #[{'publisher__city': '北京'}] ret5=models.Book.objects.filter(publisher__address='北京').values('publisher__name') print(ret5) #[{'publisher__name': '人大出版社'}, {'publisher__name': '人大出版社'}] #正向查找(条件)之多对多 ret6=models.Book.objects.filter(title='Python').values('author__name') print(ret6) ret7=models.Book.objects.filter(author__name="alex").values('title') print(ret7) #反向查找之一对多: ret8=models.Publisher.objects.filter(book__title='Python').values('name') print(ret8)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的关联表名 #反向查找之多对多: ret10=models.Author.objects.filter(book__title='Python').values('name') print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}]
多对多表的补充
class Book(models.Model): name =models.CharField(max_length=32) def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=32) m = models.ManyToManyField('Book') def __str__(self): return self.name
def test(request): author_list = models.Author.objects.values('id','name','m','m__name') for item in author_list: print(item['id'], item['name'], '书籍ID:', item['m'], item['m__name']) # 正向查找 obj = models.Author.objects.get(id=1) # 增加 # obj.m.add(2) # obj.m.add(2,3) # obj.m.add(*[1,3]) #删除 # obj.m.remove(2) # obj.m.remove(2,3) # obj.m.remove(*[1,3]) # # 清空 # obj.m.clear() # 更新 # obj.m.set([1,2,3]) # 反向查找 # obj = models.Book.objects.get(id=1) # obj.author_set.add(1) return HttpResponse('ok')
聚合的两种方式
- 在查询上生成聚合 aggregate
- 为查询集的每一项生成聚合 annotate
1、比如我们想要计算所有图书的平均价钱。Django的查询语法提供aggregate的方法。首先我们找到所有图书
#使用之前先引入 from django.db.models import Avg Book.objects.all().aggregate(Avg('price'))
aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值。
aggregate()是QuerySet的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。
2、annotate为QuerySet中每一个对象都生成一个独立的汇总值。比如如果检索一列图书,你可能想知道有多少作者写了一本书。
from django.db.models import Count q = Book.objects.annotate(Count('authors')) q[0] <Book: The Definitive Guide to Django> q[0].authors__count 2 q[1] <Book: Practical Django Projects> q[1].authors__count 1
与aggregate()不同的是,annotate()不是一个终止子句,annotate()子句返回结果是一个查询集(QuerySet)。这个查询集可以用到任何QuerySet的方法进行操作。
在查询一些复杂的关系时,比如求与、或等时,我们需要用到 F/Q查询。
- F 查询专门取对象中某列的值得操作
- Q 是构造搜索条件去查找
from django.db.models import F models.Tb1.objects.update(num=F('num')+1) #得到结果是将TB1表中的每个num的数值加 1
from django.db.models import Q #1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询 q1=models.Book.objects.filter(Q(title__startswith='P')).all() print(q1)#[<Book: Python>, <Book: Perl>] # 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。 Q(title__startswith='P') | Q(title__startswith='J') # 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合 Q(title__startswith='P') | ~Q(pub_date__year=2005) # 4、应用范围: Book.objects.get( Q(title__startswith='P'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) ) #sql 语句对比: # SELECT * from polls WHERE question LIKE 'P%' # AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') # 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。 # 正确用法: Book.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), title__startswith='P')
extra # # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) # Entry.objects.extra(where=['headline=%s'], params=['Lennon']) # Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) # Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) # F # # from django.db.models import F # models.Tb1.objects.update(num=F('num')+1) # Q # # 方式一: # Q(nid__gt=10) # Q(nid=8) | Q(nid__gt=10) # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') # 方式二: # con = Q() # q1 = Q() # q1.connector = 'OR' # q1.children.append(('id', 1)) # q1.children.append(('id', 10)) # q1.children.append(('id', 9)) # q2 = Q() # q2.connector = 'OR' # q2.children.append(('c1', 1)) # q2.children.append(('c1', 10)) # q2.children.append(('c1', 9)) # con.add(q1, 'AND') # con.add(q2, 'AND') # # models.Tb1.objects.filter(con) # 执行原生SQL # # from django.db import connection, connections # cursor = connection.cursor() # cursor = connections['default'].cursor() # cursor.execute("""SELECT * from auth_user where id = %s""", [1]) # row = cursor.fetchone()
admin配置
admin是Django自带的一个后台管理app
我们在启动Django服务后直接访问admin可以得到一个登陆界面如下。默认是英文,修改语言需要进到settings下去修改即可
LANGUAGE_CODE = 'zh-Hans' TIME_ZONE = 'Asia/Shanghai'
默认没有用户,我们首先注册一个管理员用户 python manage.py createsuperuser 即可
进入后我们先将数据库引进
- 使用register的方法
from django.contrib import admin # Register your models here. from app.models import * class PubAdmin(admin.ModelAdmin): pass admin.site.register(Author,PubAdmin) # 直接使用register方法
我们可以根据自己的需求去设置页面展示的效果
- list_display: 指定要显示的字段
- search_fields:指定搜索的字段
- list_filter: 指定列表过滤器
- ordering:指定排序字段
class PubAdmin(admin.ModelAdmin): list_display = ("name", "city") # name,city为注册数据库内的属性 search_fields = ("name",) # 相当于指定一个搜索框 list_filter = ("name",) ordering = ("name",)
官方文档 https://docs.djangoproject.com/en/1.10/ref/contrib/admin/