一. 框架
django为全栈框架
tornado,flask为非全栈框架
内容型网站(cms)一般选用django框架,对于接口类的网站可以用flask
二. django-amdin.py 和 manage.py
django-admin.py是Django的一个用于管理任务的命令行工具;manage.py是对django-admin.py的简单包装,它包含django-admin.py的所有命令,每个Django project里面都会包含一个manage.py
1. 语法:
django-admin.py 子命令
manage.py 子命令
where django-admin.py 查看django-admin.py的位置
2. django-admin.py help 查看帮助信息
常见子命令:
startproject:创建一个项目
startapp:创建一个app
runserver:运行开发服务器
shell:进入django shell
compilemessages:编译语言文件
makemessages:创建语言文件
makemigrations:生成数据库同步脚本
migrate:同步数据库
showmigrations:查看生成的数据库同步脚本
sqlflush:查看生成清空数据库的脚本
sqlmigrate:查看数据库同步的sql语句
dumpdata:导出数据
loaddata:导入数据
django-admin.py help startproject 查看命令简要说明
3. manage.py特有的一些子命令
manage.py help :查看帮助信息
比如:
manage.py runserver 开启服务
manage.py createsuperuser:创建超级管理员
manage.py changepassword 账号名: 用户修改指定用户名密码
思考点:
默认runserver的端口是8000,如果想用其他端口,在后面加上端口即可,如下
python manage.py runserver 8080
三. 建立一个简单网页的注意点
1. 编辑应用程序中的view.py
from django.shortcuts import render
def hello(request):
return render(request, 'table.html')
注意
1) hello()中request参数是必须的,否则请求不到信息
2) render()中的2个参数,table.html为请求的网页
查找用户信息需要在view.py中引入模型如下,这个是有实际的层级目录的
from django.contrib.auth.models import User
课后思考
1. 目前模板和静态文件都是放在app的目录下的,并且文件夹名必须是static和templates;
如果想把模板目录和静态文件放在项目根目录怎么做?
这需要在settings.py中进行配置
对于static目录,需要在末尾写上STATICFILES_DIRS=(os.path.join(BASE_DIR, 'static'), #末尾的逗号不能少,表示一个元组
)
对于templates目录,需要在setting.py中的TEMPLATES部分中添加目录名,如下
'DIRS': ['templates'], 原先中括号里是空的
四. MTV开发模式
M代表模型:负责业务对象和数据库的关系映射
T代表模板:负责如何把页面展示给用户
V代表视图:负责业务逻辑,并在适当时候调用model和template
除了上面的3层之外,还需要一个URL分发器,就是url.py文件,它的作用是将一个个URL页面分发给不同的view处理,view再调用相应的model和template
五. url.py
1. 基本用法
from django.conf.urls import url
from hello import views
urlpatterns = [
url(r'^hello/$', views.hello)
]
2. URL模式
urlpatterns = [
url(正则表达式,view函数,参数,别名,前缀)
]
例如url(r'^hello/$', view.hello, {'a': '123'}) #这种写法参数是固定的
此时在view.py的hello函数中必须有参数a
def hello(request, a):
print(a) #可打印出123
别名用于方便调用此url
前缀用的不多,可写app的名字
3. URL常见写法示例,正则表达式
例子1:url (r'^test/(?P<id>\d{2})/$', views.test)
其中P表示分组
<id>表定义了一个可变参数,名为id,需要在view.py中的test函数中加入这个参数id,
def test(request, id):
print(id)
例子2:多个参数
url(r'^test/(?P<id>\d{2})/(?P<key>\w+)/$', views.test)
2个参数,一个参数名为2位数的id,另一个参数名为至少一个字符的key,实际访问方式例子为localhost:8000/test/23/ab/
同样在view.py中修改test的参数为
def test(request, id, key):
print(id)
print(key)
六. view.py
1. http请求中产生的两个核心对象
http请求:HttpdRequest
http响应:HttpResponse
所在位置:django.http
也就是可写成 from django.http import HttpRequest, HttpResponse
其实在views.py中定义的函数,其参数request就属于HttpRequest
判断方法:
def hello(request, a):
print(isinstance(request, HttpRequest))会返回true
2. HttpRequest对象的属性
1)path
def hello(request,a):
print(request.path)
请求页面的全路径,不包括域名,比如"/music/bands/"
2)method
返回请求类型,返回值全大写表示,例如
def hello(request,a):
print(request.method)
if request.method == 'GET':
do something()
elif request.method == 'POST':
do something_else()
3)request.POST和request.GET
views.py中定义函数如下
def hello(request, a):
print(request.POST.get('key')) #用POST方式来接收模板文件中提供的数据。这里的POST也可改成GET,此时表单中也需改为get
使用post提交的时候,需要用到模板中的表单,(当然get方式也能用表单来获取数据)定义如下
<form method="post" action="/hello/">
{% csrf_token %} #post方式必须加这行,get方式不需要
<input type="text" value="" name="key">
<input type="submit" value="提交">
当使用get方式时,可以直接在网页中输入localhost:8000/hello/?name=12345,这样在view.py中如下写,就能打印出name的值:12345
def hello(request,a):
print(request.GET.get('name')
4)user
user是一个django.contrib.auth.models.User对象,代表当前登陆的用户,如果访问用户当前没有登陆,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你可以通过user的is_authenticated()方法来辨别用户是否登陆;
if request.user.is_authenticated():
#do something for logged-in users
else:
#do something for anoymouse users
只有激活Django中的AuthenticationMiddleware时该属性才可用
5)session
唯一可读写的属性,代表当前会话的字典对象,只有激活Django中的session支持时该属性才可用
3. HttpRequest对象的方法(部分),
get_full_path() 返回包含查询字符串的完整请求路径,包含?后面的参数,使用方法:request.get_full_path(), 返回结果类似于“/music/bands/?print=true"。
对比其path属性,request.path返回的结果不包含后面的参数
4. 在HttpResponse对象上扩展的常用方法
1)render, render_to_response, redirect
render 其实是返回一个HttpResponse对象的快捷方式
redirect也是response对象
详见https://docs.djangoproject.com/en/1.9/topics/http/shortcuts
例子
from django.shortcuts import render,render_to_response,redirect
from django.contrib.auth.models import User
def hello(request, a):
user_list = User.objects.all()
return render(request,'table.html', {'user_list' : user_list})
上面一行等价于
return render_to_response('table.html', {'user_list': user_list})
return redirect('http://www.baidu.com')
2)对于HttpRequest对象来说,是由Django自动创建,但是HttpResponse对象就必须我们自己创建。 每个view中的request请求处理的方法必须返回一个HttpResponse对象。
HttpResponse类在django.http.HttpResponse
构造HttpResponse
response = HttpResponse("here is your site")
response = HttpResponse("text only", mimetype="text/plain") #指定返回类型
3)HttpResponse的子类
HttpResponseRedirect
构造函数接收单个参数:重定向到的URL。可以是全URL,例如http://www.baidu.com或者相对URL,例如/search.
注意这将返回HTTP状态码302,表示临时跳转
HttpResponsePermanentRedirect
同HttpResponseRedirect一样,但是返回永久重定向(HTTP状态码301)
HttpResponseNotFound 返回404
JsonResponse 返回Json字符串
更详细的有关request和response对象的介绍
5. 其他常用方法
locals() :可直接将函数中的所有变量全部传给模板,变量包括系统变量和自定义的变量
return render_to_response('table.html', locals()) 这个命令和上面1)中例子的效果一样,算是一种简写的方式
七. 数据库的配置
1. mysql驱动程序
PyMySQL :
https://pypi.python.org/pypi/PyMySQL 纯python的驱动,支持python3, 推荐用这个,支持pip安装,pip install pymysql
2. 在setting中配置
mysql引擎名称:django.db.backends.mysql
DATABASES = {
‘default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'hello', 数据库名
'USER': 'root',
'PASSWORD': '1234',
# 'HOST': '',
# 'PORT': '',
}
}
3,在项目settings.py目录里的init.py加入
import pymysql
pymysql.install_as_MySQLdb()
八. ORM机制讲解
1. 定义
对象关系映射:object relational mapping, 简称ORM,用于实现面向对象编程语言里不同类型系统间的数据转换,也就是说用面向对象的方式去操作数据库的创建表,增加,删除,查询等操作
2. mysql查询
1)query属性
例子
def hello(request, a):
user_list = User.objects.all()
print (user_list.query) #会打印出select查询语句
2)配置django日志的详细使用详解
在setting.py末尾加入如下代码, 就可在控制台显示由ORM生成的mysql代码
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': True,
},
},
}
3)使用开发工具django_debug_toolbar
九. models.py
1.创建数据模型
作者:有名字
作者详情:性别,年龄,email等,和作者是1对1
出版商:名称,地址,所在城市,省,国家,网站
书籍:有书名和出版日期,一本书可有多个作者,一个作者也可写多本书,一本书由一个出版商出版,所以出版商和书籍是一对多的关系(one to many),也被称为外键
from django.db import models
class Publisher(models.Model):
name=models.CharField(max_length=30)
address=models.CharField(max_length=50)
website = models.URLField()
class Author(models.Model):
name = models.CharField(max_length=30)
class AuthorDetail(models.Model):
sex = models.BooleanField(max_lenght=1, choices((0,'男'),(1,'女'),))
email = models.EmailField()
birthday = models.DateField()
author = model.OneToOneField(Author)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToMany(Author)
publisher = models.ForeighKey(Publisher)
publication_date = models.DateField()
说明:
1. 每个数据模型都是django.db.models.Model的子类。它的父类Model包含了所有必要的和数据库交互的方法,并提供了一个简洁漂亮的定义数据库字段的语法
2. 每个模型相当于单个数据库表(这条规则的例外情况是多对对关系,此时会多生成一个关系表),每个属性名就是这个表的一个字段名
2. 模型常用的字段选项
null (null=True|False)
数据库字段的设置是否可以为空(数据库进行验证)
blank(null=True|False)
字段是否为空django会进行验证(表单进行验证)
choices 配置字段可选属性的定义
default 字段的默认值
help_text 字段文字帮助
primary_key 是否主键,如果没显示指定,django会自动增加一个默认主键,id = models.AutoField(primary_key=True)
unique 是否唯一,对于数据表而言
verbose_name 字段的详细名称,如果不指定属性,默认使用字段的属性名称
例如name = models.CharField(max_length=30, verbose_name="名称")
也可简写成
name = models.CharField("名称", max_length=30) #此时名称参数必须写在前面
字段类型和字段选项的设置详解
3. 定义数据模型的扩展属性
通过内部类Meta给数据模型类增加扩展属性, 比如会影响后台显示的名称
class Meta:
verbose_name = '名称'
verbose_name_plural = '名称复数形式'
ordering = ['排序字段']
更多详见https://docs.djangoproject.com/en/1.9/ref/models/options/
4. 定义模型的方法
用处:比如新建了一个书名,在后台显示的新条目名为book_object, 如果想改变这个名字,就可用定义模型方法
定义模型方法可将当前对应的数据,组装成具体的业务逻辑
例子,定义__unicode__()让对象有个自定义的名字
在python2中用__unicode__(), python3里用__str__()
def __str__(self):
return self.name
5. 数据库同步操作技巧
1. 创建应用程序后,app目录下会生成一个migrations目录,作用是存放通过makemigrations命令生成的数据库脚本。
migrations目录下必须有__init__.py才能正常的使用数据库同步的功能
2. 一张数据表django_migrations,记录数据库脚本的使用情况。
表中的字段:
app: app名字
name: 脚本的文件名称
applied:脚本的执行时间
3. 数据库相关的命令
flush: 清空数据库,恢复数据库到最初状态;没记录日志,生产环境下很危险。
python manage.py flush
makemigrations: 生成数据库同步脚本
python manage.py makemigrations会生成所有app的数据库脚本;也可指定单个app,比如python manage.py makemigration hello
migrate:执行同步脚本,来同步数据库;同样可指定单个app
showmigrations:查看生成的数据库同步脚本
sqlflush: 查看生成清空数据库的脚本
sqlmigrate: 查看数据库同步的sql语句
例如:python manage.py sqlmigrate hello 0001_initial
终极大招
在开发过程中,数据库同步误操作后,难免会遇到后面不能同步成功的情况。怎么搞?两种方法如下
1. 生产环境下,分析生成的数据库脚本和django_migrations的同步记录是否匹配
2. 开发环境下,把migration目录下的脚本(除__init__.py)全部删除,再把整个数据库删掉之后创建一个新数据库,数据库同步操作再来一遍
十. ORM常见操作
首先进入shell界面,python manage.py shell
1. 增加
create和save方法
1)增加一个作者记录
from hello.models import *
Author.objects.create(name="lilei")
AuthorDetail.objects.create(sex=False,email='[email protected]', address='beijing',author_id=1)
注意最后的author_id=1, 其中author_id是authordetail表中自动生成的字段名,1的值是通过在Author表中插入作者记录后自动生成的id号。
主外键的插入方式有两种,第一种是通过id的方式,这里就是通过id方式;第二种是通过对象的方式,在下面的例子中会说明
2)增加一个出版社
pub = Publisher()
pub.name = '电子工业出版社'
pub.address = '华阳'
pub.city = '四川'
pub.save()
3)增加一个书籍
Book.objects.create(title='python实现', publisher=pub.publication_date='2009-05-06')
增加多对多关系
book=Book.objects.get(id=1)
auther = Author.object.get(id=1)
book.authors.add(author)
小结:
1)objects是model默认管理器,它提供一系列方法来管理数据,比如create是objects的方法
2)插入主外键关系的时候,可以用对象的方式,也可直接用关联id的方式
3)插入多对多关系的时候要分步操作
4)save()是model对象的方法
2. 修改
update和save方法
实例
1)修改id为1的作者的名字为jack,性别为女
author=Author.objects.get(id=1)
author.name='jack'
author.save()
2)修改名为"电子工业出版社"的出版社地址为中和,城市为成都
Publisher.objects.filter(id=1).update(city='成都', address=
'中和')
注意:
update是QuerySet对象的方法,而filter()的返回类型为QuerySet
3. 查询
1)查询所有出版社信息
Publisher.objects.all()
可用type(Publlisher.objects.all())查看返回类型
注意:
1)ORM中的查询是惰性查询,比如Publisher.objects.all()只是返回了一个QuerySet(查询结果集对象),并不会马上执行sql,而是调用QuerySet的时候才执行
简单说就是print(Publisher.objects.all())会执行sql,显示出查询结果;不加print()就不执行
2)在django shell中不是惰性查询
4. 删除
delete方法
1)删除id为1的书籍信息, 会删除所有表中的书籍信息
Book.objects.filter(id=1).delete()
2)删除city为成都的出版社
Publisher.objects.filter(city='成都').delete()
注意:
1)django中的删除默认是级联删除
2)delete方法也是 QuerySet对象的方法
5. 查询常用的API,使用Queryset
API使用详解官方文档
例下
from hello.models import *
pub_list = Publisher.objects.all()
type(pub_list) #返回类型为Qeuryset
QuerySet的特点
1)可遍历,
for pub in pub_list:
print(pub)
2)可切片
pub_list[:1]
常见的查询相关的API
1)get(**kwargs):返回于所有筛选条件相匹配的对象,返回结果只有一个。如果符合条件的对象超过一个,就抛出MultipleObjectsReturn异常,如果没找到符合条件的对象,抛出DoesNotExist异常。
注意它返回的不是QuerySet对象,而是models的对象,比如pub=Publisher.object.get(id=1)
type(pub)返回结果为class 'hello.models.Publisher'
2)all(): 查询所有结果
3)filter(**kwargs):包含与所筛选条件相匹配的对象,结果可有多个
4)exclude(**kwargs):包含那些与所选条件不匹配的对象,和filter相反
5)order_by(*fields):对查询结果排序
6)reverse():对查询结果反向排序
7)distinct():从返回结果中剔除重复记录
8)values(*fields):返回一个ValuesQuerySet, 一个特殊的QuerySet子类,运行后得到的是一个可迭代的字典序列
9)values_list(*fields): 与values()功能一样,只是返回结果是元组序列
10)count():返回数据库中匹配查询(QuerySet)的对象数量
11)first(): 返回第一条记录,等价于[:1][0]
例如Publisher.objects.all().first()
12) last(): 返回最后一个记录,等价于[::-1][0]
13) exists() :如果QuerySet包含数据,就返回True,否则返回False
例如:Publisher.objects.all().exists()
实例
1. 查询id为1的书籍信息,并只显示书籍名和作者
Book.objects.filter(id=1).value('title','author'), 返回的是字典
2. 查询所有的出版社信息,并按id降序排列,并尝试使用reverse方法进行反向排序
Publisher.objects.all().order_by('-id')或者
Publisher.objects.all().order_by('id').reverse()
3. 查询出版社所在的城市信息,城市信息不要重复
Publisher.objects.all().values('city').distinct()
4.查询城市是北京的出版社,尝试使用exclude方法
Publisher.objects.filter(city='北京')
Publisher.objects.exclude(city='北京')
5. 查询男作者的数量
AhthorDetail.objects.filter(sex=0).count()
6. 多表查询
python manager.py shell
from hello.models import *
1)查询作者的所有完整信息
注意authordetail表中没有作者名字,名字在author表中
AuthorDetail.objects.value('sex','email','address','author__name')
author是authordetail表中的外键字段,在外键后面加2个下划线跟上author表中的name属性,就可输出author表中的名字
2)查询<<我的祖国>>这本书的作者名字,出版社名字
书和作者是多对多关系
Book.objects.filter(title='我的祖国').values('authors__name')
Book.objects.filter(title='我的祖国').values('publisher__name')
3)查询胡大海写了什么书
Book.objects.filter(authors__name='胡大海').values('title')
4)查询广东人民出版社出了什么书
Book.objects.filter(publisher__name='广东人民出版社').values('title')
5)查询广东人民出版社都有哪些作者出过书
Book.objects.filter(publisher__name='广东人民出版社').values('author__name')
多表查询技巧
1. 两个下划线可生成连接查询,查询关联的字段信息
2. _set提供了对象访问相关联表数据的方法,使用相关模型的小写名称,下划线和单词set。但是这种方法只能是关键类访问外键类
book = Book.objects.filter(publisher__name='广东人民出版社')
book.authors_set.all() 这种方法会报错
因为authors是Book类中的外键,这样操作行不通
正确方法如下,我们有模型Publisher和Book,而Book类中我们通过外键关联到了Publisher。
下面我们定义一个publisher对象表示一个出版社,就可以使用publisher.book_set的方法获取相关联的书籍信息了
publisher = Publisher.objects.get(name='广东人民出版社')
publisher.book_set.all().value
7. 聚集查询和分组查询
先看QuerySet里的两个函数
1. annotate(*args,**kwargs):可以为QuerySet中的每个对象添加注解。通过计算查询结果中的每个对象所关联的对象集合,得到总值(也可以是平均值等),用于分组查询
2. aggregate(*args, **kwargs): 通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中的每个参数指定一个包含在字典中的返回值。用于聚合查询
一些聚合函数所在位置:django.db.models,举例如下
1. Avg: 返回所给字段平均值
2. Count:根据所给的关联字段返回被关联model的数量
3. Max: 返回所给字段的最大值
4. Min:返回所给字段的最小值
5. Sum:计算所给字段值总和
实例
1. 在Book模型中增加一个price属性
price = models.DecimalField(max_digits=5, decimal_places=2, default=10)
2. 查询广东人民出版社出了多少本书
Publisher.objects.filter(name='广东人民出版社').count() #注意count是小写, 是QuerySet提供的一个方法
如果使用聚合函数,如下
from django.db.models import *
Publisher.objects.filter(name='广东人民出版社').aggregate(Count('name'))
可以自定义个别名
Publisher.objects.filter(name='广东人民出版社').aggregate(mycount = Count('name'))
3. 查询胡大海出的书的总价格是多少
Book.objects.filter(authors__name = '胡大海').aggregate(Sum('price'))
4. 查询各作者出书的总价格是多少, 需要进行分组
Book.objects.values('authors__name').annotate(Sum('price'))
其中values()可以当做分组条件,annotate()用于分组查询
5. 查询各出版社最便宜的书价
Book.objects.values('publisher__name').annotate(Min('price'))