-
概述
用python语言写的开源web开发框架,并遵循MVC设计
名称来源于比利时的爵士音乐家DjangoReinhardt,他是一个吉普赛人,主要以演奏吉它为主,还演奏过小提琴等
主要目的是简便、快速的开发数据库驱动的网站 >>新闻类的网站,单纯的是数据的更新
调代码复用,多个组件可以很方便的以"插件"形式服务于整个框架,Django有许多功能强大的第三方插件,你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展性。它还强调快速开发和DRY(DoNotRepeatYourself)原则。
-
特点
-
工程搭建
在Web应用中,通常有一些业务功能模块是在不同的项目中都可以复用的,故在开发中通常将工程项目拆分为不同的子功能模块,各功能模块间可以保持相对的独立,在其他工程项目中需要用到某个特定功能模块时,可以将该模块代码整体复制过去,达到复用。
在Flask框架中也有类似子功能应用模块的概念,即蓝图Blueprint。
Django的视图编写是放在子应用中的。
创建出来的子应用目录文件虽然被放到了工程项目目录中,但是django工程并不能立即直接使用该子应用,需要注册安装后才能使用。
备注 更改子应用的创建模板 在虚拟环境的site-packages中找到Django 找到conf ,更改里面的app 和 project 模板
-
创建视图
同Flask框架一样,Django也用视图来编写Web应用的业务逻辑。
Django的视图是定义在子应用的views.py中的。
-
配置
配置文件 settings.py
静态文件
项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。
路由说明
-
请求与响应
请求 Request
包含了用户向后台发送的所有数据
未命名参数按定义顺序传递
url(r'^weather/([a-z]+)/(\d{4})/$', views.weather),
正则匹配所需要的数据,按顺序匹配下来的数据, 在视图中依次代入变量。
http://127.0.0.1:8000/beijing/12345/
defweather(request, city, year): 正则匹配出来的第一个是字母,将代入city,
第二个匹配的是数字,代入year
print('city=%s' % city)
print('year=%s' % year)
return HttpResponse('OK')
city > beijing
year > 12345
命名参数按名字传递,正则的分组()
url(r'^weather/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.weather),
defweather(request, year, city): 名字已经被定义,固定的名字接收固定的参数
print('city=%s' % city)
print('year=%s' % year)
return HttpResponse('OK')
查询字符串
# /qs/?a=1&b=2&a=3
defqs(request):
a = request.GET.get('a')
b = request.GET.get('b')
alist = request.GET.getlist('a')
print(a) # 3
print(b) # 2
print(alist) # ['1', '3']
return HttpResponse('OK')
表单类型
defget_body(request):
a = request.POST.get('a')
b = request.POST.get('b')
alist = request.POST.getlist('a')
print(a)
print(b)
print(alist)
return HttpResponse('OK')
form data >> a:yy; b:12; a:cc
a > yy
b > 12
alist > [yy,cc]
非表单
{"a": 1, "b": 2}
可以进行如下方法操作:
import json
defget_body_json(request):
json_str = request.body #获取请求体中非表单 bytes形式的json数据
json_str = json_str.decode() #bytes转换为json字符串类型
req_data = json.loads(json_str) #将json数据转换成字典
print(req_data['a'])
print(req_data['b'])
return HttpResponse('OK')
响应Response
状态码
HttpResponseRedirect 301
HttpResponsePermanentRedirect 302
HttpResponseNotModified 304
HttpResponseBadRequest 400
HttpResponseNotFound 404
HttpResponseForbidden 403
HttpResponseNotAllowed 405
HttpResponseGone 410
HttpResponseServerError 500
jsonresponse
dict={'a':'2'}
dict_json = json.dumps(dict)
return HttpResponse(dict_json)
若不转换数据格式直接返回,则返回的数据只有一个a,数据不全
转换成json数据才能全部返回
cookie
Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)
Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明
Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等。服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型记住用户名。
Cookie是存储在浏览器中的一段纯文本信息
Session
中间件
CACHE设置
CACHES = {
"default":{
"BACKEND" :"django_redis.cache.RedisCache",
"LOCATION":"redis://127.0.0.1:6379/1",
"OPTIONS":{
"CLIENT_CLASS":"django_redis.client.DefaultClient",
}
},
"django_redis": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/10",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
所选择的字符串
}
setting中配置
CACHES = { #缓存数据库配置
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1", #用的一号库
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default" 使用默认的配置CACHE中的default
启用session
1、用户前端发起一个登陆请求,将账号密码传到服务端,账户名密码验证通过登陆成功
2、登陆成功,状态记录,写入一个session数据,在服务器中保存一份,产生一个sessionid,返还前端
3、前端将sessionid信息保存到cookie中,
4、下次在访问的时候,携带cookie数据到后端,cookie数据中有sessionid,用sessionid找到对应的session空间,找到用户信息,说明用户登录状态还在,找不到,说明用户已经退出
request.session['name'] = 'yy'
request.session['age'] = '22'
request.session['sex'] = 'man'
准备数据,给session中添加三条信息.
在redis中查看sessionid对应的值,二进制数据中存储三个信息 name age sex
"\x80\x04\x95(\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x02yy\x94\x8c\x03age\x94\x8c\x0222\x94\x8c\x03sex\x94\x8c\x03man\x94u."
-
类视图与中间件
类视图
函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。
defregister(request):
"""处理注册"""
# 获取请求方法,判断是GET/POST请求
if request.method == 'GET':
# 处理GET请求,返回注册页面
return render(request, 'register.html')
else:
# 处理POST请求,实现注册逻辑
return HttpResponse('这里实现注册逻辑')
from django.views.generic import View
classRegisterView(View):
"""类视图:处理注册"""
defget(self, request):
"""处理GET请求,返回注册页面"""
return render(request, 'register.html')
defpost(self, request):
"""处理POST请求,实现注册逻辑"""
return HttpResponse('这里实现注册逻辑')
#from django.views import View
from django.views import View
class funcview(View):
def get(self,request):
#业务逻辑
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
def delete(self, request):
return HttpResponse('delete')
def put(self, request):
return HttpResponse('put')
$
url(r'^class/',views.funcview.as_view())
url(r'^class_add/',views.funcview.as_view())
假如存在着两个正则,不加$的话,正则只会匹配到第一个class,第二个class_add永远不会执行,
as_view()
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
1 在前端以不同的请求方式发过来数据的时候,在dispatch中进行判断,请求方 式的小写是否在 http_method_names列表中,
2 使用getattr(),接收三个参数判断该对象是否含有该方法,如果没有就返回默认的方法.
,self是类视图中的类对象. 第二个是要求证请求方式的小写形式. 第三个是默认函数.,
handler为类视图底下相应的方法. handler == get方法
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
3 返回的handler()即为get()方法的调用.handler中的request参数也就被get方法所接收.get方法调用完成,返回一个结果给前端,.
getattr(object,'method',default)
class view(object):
def get(self):
print('get')
return 'res'
def def_func(): 自定义默认方法跟class没有关系,单独存在的
print('aaa')
return '啊啊啊'
v = view()
func=getattr(v,'get')
#(getattr判断该对象中是否有get方法,有的话就返回方法名字,这里赋值给了func)
func1 = getattr(v,'post',def_func)
#(getattr判断该对象中是否有post方法,没有返回的是设置的默认方法.def_func)
func() >> get
func1() >> aaa
hasattr(obj,name)
class Student(object):
def name(self):
print(self.name)
stu = Student()
print(hasattr(stu,'name')) >> True
print(hasattr(stu,'age')) >> False
setattr (obj , name ,value)
class Student(object):
def name(self):
print(self.name)
stu = Student()
setattr(stu,'age',12) 给Student类添加属性,age.值为12
print(stu.age) >> 12
delattr ( obj ,name)
class Student(object):
def name(self):
print('name')
stu = Student()
stu.name() >> name
delattr(stu,'name') 将类的那么属性删除
stu.name() AttributeError: Name
#首先定义一个装饰器
defmy_decorator(func):
defwrapper(request, *args, **kwargs):
print('自定义装饰器被调用了')
print('请求路径%s' % request.path)
return func(request, *args, **kwargs)
return wrapper
#如果需要为类视图的多个方法添加装饰器,
#但又不是所有的方法(为所有方法添加装饰器参考上面例子),可以直接在需要添加装饰器的方法上使用method_decorator
classDemoView(View):
@method_decorator(my_decorator) # 为get方法添加了装饰器
defget(self, request):
print('get方法')
return HttpResponse('ok')
@method_decorator(my_decorator) # 为post方法添加了装饰器
defpost(self, request):
print('post方法')
return HttpResponse('ok')
defput(self, request):# 没有为put方法添加装饰器
print('put方法')
return HttpResponse('ok')
中间件
-
模板
配置项
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',
],
},
},
]
语法
for 循环
{% for item in 列表 %}
循环逻辑
{{forloop.counter}}表示当前是第几次循环,从1开始
{%empty%} 列表为空或不存在时执行此逻辑
{% endfor %}
if 循环
{% if ... %}
逻辑1
{% elif ... %}
逻辑2
{% else %}
逻辑3
{% endif %}
日期格式化
Y表示年,格式为4位,y表示两位的年。
m表示月,格式为01,02,12等。
d表示日, 格式为01,02等。
j表示日,格式为1,2等。
H表示时,24进制,h表示12进制的时。
i表示分,为0-59。
s表示秒,为0-59。
继承
-
数据库
牛逼的ORM
配置项
配置数据库连接信息
settings.py中配置信息
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1', # 数据库主机
'PORT': 3306, # 数据库端口
'USER': 'root', # 数据库用户名
'PASSWORD': 'mysql', # 数据库用户密码
'NAME': 'django_demo'# 数据库名字
}
}
#在项目目录下__init__指定驱动
from pymysql import install_as_MySQLdb
install_as_MySQLdb()
在数据库中创建相应的库,准备模型迁移
模型准备
from django.db import models
模型类必须继承自models.Model类
字段类型
选项
db_column
name = models.Charfield(db_column='nm')
未指定,在数据库中的列名就是name
指定后,列名改为nm
choices
blank
verbose_name
help_text
对表的操作>>打印对象信息
实现__str__方法,定义返回的值
外键
多方
hero = HeroInfo.objects.filter(id=10).first()
hero 英雄对象
<HeroInfo: 令狐冲>
hero.hbook.btitle 英雄对象外键对应的书对象的btitle属性值
'笑傲江湖'
hero.hbook.bread 英雄对象外键对应的书对象的bread属性值
20
hero.hbook_id 英雄对象外键_id 对应书对象在书模型类中的id值
3
迁移模型
-
通过类和对象完成数据增删改(CRUD)查操作
增加
形式一
>>> hero = HeroInfo(
hname='孙悟空',
hgender=0,
hbook=book
)
>>> hero.save()
方法二
dic = {'btitle':'123','bpub_date':'1900-1-1','email':'123','aaa':'123123'}
book= BookInfo(**dic)
book.save()
形式二
HeroInfo.objects.create(
hname='沙悟净',
hgender=0,
hbook=book
)
<HeroInfo: 沙悟净>
查询
查询单一数据,
>>> BookInfo.objects.get(id=3)
<BookInfo: 笑傲江湖>
>>> BookInfo.objects.get(pk=3)
<BookInfo: 笑傲江湖>
问题:如果不存在会抛出异常
>>> BookInfo.objects.get(id=100)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/delron/.virtualenv/dj/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/delron/.virtualenv/dj/lib/python3.6/site-packages/django/db/models/query.py", line 380, in get
self.model._meta.object_name
db.models.DoesNotExist: BookInfo matching query does not exist.
查询所有数据。
BookInfo.objects.all()
<QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 雪山飞狐>, <BookInfo: 西游记>]>
查询结果数量。
>>> BookInfo.objects.count()
6
前端可以通过键值取值
{% for book in data2 %}
<li>{{ book.btitle }}----{{ book.bread }}</li>
{% endfor %}
value_list
books3 = BookInfo.objects.all().values_list('btitle','bpub_date')
前端获取
{% for book in data3 %}
<li>{{ book.0 }}----{{ book.1}}</li>
{% endfor %}
过滤查询
exact:表示判等。
查询编号为1的图书。
BookInfo.objects.filter(id__exact=1)
简写 BookInfo.objects.filter(id=1)
contains:是否包含。
查询书名包含'传'的图书。
book BookInfo.objects.filter(btitle__contains='传')
book[0] >> 射雕英雄传
startswith、endswith:以指定值开头或结尾。
查询书名以'部'结尾的图书
BookInfo.objects.filter(btitle__endswith='部')
isnull:是否为null。
查询书名不为空的图书。
BookInfo.objects.filter(btitle__isnull=False)
in
查询编号为1或3或5的图书
BookInfo.objects.filter(id__in=[1, 3, 5])
查询编号大于3的图书
BookInfo.objects.filter(id__gt=3)
exclude()
查询编号不等于3的图书
BookInfo.objects.exclude(id=3)
日期类
查询1980年发表的图书。
BookInfo.objects.filter(bpub_date__year=1980)
查询1980年1月1日后发表的图书。
BookInfo.objects.filter(bpub_date__gt=date(1990, 1, 1))
查询阅读量大于等于评论量的图书。
from django.db.models import F
BookInfo.objects.filter(bread__gte=F('bcomment'))
查询阅读量大于2倍评论量的图书。
BookInfo.objects.filter(bread__gt=F('bcomment') * 2)
原先:
BookInfo.objects.filter(id=3,btitle__contains='八')
与关系,and, 同时满足
不存在返回空列表。
使用Q对象:多应用于或关系
BookInfo.objects.filter(Q(id=3) | Q(btitle__contains='八' ))
只要有一个条件满足,就返回
BookInfo.objects.aggregate(Sum('bread'))
更改
hero = HeroInfo.objects.get(hname='猪八戒')
hero.hname = '猪悟能'
hero.save()
HeroInfo.objects.filter(hname='沙悟净').update(hname='沙僧')
删除
hero = HeroInfo.objects.get(id=13)
hero.delete()
HeroInfo.objects.filter(id=14).delete()
-
查询集 QuerySet
对查询集可以再次调用过滤器进行过滤
BookInfo.objects.filter(bread__gt=30).order_by('bpub_date')
惰性执行
当执行如下语句时,并未进行数据库查询,只是创建了一个查询集qs
qs = BookInfo.objects.all()
继续执行遍历迭代操作后,才真正的进行了数据库的查询
for book in qs:
print(book.btitle)
-
管理器Manager
方式1 在管理器类中补充定义新的方法
1 在模型类中定义闲的manger方法
classBookInfoManager(models.Manager):
#创建模型类,接收参数为属性赋值
defcreate_book(self, title, pub_date):
#创建模型类对象self.model可以获得模型类
book = self.model()
book.btitle = title
book.bpub_date = pub_date
book.bread=0
book.bcommet=0
book.is_delete = False
# 将数据插入进数据表
book.save()
return book
2 在模型类下使用定义的管理类
class BookInfo(models.Model):
...
books = BookInfoManager()
3 调用
book=BookInfo.books.create_book("abc",date(1980,1,1))
方式2 直接进行重写
1 修改原始查询集,重写all()方法
classBookInfoManager(models.Manager):
defall(self):
#默认查询未删除的图书信息
#调用父类的成员语法为:super().方法名
return super().filter(is_delete=False)
2 在模型类BookInfo中定义管理器
class BookInfo(models.Model):
...
books = BookInfoManager()
3 使用
BookInfo.books.all()
-
Admin站点管理
自定义管理页面
from django.apps import AppConfig
class UserConfig(AppConfig):
name = 'user',
verbose_name='图书管理'
定义后台管理类
继承自admin.ModelAdmin类
from django.contrib import admin
class BookInfoAdmin(admin.ModelAdmin):
pass
模型类列表展示
class BookInfoAdmin(admin.ModelAdmin):
...
list_display = ['id','btitle']
在后台界面显示出id,btitle信息,注意,这里是个元祖或者列表,
在models.py定义一个方法.
class BookInfo(models.Model):
...
def pub_date(self):
return self.bpub_date.strftime('%Y年%m月%d日')
返回值
pub_date.short_description = '发布日期'# 设置方法字段在admin中显示的标题
给方法添加描述信息,
在admi.py中修改显示字段信息
class BookInfoAdmin(admin.ModelAdmin):
...
list_display = ['id','atitle','pub_date']
class BookInfo(models.Model):
...
def pub_date(self):
return self.bpub_date.strftime('%Y年%m月%d日')
pub_date.short_description = '发布日期'
pub_date.admin_order_field = 'bpub_date'
classHeroInfo(models.Model):
...
defread(self):
return self.hbook.bread
#self.hbook 用外键关联到英雄对应的图书信息, .read显示数据对象的read属性.
read.short_description = '图书阅读量'
#添加后台显示名字
classHeroInfoAdmin(admin.ModelAdmin):
...
list_display = ['id', 'hname', 'hbook', 'read']
以块的形式嵌入
classHeroInfoStackInline(admin.StackedInline):
model = HeroInfo # 要编辑的对象
extra = 1# 附加编辑空白格的数量
以表格的形式嵌入
classHeroInfoTabularInline(admin.TabularInline):
model = HeroInfo #指定模型类
extra = 1 #指定空表格数量
admin.py修改一端类信息