Django框架基础知识汇总(无项目版)

网友投稿 455 2022-09-13

Django框架基础知识汇总(无项目版)

一、WEB框架本质

所有的Web应用,本质上其实是一个socket服务端,用户浏览器是一个socket客户端.

#!/usr/bin/env python#coding:utf-8# 服务端SOCKET import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n") client.send("Hello, Seven") def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8000)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()

python web程序分为两部分:服务器程序和应用程序。 服务器程序负责对socket服务器进行封装,在请求到来时对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。

WSGI(Web Server Gateway Interface)是一种规范。它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。

from wsgiref.simple_server import make_serverdef RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [bytes('

Hello, web!

', encoding='utf-8'), ] if __name__ == '__main__': = make_server('', 8000, RunServer) print("Serving HTTP on port 8000...") python#coding:utf-8from wsgiref.simple_server import make_server def index(): return 'index' def login(): return 'login' def routers(): urlpatterns = ( ('/index/',index), ('/login/',login), ) return urlpatterns def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) url = environ['PATH_INFO'] urlpatterns = routers() func = None for item in urlpatterns: if item[0] == url: func = item[1] break if func: return func() else: return '404 not found' if __name__ == '__main__': = make_server('', 8000, RunServer) print "Serving HTTP on port 8000..."

模板引擎生成静态内容:

在上叙中,所有login、index均返回给用户浏览器简单字符串,在现实的Web请求中会返回一个复杂的符合HTML规则的字符串,需将要返回给用户的HTML写在指定文件中,然后再返回,如:

Index

……views模块内容:def index(): # return 'index' f = open('index.html') data = f.read() return data def login(): # return 'login' f = open('login.html') data = f.read() return

开源工具Jinja2实现动态内容:

上述代码返回给用户HTML的内容来现实复杂的页面,但如何给用户返回动态内容?

自定义一套特殊的语法,进行替换使用开源工具jinja2,遵循其指定语法

# index.html文件内容:

{{name}}

    {% for item in
  • {{item}}
  • {% endfor

#遵循jinja2的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容:

#!/usr/bin/env python# -*- coding:utf-8 -*- from wsgiref.simple_server import make_serverfrom jinja2 import Template def index(): # return 'index' # template = Template('Hello {{ name }}!') # result = template.render(name='John Doe') f = open('index.html') result = f.read() template = Template(result) data = template.render(name='John Doe', user_list=['alex', 'eric']) return data.encode('utf-8') def login(): # return 'login' f = open('login.html') data = f.read() return data def routers(): urlpatterns = ( ('/index/', index), ('/login/', login), ) return urlpatterns def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) url = environ['PATH_INFO'] urlpatterns = routers() func = None for item in urlpatterns: if item[0] == url: func = item[1] break if func: return func() else: return '404 not found' if __name__ == '__main__': = make_server('', 8000, run_server) print "Serving HTTP on port 8000..."

三、Django基本配置

创建django程序:

终端命令:django-admin startproject sitenameIDE创建Django程序时,本质上都是自动执行上述命令其他常用命令:python manage.py runserver 0.0.0.0python manage.py startapp appname # 创建app程序模块python manage.py syncdbpython manage.py makemigrationspython manage.py migratepython manage.py createsuperuser

程序目录:

mysite mysit init.py settings.py urls.py wsgi.py templates manage.py

setting.py 配置文件:

Mysql数据库: DATABASES = { ‘default’: { ‘ENGINE’: ‘django.db.backends.mysql’, ‘NAME’:’dbname’, ‘USER’: ‘root’, ‘PASSWORD’: ‘xxx’, ‘HOST’: ”, ‘PORT’: ”, } }

备注: 由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替 如下设置放置的与project同名的配置的 init.py文件中 **import pymysql pymysql.install_as_MySQLdb()**

四、路由系统

单一路由对应: url(r’^index$’, views.index)

.

基于正则的路由: url(r’^index/(\d*)’, views.index), # 匹配0到无穷个数字 url(r’^manage/(?P\w*)/(?P\d*)’, views.manage), # 匹配到两个组,别名分别为name,id

.

添加额外的参数: url(r’^manage/(?P\w*)’, views.manage,{‘id’:333}), #> 视图函数中的参数可接收id

.

为路由映射设置名称:

url(r’^home’, views.home, name=’h1’), url(r’^index/(\d*)’, views.index, name=’h2’),

设置名称之后,可以在不同的地方调用,如: 模板中使用生成URL {% url ‘h2’ 2012 %}

函数中使用生成URL reverse(‘h2’, args=(2012,)) 路径:django.urls.reverse Model中使用获取URL 自定义get_absolute_url() 方法:

class NewType(models.Model): caption = models.CharField(max_length=16) def get_absolute_url(self): """ 为每个对象生成一个URL 应用:在对象列表中生成查看详细的URL,使用此方法即可!!! :return: """ # return '/%s/%s' % (self._meta.db_table, self.id) # 或 from django.urls import reverse return reverse('NewType.Detail', kwargs={'nid': self.id})

根据app对路由规则进行分类: url(r’^web/’,include(‘web.urls’)),

命名空间:# a. project.urls.py:from django.conf.urls import url,include urlpatterns = [ url(r'^a/', include('app01.urls', namespace='author-polls')), url(r'^b/', include('app01.urls', namespace='publisher-polls')),]# b. app01.urls.py:from django.conf.urls import urlfrom app01 import views app_name = 'app01'urlpatterns = [ url(r'^(?P\d+)/$', views.detail, name='detail')]# c. app01.views.py:def detail(request, pk): print(request.resolver_match) return HttpResponse(pk)# 以上定义带命名空间的url之后,使用name反向生成URL时候,应该如下:v = reverse('app01:detail', kwargs={'pk':11}){% url 'app01:detail' pk=12 pp=99

django中的路由系统和其他语言的框架有所不同,在django中每一个请求的url都要有一条路由映射,这样才能将请求交给一个views中的函数去处理。其他大部分的Web框架则是对一类的url请求做一条路由映射,从而是路由系统变得简洁。

五、模板

模版的执行:

模版的创建过程,是读取模版(其中嵌套着模版标签),然后将 Model 中获取的数据插入到模版中,最后将信息返回给用户.

def current_datetime(request): now = datetime.datetime.now() html = "It is now %s." % now return

内置模版语言:

{{ item }} # 获得字典数据{% for item in{{ item }}{% endfor # for循环出数据  forloop.counter  forloop.first  forloop.last {% if {% else {% endif # if判断数据母板:{% block{% endblock # title为母板名称子板:{% extends   {% block{% endblock # 子板继承母板title帮助方法:{{ item.event_start|date:"Y-m-d H:i:s"}} # 日期函数应用{{ bio|truncatewords:"30"{{ my_list|first|upper # simple_tag实现模板中加载函数执行{{ name|lower # 模版中应用小写的函数

自定义simple_tag模版语言:

定义:

a、在app中创建templatetags模块文件夹b、创建任意 .py 文件,如:xx.py# xx.py 文件内容如下:#!/usr/bin/env python#coding:utf-8from django import templatefrom django.utils.safestring import mark_safe register = template.Library() @register.simple_tagdef my_simple_time(v1,v2,v3): return v1 + v2 + v3 @register.simple_tag # 将函数注册到simple_tag中def my_input(id,arg): # 定义处理的函数 result = "" %(id,arg,) return mark_safe(result) # 创建html能识别的格式标签,而非字符

使用方式:

使用自定义simple_tag的html文件中导入之前创建的 xx.py 文件名{% load xx %} # 加载自定义的simple_tag 所在的文件名{% my_simple_time 1 2 3%} # 使用自定义的simple_tag函数名,并传入参数{% my_input 'id_username' 'hide'%}

注意:

在settings中配置当前app,不然django无法找到自定义的simple_tag:INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', # 注册

七、中间件

中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。 settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件。 中间件中可以定义5个方法,分别是:

process_request(self,request) # 重要process_view(self, request, callback, callback_args, callback_kwargs)process_template_response(self,request,response)process_exception(self, request, exception)process_response(self, request, response) # 重要

以上方法的返回值可以是None和HttpResonse对象。 如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户,不再走其他中间件的request方法

引用块内容:

创建中间件类:

class RequestExeute(object): def process_request(self,request): # 先执行所有中间件的process_request函数 pass def process_view(self, request, callback, callback_args, callback_kwargs): i =1 pass def process_exception(self, request, exception): pass def process_response(self, request, response): #所有中间件的所有process_request执行完后再执行process_response函数 return

注册中间件:

'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'wupeiqi.middleware.auth.RequestExeute',)

八、admin后台

使用步骤:

django amdin是django提供的一个后台管理页面,该管理页面提供完善的html和css, 使得在通过Model创建完数据库表后,就可对数据直接进行增删改查。

创建后台管理员:python manage.py createsuperuser配置url :url(r’^admin/’, include(admin.site.urls)) # 默认会自动生成注册和配置django admin后台管理页面:.

# 设置数据表名称:class UserType(models.Model): name = models.CharField(max_length=50) class Meta: # 元信息类 verbose_name = '用户类型' verbose_name_plural = '用户类型'

# 在admin.py模块中执行如下配置:from django.contrib import admin from app01 import models admin.site.register(models.UserType) # 将model模块中的UserType类注册到admin中admin.site.register(models.UserInfo)admin.site.register(models.UserGroup)admin.site.register(models.Asset)

# 打开表之后,可设定默认显示,需要在model.py模块中作如下配置:class UserType(models.Model): name = models.CharField(max_length=50) def __unicode__(self): return self.nameclass UserInfoAdmin(admin.ModelAdmin): list_display = ('username', 'password', 'email') # 默认显示

# 为数据表添加搜索功能:from django.contrib import admin from app01 import modelsclass UserInfoAdmin(admin.ModelAdmin): list_display = ('username', 'password', 'email') search_fields = ('username', 'email') # 搜索功能

# 添加快速过滤:from django.contrib import admin from app01 import models class UserInfoAdmin(admin.ModelAdmin): list_display = ('username', 'password', 'email') search_fields = ('username', 'email') list_filter = ('username', 'email') # 快速过滤

八、Model类

使用前准备工作:

创建数据库,设计表结构和字段使用 MySQLdb 来连接数据库,并编写数据访问层代码业务逻辑层去调用数据访问层执行数据库操作

import MySQLdb def GetList(sql): db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost') # 连接数据库 cursor = db.cursor() # 获得游标 cursor.execute(sql) # 执行sql语句 data = cursor.fetchall() # 获得数据表所有数据 db.close() # 关闭连接 return data def GetSingle(sql): db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost') cursor = db.cursor() cursor.execute(sql) data = cursor.fetchone() db.close() return

django为使用一种新的方式,即:对象关系映射(Object Relational Mapping,简称ORM)。 django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。

创建数据表:

- 基本结构:

from django.db import models class userinfo(models.Model): name = models.CharField(max_length=30) # 定义字符格式字段 email = models.EmailField() # 邮箱格式字段,会自动进行格式校验 memo = models.TextField() # 文本格式字段

- 字段:

# int自增列,必须填入参数 primary_key=TrueBigAutoField(AutoField) # bigint自增列,必须填入参数 primary_key=True# 注:当model中如果没有自增列,则自动会创建一个列名为id的列from django.db import modelsclass UserInfo(models.Model): username = models.CharField(max_length=32) # 自动创建一个列名为id的且为自增的整数列在其中class Group(models.Model): nid = models.AutoField(primary_key=True) # 手动自定义自增列 name = models.CharField(max_length=32)SmallIntegerField(IntegerField): # 小整数 -32768 ~ 32767PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) # 正小整数 0 ~ 32767IntegerField(Field) # 整数列(有符号的) -2147483648 ~ 2147483647BigIntegerField(IntegerField): # 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807# 自定义无符号整数字段:class UnsignedIntegerField(models.IntegerField): def db_type(self, connection): return 'integer UNSIGNED'# PS: 返回值为字段在数据库中的属性,Django字段默认的值为:'AutoField': 'integer AUTO_INCREMENT','BigAutoField': 'bigint AUTO_INCREMENT','BinaryField': 'longblob','BooleanField': 'bool','CharField': 'varchar(%(max_length)s)','CommaSeparatedIntegerField': 'varchar(%(max_length)s)','DateField': 'date','DateTimeField': 'datetime','DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)','DurationField': 'bigint','FileField': 'varchar(%(max_length)s)','FilePathField': 'varchar(%(max_length)s)','FloatField': 'double precision','IntegerField': 'integer','BigIntegerField': 'bigint','IPAddressField': 'char(15)','GenericIPAddressField': 'char(39)','NullBooleanField': 'bool','OneToOneField': 'integer','PositiveIntegerField': 'integer UNSIGNED','PositiveSmallIntegerField': 'smallint UNSIGNED','SlugField': 'varchar(%(max_length)s)','SmallIntegerField': 'smallint','TextField': 'longtext','TimeField': 'time','UUIDField': 'char(32)',BooleanField(Field) # 布尔值类型NullBooleanField(Field): # 可以为空的布尔值CharField(Field) # 字符类型,必须提供max_length参数, max_length表示字符长度TextField(Field) # 文本类型EmailField(CharField): # 字符串类型,Django Admin以及ModelForm中提供验证机制IPAddressField(Field) # 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制GenericIPAddressField(Field) # 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 # 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要 protocol="both"URLField(CharField) # 字符串类型,Django Admin以及ModelForm中提供验证 URLSlugField(CharField) # 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)CommaSeparatedIntegerField(CharField) # 字符串类型,格式必须为逗号分割的数字UUIDField(Field) # 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证FilePathField(Field) # 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 # 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹FileField(Field) # 字符串,路径保存在数据库,文件上传到指定目录 # 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorageImageField(FileField) # 字符串,路径保存在数据库,文件上传到指定目录 # 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串)DateTimeField(DateField) # 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]DateField(DateTimeCheckMixin, Field) # 日期格式 YYYY-MM-DDTimeField(DateTimeCheckMixin, Field) # 时间格式 HH:MM[:ss[.uuuuuu]]DurationField(Field) # 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型FloatField(Field) # 浮点型DecimalField(Field) # 10进制小数 # 参数: max_digits,小数总长度 decimal_places,小数位长度BinaryField(Field) # 二进制类型

- 参数:

null 数据库中字段是否可以为空db_column 数据库中字段的列名db_tablespacedefault 数据库中字段的默认值primary_key 数据库中字段是否为主键db_index 数据库中字段是否可以建立索引unique 数据库中字段是否可以建立唯一索引unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引unique_for_month 数据库中字段【月】部分是否可以建立唯一索引unique_for_year 数据库中字段【年】部分是否可以建立唯一索引verbose_name Admin中显示的字段名称blank Admin中是否允许用户输入为空editable Admin中是否可以编辑help_text Admin中该字段的提示信息choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息; 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date 如:{'null': "不能为空.", 'invalid': '格式错误'}validators 自定义错误验证(列表类型),从而定制想要的验证规则 from django.core.validators import RegexValidator from django.core.validators import EmailValidator,URLValidator,DecimalValidator, MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator 如: test = models.CharField( max_length=32, error_messages={ 'c1': '优先错信息1', 'c2': '优先错信息2', 'c3': '优先错信息3', }, validators=[ RegexValidator(regex='root_\d+', message='错误了', code='c1'), RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'), EmailValidator(message='又错误了', code='c3'), ] )

- 元信息:

class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) class Meta: # 数据库中生成的表名称: app名称 + 下划线 + 类名 (默认生成) db_table = "table_name" # 联合索引 index_together = [ ("pub_date", "deadline"), ] # 联合唯一索引 unique_together = (("driver", "restaurant"),) # admin中显示的表名称 verbose_name # verbose_name加s

- 补充知识:

1.触发Model中的验证和错误提示有两种方式:a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息。b. 调用Model对象的 clean_fields 方法,如: # models.py class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) email = models.EmailField(error_messages={'invalid': '格式错了.'}) # views.py def index(request): obj = models.UserInfo(username='11234', email='uu') try: print(obj.clean_fields()) except Exception as e: print(e) return HttpResponse('ok') # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。 2.Admin中修改错误提示: # admin.py from django.contrib import admin from model_club import models from django import forms class UserInfoForm(forms.ModelForm): username = forms.CharField(error_messages={'required': '用户名不能为空.'}) email = forms.EmailField(error_messages={'invalid': '邮箱格式错误.'}) age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'}) class Meta: model = models.UserInfo # fields = ('username',) fields = "__all__" class UserInfoAdmin(admin.ModelAdmin): form = UserInfoForm admin.site.register(models.UserInfo, UserInfoAdmin) # 注册到admin组件中

创建连表:

一对多:models.ForeignKey(其他表)多对多:models.ManyToManyField(其他表)一对一:models.OneToOneField(其他表)

应用场景:

一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。多对多:在某表中创建一行数据是,有一个可以多选的下拉框例如:创建用户信息,需要为用户指定多个不同的爱好,生成多条1个用户与不同爱好关联的数据,且一个爱好对应多个用户的数据。一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了,相当于一对多+唯一性约束)例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列新的数据

字段以及参数:

ForeignKey

ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要进行关联的表名 to_field=None, # 要关联的表中的字段名称 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 - models.CASCADE,删除关联数据,与之关联也删除 - models.DO_NOTHING,删除关联数据,引发错误IntegrityError - models.PROTECT,删除关联数据,引发错误ProtectedError - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) - models.SET,删除关联数据, a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) def func(): return 10 class MyModel(models.Model): user = models.ForeignKey( to="User", to_field="id" on_delete=models.SET(func),) related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:# 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') db_constraint=True # 是否在数据库中创建外键约束 parent_link=False # 在Admin中是否显示关联数据

OneToOneField:

OneToOneField(ForeignKey)to, # 要进行关联的表名to_field=None # 要关联的表中的字段名称on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为###### 对于一对一 ###### # 1. 一对一其实就是 一对多 + 唯一索引 # 2.当两个类之间有继承关系时,默认会创建一个一对一字段 # 如下会在A表中额外增加一个c_part_id列且唯一:class C(models.Model): nid = models.AutoField(primary_key=True) part = models.CharField(max_length=12)class A(C): id = models.AutoField(primary_key=True) code = models.CharField(max_length=1)

ManyToManyField:

ManyToManyField(RelatedField)to, # 要进行关联的表名related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: # 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10)symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段# 做如下操作时,不同的symmetrical会有不同的可选字段models.BB.objects.filter(...)# 可选字段有:code, id, m1class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=True)# 可选字段有: bb, code, id, m1class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=False)through=None, # 自定义第三张表时,使用字段用于指定关系表through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', through_fields=('group', 'person'), )class Membership(models.Model): group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64) db_constraint=True, # 是否在数据库中创建外键约束 db_table=None, # 默认创建第三张表时,数据库中表的名称

单表操作:

基本操作:

create(c1='xx', c2='oo') 增加一条数据,可以接受字典类型数据 **kwargs obj = models.Tb1(c1='xx', c2='oo') obj.save() 查 models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议) models.Tb1.objects.filter().first # 推荐该方式获取单条数据 models.Tb1.objects.all() #获取全部 models.Tb1.objects.filter(name='seven') #获取指定条件的数据 删 models.Tb1.objects.filter(name='seven').delete() 删除指定条件的数据 改 models.Tb1.objects.filter(name='seven').update(gender='0') 将指定条件的数据更新,均支持 **kwargs第2种修改数据方式: obj = models.Tb1.objects.get(id=1) obj.c1 = '111' # 对c1字段进行赋值班

进阶操作 (双下划线连表操作)

# 获取个数: models.Tb1.objects.filter(name='seven').count() # 大于,小于: models.Tb1.objects.filter(id__gt=1) 获取id大于1的值 models.Tb1.objects.filter(id__gte=1) 获取id大于等于1的值 models.Tb1.objects.filter(id__lt=10) 获取id小于10的值 models.Tb1.objects.filter(id__lte=10) 获取id小于10的值 models.Tb1.objects.filter(id__lt=10, id__gt=1) 获取id大于1 且 小于10的值,等价于and #in,范围有效值判断 models.Tb1.objects.filter(id__in=[11, 22, 33]) 获取id等于11、22、33的数据 models.Tb1.objects.exclude(id__in=[11, 22, 33]) 等价于 not in # isnull,判断是否为空 Entry.objects.filter(pub_date__isnull=True) # contains 表中字段是否包含某一个值 models.Tb1.objects.filter(name__contains="ven") models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.exclude(name__icontains="ven") # range 范围判断 models.Tb1.objects.filter(id__range=[1, 2]) 范围bettwen and # 其他类似: startswith,istartswith, endswith, iendswith, # order by排序 models.Tb1.objects.filter(name='seven').order_by('id') # asc升序 models.Tb1.objects.filter(name='seven').order_by('-id') # desc降序 # group by分组 from django.db.models import Count, Min, Max, Sum models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) #以id为分组条件,分组结果字段别名为c,获取每个组中num字段的数量 #等价于:SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" # limit限制查询数据, offset偏移 : models.Tb1.objects.all()[10:20] # regex正则匹配,iregex 不区分大小写: Entry.objects.get(title__regex=r'^(An?|The) +') # 至少有一个以An或The开头的内容 Entry.objects.get(title__iregex=r'^(an?|the) +') # date日期 Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) # 查询pub_date字段中日期为2005,1,1的数据 Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year: Entry.objects.filter(pub_date__year=2005) Entry.objects.filter(pub_date__year__gte=2005) # month: Entry.objects.filter(pub_date__month=12) Entry.objects.filter(pub_date__month__gte=6) # day: Entry.objects.filter(pub_date__day=3) Entry.objects.filter(pub_date__day__gte=3) # week_day: Entry.objects.filter(pub_date__week_day=2) Entry.objects.filter(pub_date__week_day__gte=2) # hour: Event.objects.filter(timestamp__hour=23) Event.objects.filter(time__hour=5) Event.objects.filter(timestamp__hour__gte=12) # minute: Event.objects.filter(timestamp__minute=29) Event.objects.filter(time__minute=46) Event.objects.filter(timestamp__minute__gte=29) # second(秒): Event.objects.filter(timestamp__second=31) Event.objects.filter(time__second=2) Event.objects.filter(timestamp__second__gte=31)

其它高级操作:

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) # 在所有的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)# 等价于((id=1)or(id=10)or(id=9))and((c1=1)or(c1=10)or(c1=9))# 通过sqlAlchemy执行原生SQL : from django.db import connection, connections cursor = connection.cursor() # 创建游标cursor = connections['default'].cursor() # 设置游标 cursor.execute("SELECT * from auth_user where id = %s", [1]) # 执行sql语句 row = cursor.fetchone() # 获取一条数据

连表操作:

表结构

class UserProfile(models.Model): user_info = models.OneToOneField('UserInfo') username = models.CharField(max_length=64) password = models.CharField(max_length=64) def __str__(self): return self.usernameclass UserInfo(models.Model): user_type_choice = ( (0, u'普通用户'), (1, u'高级用户'), ) user_type = models.IntegerField(choices=user_type_choice) name = models.CharField(max_length=32) email = models.CharField(max_length=32) address = models.CharField(max_length=128) def __str__(self): return self.nameclass UserGroup(models.Model): caption = models.CharField(max_length=64) user_info = models.ManyToManyField('UserInfo') def __str__(self): return self.captionclass Host(models.Model): hostname = models.CharField(max_length=64) ip = models.GenericIPAddressField() user_group = models.ForeignKey('UserGroup') def __str__(self): return

九、扩展内容:(上传文件)

自定义上传文件:

def upload_file(request): if request.method == "POST": obj = request.FILES.get('fafafa') # 获得文件对象 f = open(obj.name, 'wb') # 创建文件句柄,以字节形式写入文件 for chunk in obj.chunks(): # 获得文件块 f.write(chunk) # 将文件块写入文件 f.close() return render(request, 'file.html')

Form上传文件实例:

Form定义:class FileForm(forms.Form): ExcelFile = forms.FileField()

Model类定义:from django.db importclass UploadFile(models.Model): userid = models.CharField(max_length file = models.FileField(upload_to = './upload/') date = models.DateTimeField(auto_now_add=True)

views:def UploadFile(request): uf = FileForm(request.POST,request.FILES) # 将请求中的数据发送给定义的Form组件进行校验 if uf.is_valid(): # 校验通过,Form组件中包含用户输入到字段对应的值 upload = models.UploadFile() # 实例化Model类,获得对象 upload.userid = 1 # 为对象的userid赋值,可以对应成是为数据表中的字段赋值 upload.file = uf.cleaned_data['ExcelFile'] # 从Form组件中获得ExcelFile中的值传给Model类中对应数据表中的字段 upload.save() # 将该条数据保存到数据库 print

十、Form内置组件

django中的Form一般有2种功能:

输入html(生成表单组件)验证用户输入

Form:

#!/usr/bin/env python# -*- coding:utf-8 -*-import refrom django import formsfrom django.core.exceptions import ValidationError# 定义验证函数:def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') # 定义正则表达式规则,并进行编译 if not mobile_re.match(value): # 判定传入的参数是否匹配正则表达式的规则 raise ValidationError('手机号码格式错误') # 将手机输入框组件的错误信息添加到该字段对应的错误信息列表中class PublishForm(forms.Form): # 定义Form组件类 user_type_choice = ( (0, u'普通用户'), (1, u'高级用户'), ) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice, attrs={'class': "form-control"})) # 定制下拉框表单组件 title = forms.CharField(max_length=20, min_length=5, error_messages={'required': u'标题不能为空', 'min_length': u'标题最少为5个字符', 'max_length': u'标题最多为20个字符'}, widget=forms.TextInput(attrs={'class': "form-control", # 对组件添加属性 'placeholder': u'标题5-20个字符'})) # 默认提示内容 memo = forms.CharField(required=False, max_length=256, widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'详细描述', 'rows': 3})) phone = forms.CharField(validators=[mobile_validate, ], # 添加验证器,对用户在该表单组件中输入的值进行正则表达式函数的验证 error_messages={'required': u'手机不能为空'}, # 设置验证不通过时前端显示的错误信息 widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'手机号码'})) email = forms.EmailField(required=False, # 该字段非必填项 error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'}, widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

views:

def publish(request): ret = {'status': False, 'data': '', 'error': '', 'summary': ''} # 定义返回参数 if request.method == 'POST': request_form = PublishForm(request.POST) # 将请求数据传入Form组件进行验证 if request_form.is_valid(): request_dict = request_form.clean() # 获得Form组件验证过的数据 ret['status'] = True else: error_msg = request_form.errors.as_json() # 获得错误信息列表的json字符串 ret['error'] = json.loads(error_msg) # 将json字符串转换为字典对象 return HttpResponse(json.dumps(ret)) # 将字典对象转换成json字符串进行传输

扩展ModelForm:

在使用Model和Form时,都需要对字段进行定义并指定类型,通过ModelForm则可以省去From中字段的定义。class AdminModelForm(forms.ModelForm): class Meta: model = models.Admin #fields = '__all__' fields = ('username', 'email') widgets = { 'email' : forms.PasswordInput(attrs={'class':"alex"}), }

十一、跨站请求伪造攻击(crsf)

简介:

crsf会生成随机字符串发送给前端,前端请求可携带随机字符串来进行识别。 django为用户实现防止跨站请求伪造的功能, 通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。 而对于django中设置防跨站请求伪造功能有分为全局和局部。

.middleware.csrf.CsrfViewMiddleware - 局部:@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。注:from django.views.decorators.csrf

应用:

普通Form表单:

veiw中设置返回值:  return render_to_response('Account/Login.html',data,context_instance=RequestContext(request))   或者 return render(request, 'xxx.html', data) # 返回的request请求中包含crsf随机字符串发送给前端html中设置Token:  {% csrf_token %} # 从返回中的request中取出crsf_token随机字符串,再次发送请求时,将该值传递给中间件验证

Ajax实现跨站请求验证:

# html部分:{% csrf_token Ajax提交

十三、Session

简介:

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

数据库(默认)缓存文件缓存+数据库加密cookie

数据库Session:

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。

配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)

使用示例:

def index(request): # 获取、设置、删除Session中数据 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1'] # 获得所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 获得用户session的随机字符串,并非值 request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否存在(传入cookies中的key,它对应的value是随机字符串) request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key") # 设置session生命周期 request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。

缓存session:

# 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存

文件Session:

# 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存

缓存+数据库Session:

数据库用于做持久化,缓存用于提高效率: a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎

加密cookies:

a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎

扩展:Session用户验证(将验证封装成装饰器)

def login(func): def wrap(request, *args, **kwargs): # 如果未登陆,跳转到指定页面 if request.path == '/test/': return redirect(' return func(request, *args, **kwargs) return

十四、分页

Django内置分页:

# views:from django.shortcuts import renderfrom django.core.paginator import Paginator, EmptyPage, PageNotAnIntegerL = []for i in range(999): L.append(i)def index(request): current_page = request.GET.get('p') paginator = Paginator(L, 10) # 传入数据及每页需要显示的条数 # per_page: 每页显示条目数量 # count: 数据总个数 # num_pages:总页数 # page_range:总页数的索引范围,如: (1,10),(1,200) # page: page对象 try: posts = paginator.page(current_page) # has_next 是否有下一页 # next_page_number 下一页页码 # has_previous 是否有上一页 # previous_page_number 上一页页码 # object_list 分页之后的数据列表 # number 当前页 # paginator paginator对象 except PageNotAnInteger: posts = paginator.page(1) # 返回第1页 except EmptyPage: posts = paginator.page(paginator.num_pages) # 返回最后一页 return render(request, 'index.html', {'posts': posts}) #

# html:

    {% for item in
  • {{ item }}
  • {% endfor

自定义分页:

分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入,计算出应该在数据库表中的起始位置。

数据显示部分:1、设定每页显示数据条数2、用户输入页码(第一页、第二页...)3、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置4、在数据表中根据起始位置取值,页面上输出数据

页数显示部分:(如:[上一页][1][2][3][4][5][下一页])1、设定每页显示数据条数2、用户输入页码(第一页、第二页...)3、设定显示多少页号4、获取当前数据总条数5、根据设定显示多少页号和数据总条数计算出总页数6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置7、在数据表中根据起始位置取值,页面上输出数据8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

#!/usr/bin/env python# _*_coding:utf-8_*_from django.utils.safestring import mark_safeclass PageInfo(object): def __init__(self,current,totalItem,peritems=5): # current:当前页; totalItem:数据库查出的数据总条数; peritems:每页显示的条数,默认值为5条 self.__current=current self.__peritems=peritems self.__totalItem=totalItem""" 根据当前页码数计算出数据表的查询范围 """# 范围查询范例:models.UserInfo.objects.all()[page_info.start():page_info.end()] def From(self): # 根据当前页获得从数据库中进行范围查询时的起始序号 return (self.__current-1)*self.__peritems def To(self): return self.__current*self.__peritems # 根据当前页获得从数据库中进行范围查询时的结束序号 def TotalPage(self): # 计算显示页码部分的总页数 result=divmod(self.__totalItem,self.__peritems) #返回一个包含商和余数的元组,商为总页数 if result[1]==0: # 表示可以整除 return result[0] else: return result[0]+1 # 数据库总数/页面总数后还有多余的数据,所以总条数还需要加1页来显示剩余的数据""" 根据基础页链接,当前页,总页数计算出页码显示部分的内容 """def Custompager(baseurl,currentPage,totalpage): #基础页,当前页,总页数perPager=11 # 默认页码数显示范围为11# if 总页数<11#0 -- totalpage # 获得从起始页到总页数的范围,end页码数就是根据数据库总页数# if 总页数>11# if 当前页>5 获得 currentPage-5 -- currentPage+5的页码数#currentPage+5是否超过总页数,超过总页数,end就是总页数# if 当前页< 5 获得 0 -- 11 范围的页码数# 根据begin,end的范围显示所有的中间页码数begin=0end=0if totalpage <= 11:begin=0end=totalpageelse:if currentPage>5:begin=currentPage-5end=currentPage+5if end > totalpage:end=totalpageelse:begin=0end=11# 组装页码数显示模块pager_list=[]if currentPage<=1:first="首页"else:first="首页" % (baseurl,1)pager_list.append(first)if currentPage<=1:prev="上一页"else:prev="上一页" % (baseurl,currentPage-1)pager_list.append(prev)# 循环中间页,与数据表中的数据显示关联起来for i in range(begin+1,end+1):if i == currentPage: # 当前页选中状态,添加class样式temp="%d" % (baseurl,i,i)else:temp="%d" % (baseurl,i,i)pager_list.append(temp)#if currentPage>=totalpage:next="下一页"else:next="下一页" % (baseurl,currentPage+1)pager_list.append(next)# if currentPage>=totalpage:last="末页"else:last="末页" % (baseurl,totalpage)pager_list.append(last)result=''.join(pager_list) # 将列表数据通过拼接转换成字符串return mark_safe(result) #把字符串转成html语言

总结,分页时需要做三件事:创建处理分页数据的类根据分页数据获取数据输出分页HTML,即:[上一页][1][2][3][4][5][下一页]

十五、序列化

Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求一般返回的为Json格式。

serializers模块:对Queryset对象序列化

from django.core import serializers ret = models.BookType.objects.all() data = serializers.serialize("json", ret)

json.dumps:

import json #ret = models.BookType.objects.all().values('caption') ret = models.BookType.objects.all().values_list('caption') ret=list(ret) # 将元组列表对象转换为列表对象 result = json.dumps(ret) # 将列表对象序列化为json字符串

json.dumps时无法处理datetime日期,可通过自定义处理器来做扩展

import json from datetime import date from datetime import datetime class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, datetime): # 传入数据类型判断,时间 return o.strftime('%Y-%m-%d %H:%M:%S') # 格式化成字符串 elif isinstance(field, date): # 日期 return o.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field) # 调用父类中的default方法 # ds = json.dumps(d, cls=JsonCustomEncoder) # 扩展类的应用范例,d为日期数据

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:首席营销官:“爽文男主”黄峥急流勇退,拼多多继续“下沉”!
下一篇:营销最前线:刘强东“败”了,江苏新首富诞生,每天赚3亿,30年前是建筑工人!
相关文章

 发表评论

暂时没有评论,来抢沙发吧~