路由和orm
一、起步 1、安装django: pip install Django 安装成功之后会在python安装目录下面生成django-admin.py和django-admin.exe文件 2、创建工程: django-admin startproject m9d5 3、创建app python manage.py startapp app01 4、启动服务 python manage.py makemigrations python manage.py migrate python manage.py runserver 127.0.0.1:8001 5、设置静态文件和模板文件的目录 1、找到settings.py里面,在最下面加上: STATIC_DIRS = (os.path.join(BASE_DIR, 'static'),) 2、设置模板文件路径,settings.py里面找到TEMPLATES对应的列表,在DIRS所在的列表里面添加: 'DIRS': [os.path.join(BASE_DIR, 'templates'),], 二、路由: """ 路由后面的斜杠要加都加,要不加都不加 """ #基础一对一 path('cmdb/', views.index), path('cmdb/login/', views.login), path('cmdb/home/', views.home), # CBV的路由书写方式 path('cmdb/welcome/', views.Welcome.as_view()), #利用正则,多个url对应一个函数 需要from django.urls import re_path re_path('cmdb/detail-(\d+).html', views.detail), re_path('cmdb/detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail), #用name字段程序更加灵活,模版文件里用{% url 'r1' %},修改url的时候的不用改模版地址 path('cmdb/host/', views.host, name="r1"), """ 模版文件里用{% url 'r1' num1 num2 %} {% url 'r1' pid=num1 uid=num2 %} 如果要获取完整url,可以利用<form action="{{ request.path_info }}"> request.path_info获取当前url """ re_path('cmdb/host-(\d+)-(\d+)', views.host, name="r2"), """ 如果要根据接收的路由生成新的路由 from django.urls import reverse 可根据路由是位置参数还是关键字参数,选择使用args还是kwargs v = reverse('route_name', args=(3, 5), kwargs={'nid':3, 'uid':5}) """ re_path('cmdb/host-(?P<nid>\d+)-(?P<uid>\d+)', views.host, name="r3"), -------------------- 路由分发: #app01的url请求分发到app01下面的urls路由文件里 #app02的url请求分发到app02下面的urls路由文件里 urlpatterns = [ path('admin/', admin.site.urls), path('app01/', include('app01.urls')), path('app02/', include('app02.urls')), ] 然后在app01和app02的urls.py里面添加from app01 import views和from app02 import views 三、Model数据库操作: ORM分为两种,db first和code first db first: 先执行原生sql语句创建表,再用对象去操作 code first: 先创建类,然后用类去创建表和操作数据库 如:django和sqlalchemy 数据库接口在models.py里 1、创建类 from django.db import models # Create your models here. class UserInfo(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=64) 2、注册app INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', #增加此项 ] 3、执行 python manage.py makemigrations #在migrations里面创建记录 python manage.py migrate #根据记录创建表结构 如果在已有的表上新增一列: class UserInfo(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=64) age = models.IntegerField() 运行python manage.py makemigrations的时候会有两种选择: 1、在cmd里给定一个默认值; 2、在models.py里面age = models.IntegerField(null=True) 注意如果要用mysql: django默认使用mysqldb模块连接mysql,python3没有 要在project同名目录下的__init__.py里面加上如下: import pymysql pymysql.install_as_MYSQLdb() 4、外键操作 创建表 class Users(models.Model): uid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) user_group = models.ForeignKey('UserGroup', on_delete=models.CASCADE) class UserGroup(models.Model): gid = models.AutoField(primary_key=True) section = models.CharField(max_length=32) 创建数据 #创建部门表数据 # models.UserGroup.objects.create(section='技术') # models.UserGroup.objects.create(section='行政') # models.UserGroup.objects.create(section='财务') #创建职员表数据,user_group是django类,数据库给外键自动增加_id后缀 # models.Users.objects.create(name='Eric', user_group_id=1) # models.Users.objects.create(name='Alice', user_group_id=2) # models.Users.objects.create(name='Tom', user_group_id=3) 数据查询的三种方式: models.Users.objects.all() #取出users表里的所有数据,QuerySet,内部都是对象 python取出方法: for i in user_list: print(i.uid, i.name, i.user_group.section) html取出方法: {% for i in item %} {{ i.id }} {% endfor %} ----------------------------------------------------------- models.Users.objects.all().values('uid', 'name', 'user_group__section') #QuerySet,内部都是字典类型[{'uid':1, 'name':'Eric'}, {}, ...] python取出方法: for i in user_list: print(i['uid'], i['name'], i['user_group__section']) html取出方法: {% for i in item %} {{ i['id'] }} {{ i.['name'] }} 只能取这两个字段 {% endfor %} ----------------------------------------------------------- models.Users.objects.all().values_list('uid', 'name') #QuerySet,内部都是元组类型 [(1, 'Eric'), (2, 'Alice'), ...] python取出方法: for i in user_list: print(i[0], i[1]) html取出方法: {% for i in item %} {{ i.0 }} {{ i.1 }} {% endfor %} #获取一个对象,如果不存在就报错 models.Users.objects.get(id=1) #获取一个对象,如果不存在就返回None models.Users.objects.filter(id=1).first() models.Users.objects.filter(id__gt=1) #gt=greatthan lt=litterthan 关联查询的三种方式: 方式一: user_list = models.Users.objects.all() for i in user_list: # i.user_group是UserGroup object print(i.uid, i.name, i.user_group.section) 方式二: models.Users.objects.filter(id__gt=0).values('uid', 'user_group_id', 'user_group__section') {{ forloop.counter }} #正序从1开始计数 {{ forloop.counter0 }} #正序从0开始计数 {{ forloop.revcounter }} #倒序从1开始计数 {{ forloop.revcounter0 }} #倒序从1开始计数 {{ forloop.first }} #是否第一个计数 第一个True其他False {{ forloop.last }} #是否最后一个计数 最后一个True其他False {{ forloop.parentloop }} #父循环的信息,是一个字典 多对多数据表的创建: 方式一:自定义第三张表,好处是字段数可以自定义添加 #主机表 class Host(models.Model): host_name = models.CharField(max_length=32) #应用表 class Application(models.Model): app_name = models.CharField(max_length=32) #主机和应用的关系连接表 class AppToHost(models.Model): hid = models.ForeignKey(to='Host', to_field="id") aid = models.ForeignKey(to='Application', to_field="id") 方式二:django自动创建第三张表,缺点是第三张表只有三个字段,不能主动添加 #主机表 class Host(models.Model): host_name = models.CharField(max_length=32) #应用表 class Application(models.Model): app_name = models.CharField(max_length=32) r = models.ManyToManyField('Host') #自动生成第三张表 第三张表的名字是:app01_application_r 字段名是application_id和host_id 要添加关系,方法如下: obj = models.Application.get(id=1) obj.r.add(1) # application_id=1 host_id=1 obj.r.add(2,3,4) obj.r.add(*[2,3,4]) # 把id为1的application关联2,3,4 obj.r.remove(1) obj.r.remove(*[2,3]) #移除 obj.r.remove(2,3) obj.r.clear() #清除application_id=1的所有 obj.r.set([1,2,3]) #去掉原先的关系,设置为新值 四、ajax无跳转发送数据 $.ajax({ url:'/app01/index' //url地址 type: 'POST' //提交方式 data:{ //提交的数据 也可以$('#form_id').serialize()表单全部序列化 'username':'Eric', } dataType:'JSON', //默认success返回的数据都是字符串,如果要转换为json要用parse(),加上这个字段可以返回json格式 traditional:true, //加上此字段,发送的数据可以直接写列表,django端接收用getlist() success:function(data){ //数据发送成功后的回调函数 }, error:function(){ //错误信息接收函数 }, }) ajax后台接收到数据,可以两种返回方式:HttpResponse和render HttpResponse更加灵活,可以返回字典类型的数据 render只能返回字符串 ajax不能用redirect返回数据:如果想跳转,可以在前端里面用js跳转,location.reload()和location.href='url' 五、视图获取请求相关信息 def home(request): print(type(request)) # from django.core.handlers.wsgi import WSGIRequest # for k, v in request.environ.items(): # #循环输出请求头对应字段和内容 # print(k, v) print(request.environ['HTTP_USER_AGENT']) #打印客户端的代理信息 return HttpResponse('app01, home') 六、模板继承和模板导入 公共模板: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> {% block css %}{% endblock %} </head> <body> <h1>My Blog</h1> {% block content %}{% endblock %} <script src='/static/jquery-1.12.4.js'></script> {% block js %}{% endblock %} </body> </html> 应用模板的文件,extends只能继承一个模板,导入使用:{% include 'tag.html' %} {% extends 'master.html' %} {% block css %} <link rel="stylesheet" href="/static/common.css"> {% endblock %} {% block content %} <div class='content' style="background-color:orange;"> <p>据哈市警方撒娇的分公司地方</p> <p>据哈市警方撒娇的分公司地方</p> <p>据哈市警方撒娇的分公司地方</p> <p>据哈市警方撒娇的分公司地方</p> <p>据哈市警方撒娇的分公司地方</p> </div> {% endblock %} {% block js %} <script src='/static/my.js'></script> {% endblock %} simple_tag和filter {{ 'Eric'|lower }} 自带的filter把Eric变小写 想创建自己的规则: 一、在对应的app下面创建templatetags文件夹,在里面添加.py文件 二、在模板文件里使用{% load py文件名不带后缀不加引号 %} py文件里的内容如下示例: from django import template from django.utils.safestring import mark_safe #死套路不能更改变量名 register = template.Library() #套路 @register.simple_tag def say(a, b): return 'Hello', a+b @register.filter def play(a, b): return 'basketball' @register.filter def eat(a, b): return a+b 如果是simple_tag,在模板里的使用方法:{% say 参数1 参数2 参数3 %} 函数名+空格+参数1+参数2 如果是filter,在模板里的使用方法:{{ '参数1'|play:'参数2, 参数3' }} 两种方式的优缺点: simple_tag 优点:参数数量不限制,直接后面跟空格+参数 缺点:不能做if条件 filter 优点:可以做if条件 缺点:参数数量最低一个最多两个,使用方式如:{{ '参数1'|函数名:'参数2' }} 参数2也可以不加引号是个整数 如果要传多个参数,可以在参数2里面用字符串的形式传入,然后split分割出来 七、COOKIE # res是对象 res = redirect('/app01/index') res.set_cookie('user1', username) # 设置cookie,关闭浏览器失效 request.COOKIES.get('user1') #获取cookie set_cookie()的参数如下: key 键 value 值 max_age=None 超时时间 expires=None 超时时间(IE requires expires, so set it if hasn't been already.) path='/' Cookie生效的路径,/表示根路径,特殊的:根路径的cookie可以被任何url的页面访问 domain=None Cookie生效的域名 secure=False https传输 httponly=False 只能http协议传输,无法被javascript获取(不是绝对,底层抓包可以获取到,也可以被覆盖) 八、小结 request.body http请求体 request.POST(request.body) request.GET(request.body) request.FILES(request.body) request.xxxx.getlist request.Meta() http请求头 request.method request.path_info request.COOKIES return a = 'China' HttpResponse('字符串或者bytes') response = HttpResponse(a) response['name'] = 'Eric' #显示在响应头内 response.set_cookie() return response render redirect cookie 存储在浏览器客户端的一个字典 不设置超时时间,就是关闭浏览器失效 session django的session是存储在数据库里, 存储在服务端的一个字典,只给用户返回一个随机字符串作为通行证, 用户再次请求,客户端字符串和服务端比较 { 'dfsdfdsfds':{...}, 'sdfgdfgf':{...}, } # 生成随机字符串 # 写到用户浏览器cookir # 保存在session中 # 在随机字符串对应的字典中设置相应的内容... request.session['username'] = 'Eric' # django就这一步就完成所有的session操作 如果要判断用户是否登陆,request.session['is_login'] = True ------------- session的操作: - session依赖于cookie - 服务器端session: request.session['k1'] # 取值,没有就报错 request.session.get('k1', None) # 取值,没有返回None request.session['k1'] = 123 # 设置值 request.session.setdefault('k1', '123') # 没有就设置默认值 request.session.clear() del request.session['k1'] # 删除某个键 # 所有键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用户sesssion的随机字符串 request.session.session_key # 将所有session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查用户session的随机随机字符串在数据库中是否存在 request.session.exists('session_key') # 删除当前用户的所有session数据 request.session.delete('session') # session的默认超时时间是两周 request.session.set_expiry(value) * 如果value是个整数,session会在秒数后失效 * 如果value是个datetime或timedelta,session就会在这个时间失效 * 如果value是0,用户关闭浏览器session会失效 * 如果value是None,session会依赖全局session失效策略。 - 配置文件中设置默认操作: SESSION_COOKIE_NAME = 'sessionid' # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = '/' # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = True # 是否Https传输cookie(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) #登陆之后,失效日期从每次刷新之后开始计时 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) django支持5种session模式 1、数据库 SESSION_ENGINE = 'django.contrib.sessions.backends.db' 2、缓存 SESSION_ENGINE = 'django.contrib.sessions.backends.cache' SESSION_CACHE_ALIAS = 'default' CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache' 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ], }, 'db1': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache' 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ], }, } 3、文件 SESSION_ENGINE = 'django.contrib.sessions.backends.file' SESSION_FILE_PATH = None #缓存文件路径,如果没None,则使用tempfile获取一个临时地址进行存储 4、缓存+数据库 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' 5、加密cookie csrf:GET请求页面,服务器发送加密字符串给页面,客户端携带加密字符串POST提交表单才被允许 1、普通提交要在html表单里面加上{% csrf_token %},要不然就forbidden 2、ajax提交要携带随机字符串: 方法一: $.ajax({ headers: {'X-CSRFtoken': $.cookie(csrftoken)}, }) 方法二: # 也可以用下面方式,为所有ajax请求设置请求头 $.ajaxSetup({ beforeSend: function(xhr, settings){ xhr.setRequestHeader('X-CSRFtoken', $.cookie(csrftoken)); } }) 3、如果想为某个views单独设置csrf from django.views.decorators.csrf import csrf_exempt, csrf_protect # 不想用csrf就在函数前面加上 @csrf_exempt # 想用csrf就在函数前面加上 @csrf_protect 九、中间件 1、找到settings.py文件里面的MIDDLEWARE,里面加上自己的中间件路径: 如:'Middle.m1.Row1' # 工程目录下的Middle模块下面的m1文件里面的Row1类 中间件的本质就是一个类 2、中间件创建方法 from django.utils.deprecation import MiddlewareMixin class Row1(MiddlewareMixin): def process_request(self, request): print('Eric') def process_request(self, request): pass def process_response(self, request, response): pass """ @ view_func 对应的Views函数名 @ view_func_args 路由中的位置参数的值 @ view_func_kwargs 路由中的关键字参数的值 """ def process_view(self, request, view_func, view_func_args, view_func_kwargs): pass #异常处理 def process_exception(self, request, exception): if isinstance(exception, ValueError): return HttpResponse('出错') """ 如果views中的函数返回的对象中含有render方法执行此方法 如果要httpresponse和redirect执行可以用下面方法 Class Foo: def render(self): return HttpResponse('OK') # Views函数 def test(self, request): return Foo() # 用此方法可以让process_template_response都执行 """ def process_template_response(self, request, response): return response # 必须要返回response请求才会继续往下执行
中间件各方法图示: