div标签和span标签
div标签用来定义一个块级元素,并无实际的意义。主要通过CSS样式为其赋予不同的表现。
span标签用来定义内联(行内)元素,并无实际的意义。主要通过CSS样式为其赋予不同的表现。
块级元素与行内元素的区别:
所谓块元素,是以另起一行开始渲染的元素,行内元素则不需另起一行。如果单独在网页中插入这两个元素,不会对页面产生任何的影响。
这两个元素是专门为定义CSS样式而生的。
注意:
关于标签嵌套:通常块级元素可以包含内联元素或某些块级元素,但内联元素不能包含块级元素,它只能包含其它内联元素
______________________________________________________________________________________
上传文件
form表单设置enctype="multipart/form-data"
views.py中取file_obj用request.FILES.get()

from django.shortcuts import render,HttpResponse # Create your views here. def upload(request): if request.method=='POST': file_obj=request.FILES.get('file') print(file_obj) with open(file_obj.name,'wb') as f: for line in file_obj: f.write(line) return render(request,'upload.html')

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post" action="upload.html" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="file"> <input type="submit" > </form> </body> </html>
form组件的作用
1.生成html代码
2.验证
3.把验证的错误信息保留在页面上并且保留原始数据
Form表单的基本实例

from django import forms from django.core.exceptions import ValidationError # 定义注册的form类 class RegForm(forms.Form): username=forms.CharField( max_length=16, label='用户名:', error_messages={ 'max_length':'最长为16位', 'required':'不能为空' }, # 为前端利用bootstrap添加东西 widget=forms.widgets.TextInput( attrs={'class':'form-control','placeholder':'username'}, ) ) password=forms.CharField( min_length=6, label='密码:', # widget= 用于设置html格式 widget=forms.PasswordInput( attrs={'class': 'form-control', 'placeholder': 'password'}, # 密码不消失 # render_value=True ), error_messages={ 'max_length': '最短为6位', 'required': '不能为空' } ) re_password=forms.CharField( min_length=6, label='重复密码:', # widget= 用于设置html格式 widget=forms.PasswordInput( attrs={'class': 'form-control', 'placeholder': 'password'}, ), error_messages = { 'max_length': '最短为6位', 'required': '不能为空' } ) email=forms.EmailField( label='邮箱:', widget=forms.widgets.EmailInput( attrs={'class': 'form-control', 'placeholder': 'email'}, ), error_messages={ # 格式错误 'invalid':'格式错误', 'required':'不能为空' } ) # 重写全局钩子函数,对确认密码进行确认 def clean(self): password=self.cleaned_data.get('password') re_password=self.cleaned_data.get('re_password') if password!=re_password: # 注意需要引入ValidationError self.add_error('re_password',ValidationError('两次密码不一致')) else: return self.cleaned_data
# 重写局部钩子函数,对username是否注册做校验,不是实时校验
def clean_username(self):
username=self.cleaned_data.get('username')
is_exit=models.UserInfo.objects.filter(username=username)
if is_exit:
self.add_error('username',ValidationError('用户名已存在'))
else:
return username 注意全局钩子函数返回cleaned_date,局部钩子函数返回具体值,钩子函数的名字都是固定的。
视图函数获取form_obj,传到前端(
return render(request, 'app01/reg.html', {'form_obj':form_obj})
)
form_obj=forms.RegForm()
POST方式接受数据之后,
obj=forms.RegForm(request.POST)
obj.is_valid()
obj.cleaned_data
css引入方式
<link rel="stylesheet" href="">
js引入方式
<script src=""></script>
前端Form表单中对form_obj的几种显示方式
(有文件上传是在form表单中加enctype="multipart/form-data"
label标签中for=“”填入id,可以实现在点击label=点击id的标签
)

<form novalidate action="/reg/" method="post" class="form-horizontal reg_form" enctype="multipart/form-data"> {% csrf_token %} {# form_obj的几种前端显示方式#} {# 方式一#} {# {{ form_obj.username.label }}#} {# {{ form_obj.username }}#} {# {{ form_obj.password.label }}#} {# {{ form_obj.password }}#} {# 方式二#} {# {% for item in form_obj %}#} {# {{ item.label }}#} {# {{ item }}#} {# {% endfor %}#} <div class="form-group"> <label for="{{ form_obj.username.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.username.label }}</label> <div class="col-sm-10"> {{ form_obj.username }} <span id="helpBlock2" class="help-block">{{ form_obj.username.errors.0 }}</span> </div> </div> <div class="form-group"> <label for="{{ form_obj.password.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.password.label }}</label> <div class="col-sm-10"> {{ form_obj.password }} <span id="helpBlock2" class="help-block">{{ form_obj.password.errors.0 }}</span> </div> </div> <div class="form-group"> <label for="{{ form_obj.re_password.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.re_password.label }}</label> <div class="col-sm-10"> {{ form_obj.re_password }} <span id="helpBlock2" class="help-block">{{ form_obj.re_password.errors.0 }}</span> </div> </div> <div class="form-group"> <label for="{{ form_obj.email.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.email.label }}</label> <div class="col-sm-10"> {{ form_obj.email }} <span id="helpBlock2" class="help-block">{{ form_obj.email.errors.0 }}</span> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">头像</label> <div class="col-sm-10"> <label for="avatar_id"> <img src="/static/default_avatar/default_avatar.jpg" id="avatar_img" width="50px" height="50px"> </label> <input type="file" name="avatar" id="avatar_id" style="display: none;"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default">注册</button> </div> </div> </form >
通过js代码实现对头像的预览

<script> {#通过js代码实现选中头像前端可以有显示#} {#找到头像input标签绑定chang事件#} $('#avatar_id').change(function () { {#创建一个读取文件的对象#} alert('123') var fileReader=new FileReader(); {#当前选中的头像文件 this指的是当前input框#} console.log(this.files[0]); {#读取选中的文件,这里的读取需要时间#} fileReader.readAsDataURL(this.files[0]); {#等到上一步读取完成,才能把图片加载出来在前端img标签中#} console.log(fileReader.result) fileReader.onload=function () { $('#avatar_img').attr('src',fileReader.result) } }) </script>
ajax的优缺点:
优点:
- AJAX使用Javascript技术向服务器发送异步请求;
- AJAX无须刷新整个页面;
- 因为服务器响应内容不再是整个页面,而是页面中的局部,所以AJAX性能高;
缺点:
- AJAX并不适合所有场景,很多时候还是要使用同步交互;
- AJAX虽然提高了用户体验,但无形中向服务器发送的请求次数增多了,导致服务器压力增大;
- 因为AJAX是在浏览器中使用Javascript技术完成的,所以还需要处理浏览器兼容性问题;
同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
同步通信方式与异步通信的概念
同步通信方式要求通信双方以相同的时钟频率进行,而且准确协调,通过共享一个单个时钟或定时脉冲源保证发送方和接收方的准确同步,效率较高;
异步通信方式不要求双方同步,收发方可采用各自的时钟源,双方遵循异步的通信协议,以字符为数据传输单位,发送方传送字符的时间间隔不确定,发送效率比同步传送效率低。
ajax提交是data中需要添加csrf信息
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
ajax基本实例:

$.ajax({ url:'/reg/', type:'post', {#利用ajax传文件必须加以下两个#} processData:false, contentType:false, data:{}, success:function (data) { {#数据提交成功返回data#} if (data.status){ {#有错误展示错误信息#} console.log(data.msg); {#将错误信息填写到页面上#} } else { } } })
通过ajax上传文件需要
processData:false, 告诉jquery不要处理我的数据
contentType:false, 告诉jquery不要设置content类型
并且
{#传递文件需要把数据加入FormData中,data:{}处直接data:formData#}
var formData=new FormData();
formData.append('username',$('#id_username').val());
formData.append('csrfmiddlewaretoken',$('[name="csrfmiddlewaretoken"]').val());
{#$('#avatar_id')可能是一个jQuery对象#}
{#$('#avatar_id')[0]是input框#}
{#$('#avatar_id')[0].files是一个Filelist#}
{#$('#avatar_id')[0].files[0]是一个File#}
formData.append('avatar',$('#avatar_id')[0].files[0]);
jquery中each循环
$.each((对象),function (k(对象中的键),v(对象中的值)) {
{#通过找id的方式找到input标签,在找到其下的span标签添加错误信息#}
$('#id_'+k).next('span').text(v[0])
})
JS知识
.next() 下一个标签
.next(‘span’).text()下一个span标签添加文本
获取焦点事件,就是鼠标点到的地方触发的函数
$('form input').focus(function () {
})
$('form input').blur(function () {
当失去焦点时触发的函数
})
$('').change(function () {
当发生改变时触发的函数
})
js实现页面跳转
location.href=url
from django.http import JsonResponse
返回一个json数据,适合用ajax返回success中的数据
return JsonResponse()
实现实时检测用户名是否存在

{#当失去焦点时,检测数据库用户名是否已被注册#} $('#id_username').blur(function () { $.ajax({ {#专门处理此问题函数#} url:'check_username_exit', type: 'get', data: { username:$('#id_username').val(), }, success:function (data) { if(data.status){ {#将错误信息添加到input标签下的span标签#} $('#id_username').next('span').text(data.msg) } } }) })

# 专门处理检查用户名是否已被注册 def check_username_exit(requesnt): ret={'status':0,'msg':''} username=requesnt.GET.get('username') r=models.UserInfo.objects.filter(username=username) if r: ret['status']=1 ret['msg']='用户名已存在' return JsonResponse(ret)
加到form表单中把自动补全关闭autocomplete="off"
钩子函数实现为form组件添加额外的校验,分为全局钩子和局部钩子函数
from django.core.exceptions import ValidationError

# 必须写在form类中 # 重写全局钩子函数,对确认密码进行确认 def clean(self): password=self.cleaned_data.get('password') re_password=self.cleaned_data.get('re_password') if password!=re_password: # 注意需要引入ValidationError self.add_error('re_password',ValidationError('两次密码不一致')) else: return self.cleaned_data # 重写局部钩子函数,对username是否注册做校验,不是实时校验 def clean_username(self): username=self.cleaned_data.get('username') is_exit=models.UserInfo.objects.filter(username=username) if is_exit: self.add_error('username',ValidationError('用户名已存在')) else: return username
中间件的五种方法:
process_request
process_view
process_exception
process_template_response
process_response
web请求的部分执行流程:
wsgi(从用户过来的)解析的信息,走中间件的process_request,然后走urls.py,走process_view,然后views.py,如果需要走process_exception,process_template_response,最后一定会走process_response。
中间件版的登录验证:

from django.contrib import admin from django.urls import path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), re_path(r'^index/$', views.index), re_path(r'^login/$', views.login, name='login'), ]

MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'middleware.middlewares.AuthMD', ]
需要在根目录建middleware目录

from django.utils.deprecation import MiddlewareMixin class AuthMD(MiddlewareMixin): white_list = ['/login/', ] # 白名单 balck_list = ['/black/', ] # 黑名单 def process_request(self, request): from django.shortcuts import redirect, HttpResponse next_url = request.path_info print(request.path_info, request.get_full_path()) if next_url in self.white_list or request.session.get("user"): return elif next_url in self.balck_list: return HttpResponse('This is an illegal URL') else: return redirect("/login/?next={}".format(next_url))

from django.shortcuts import render,HttpResponse,redirect # Create your views here. def index(request): return HttpResponse('this is index') def home(request): return HttpResponse('this is home') def login(request): if request.method == "POST": user = request.POST.get("user") pwd = request.POST.get("pwd") if user =='zw' and pwd == '123456': # 设置session request.session["user"] = user # 获取跳到登陆页面之前的URL next_url = request.GET.get("next") # 如果有,就跳转回登陆之前的URL if next_url: return redirect(next_url) # 否则默认跳转到index页面 else: return redirect("/index/") return render(request, "login.html")

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>登录页面</title> </head> <body> <form action="{% url 'login' %}" method="post"> {% csrf_token %} <p> <label for="user">用户名:</label> <input type="text" name="user" id="user"> </p> <p> <label for="pwd">密 码:</label> <input type="text" name="pwd" id="pwd"> </p> <input type="submit" value="登录"> </form> </body> </html>
bootstrap中的媒体对象就像博客首页每一篇文章的一块地方。
以下是py文件用django环境的方法,ORM基于对象的查询和基于queryset的查询

# 以下是py文件用django环境的方法 from django.db import connection import os if __name__=='__main__': # 从manage.py中复制出来的 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Blog_2.settings") # 启动django import django django.setup() # 这里引入models不能再主函数外边引入不知道为什么 from app01 import models # 基于对象的查询 SQL:子查询 a=models.Article.objects.first() a.user.avatar # 查看执行的所有SQL语句,加上[-1]指最后一条 print(connection.queries[-1]) print(a.user.avatar) # 基于queryset的查询,SQL:join连表查询 # .filter()和.all()都是基于queryset的查询 b=models.Article.objects.filter(pk=1) b.values('user__avatar') print(b.values('user__avatar')) print(connection.queries[-1]) # .values_list()输出queryset[(),()]元组里是数据库数据 # .values()输出queryset[{},{}]字典里是数据库数据 c=models.Article.objects.all() print(c.values_list()) print(c.values())
# 查看ORM语句执行的所有SQL语句,加上[-1]指最后一条
from django.db import connection
print(connection.queries[-1])
在settings.py中设置可以输出全部的SQL语句

LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
在django 中用户只能看到static中的文件,通过设置为用户打开一个media。前端可以通过拼接从而达到显示上传头像的目的。

# 用户是无权访问/static/之外的路径的,所以需要开一个, # django用户上传的都叫media文件 MEDIA_URL='/media/' MEDIA_ROOT=os.path.join(BASE_DIR,'media') # 还需要在urls.py里写url

from django.views.static import serve from django.conf import settings re_path(r'^media/(?P<path>.*)$',serve,{"document_root":settings.MEDIA_ROOT}),
ORM foreign key 基于对象的查询和基于queryset的查询(正反向)。
manytomany基于对象查询方法和foreign key 相同。onetoone基于对象反向查询时只写小写表名,不用加_set。

#Article有对Classification的外键 # 基于对象的 # 正向 a = models.Article.objects.first() # print(a.classification.blog) # 反向 b=models.Classification.objects.first() # print(b) # print(b.article_set.all()) # print(type(b.article_set)) # print(type(b.article_set.all())) # 基于queryset # 正向 c=models.Article.objects.filter(pk=1).values('classification__title') print(c) # 反向 d=models.Classification.objects.filter(pk=1).values('article__title') print(d) for i in d: print(i['article__title'])
ORM分组和聚合查询
聚合:
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。 键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的.
from django.db.models import Avg, Sum, Max, Min, Count
>>> from django.db.models import Avg, Sum, Max, Min, Count >>> models.Book.objects.all().aggregate(Avg("price")) {'price__avg': 13.233333}
自己指定一个名称 >>> models.Book.objects.aggregate(average_price=Avg('price')) {'average_price': 13.233333}
查询多个值 >>> models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price")) {'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}
分组:
annotate是ORM中的分组,关键字前面内容是按照什么分组,关键字前面values()括号中填的是分组的字段,最后的values()中是需要显示的内容。
总之:1.annotate前面内容是按照什么分组
2.最后values()是需要显示的内容。
建议对应原生SQL看看。
执行原生SQL:

from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) ret = cursor.fetchone()
extra构造额外的查询条件或者映射,如:子查询
按照月份给文章分类:
create_time_format_list=models.Article.objects.filter(user=user_obj).extra(
select={'create_time_format':'date_format(create_time,"%%Y-%%m")'}
).values('create_time_format').annotate(c=Count('nid')).values('create_time_format','c')
注意:create_time_format是一个变量
select date_format(create_time,"%Y-%m") from app01_article;实现对creat_tiem的格式化
extra中添加就是将日期格式化的语句。
路由系统中
re_path(r'(w+)/article/(d+)', views.article_detial),
(w+)(d+)加的括号就是参数,需要在视图函数中接受
ORM模版语言:
母版中添加
{% block main_body %}
{% endblock %}
子板添加
{% extends 母版所在html文件%}
{%block main_body%}
子板语句
{%endblock%}
自定义标签:
1.在app下创建python package 名字必须为templatetags
2.其下创建xx.py, xx为标签名,app必须被注册
from django import template register=template.Library() # 自定义tag 标签 @register.inclusion_tag('left_menu.html')#这里的html文件可以嵌入标签的地方 #@register.simple_tag def get_left_menu(arg): return { }#一般都是字典,对于inclusion_tag
3.在html中用标签
{%load xx %} xx为标签名
{%get_left_menu arg%}
render渲染:不论是html,css,js都认为是字符串,把{{}}{%%}替换中的
内容进行替换,在js中需要注意替换时可能找不到(就是未定义变量)
需要加双引号。
json: 是一种标准的格式。
python JSON(格式固定)
dict objects {"":""}
list,tuple array []
str string ""
int,float number
True true
False false
None null
所有json格式的数据本质都是json字符串,如objects类型'{"name":"aa"}'(注意外边必须是单引号,里面必须是双引号,才算是json字符串),
array类型'["x",2]',string类型'"abc"'(总之就是在外边加单引号),所有符合json字符串的都可以进行反序列化成需要的语言格式。
js中:JSON.stringify()和JSON.parse()用来序列化和还原JavaScript对象。
python中:json.dumps()和json.loads()用来序列化和反序列化。
ajax请求后端返回的几乎都是字典。
如果把js写成一个文件,然后引入,注意模板语言不能进行渲染js文件,所以需要把js文件中需要的值存在一个地方,然后再取出来,例如:在html页面多加一个<div id="info" style="display: none" article_obj_nid="{{ article_obj.nid }}" ></div>(自定义属性),在js文件中var article_id=$('#info').attr('article_obj_nid');
JS知识:
.attr()取到的是属性。
.hasClass('xx')如果有xx属性就返回true,否则返回false。
.text()取尖括号之间的值,也可以进行设置。
.val()取的是框里输入的内容,也可以设置。
parseInt()转换类型为int。
设置延时,将digg_tips的值设置为空,在1s之后:
setTimeout(function () {
$('#digg_tips').text('')
},1000);
F和Q操作:
from django.db.models import F,Q
在查询的时候有时需要用数据库本身的字段,需要用到F
给Article表的赞的数量增加一
models.Article.objects.filter(pk=article_id).update(up_like_count=F('up_like_count')+1)
filter()中的操作都是进行AND,如果需要OR就要用Q操作
示例1: 查询作者名是a或b的
models.Book.objects.filter(Q(authors__name="a")|Q(authors__name="b"))
你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
示例:查询作者名字是a并且不是2018年出版的书的书名。
>>> models.Book.objects.filter(Q(author__name="a") & ~Q(publish_date__year=2018)).values_list("title")
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。
例如:查询出版年份是2017或2018,书名中带a的所有书。 >>> models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="a")
模板语言显示多少楼,从1开始
#{{ forloop.counter }}楼
html中的空格
 
前端日期格式化
{{create_time|date:'Y-m-d H-i'}}
python 中datetime类常见:
datetime.datetime.now()
datetime.date
date.time
JS字符处理
根据字符取索引
.indexOf();
根据索引取字符
.charAt(3)
切片 包括前面不包括后面
.slice(1,5)
var 定义变量时,子作用域中加var是局部变量,不加是全局变量。
$('[name='xxx']') 应该算是属性选择器。
ajax添加评论是通过字符拼接html代码,然后append到指定地方。
评论树:后端把所有本篇文章评论传给前端,在前端构建评论树。Jsonrespoonse如果返回的不是字典需要设置safe=False

<script> var article_nid = $('#info').attr('article_obj_nid'); $.ajax({ url: 'blog/comment_tree/' + article_nid + '/', type: 'GET', success: function (data) { $.each(data,function (k,comment) { {#构建comment_item代码块#} var s='<div class="comment_item" my_id='+comment.nid+' parent_comment_id='+comment.parent_comment_id+'> <span>'+comment.content+'</span> </div>' {#如果是子评论#} if(comment.parent_comment_id){ var parent_comment_id=comment.parent_comment_id {#找到父评论,添加进父评论#} $('[my_id='+parent_comment_id+']').append(s) } {#根评论,直接添加#} else{ $('.comment_tree ').append(s) } }) } }) </script>

def comment_tree(request,article_nid): comment_list=models.comment.objects.filter(article=article_nid).values() comment_list=list(comment_list) # print(comment_list) return JsonResponse(comment_list,safe=False)
富文本编辑器kindeditor,需要下载引入

# 文章上传图片 def article_updown_pic(request): # 获取文件句柄,文件名可以通过print(request.POST)看出 import os,json from Blog_2 import settings img_obj=request.FILES.get('imgFile') # 通过路径拼接,把文件(图片)写到对应位置。因为是所有人都能看,所以放到media中 path = os.path.join(settings.MEDIA_ROOT, 'article_add_img',img_obj.name ) print(img_obj) print(type(img_obj)) with open(path,'wb') as f: for i in img_obj: f.write(i) # 这里返回是为了让图片成功在textarea框里预览 ret={ # 这里的参数不知道在哪找的 'error':0, 'url':'/media/article_add_img/'+img_obj.name, } return HttpResponse(json.dumps(ret))
部分js代码 处理上传文件(图片)

<script> KindEditor.ready(function(K) { {#textarea的id#} window.editor = K.create('#article_content',{ {#相关文档中发送文件的url和发送的额外参数#} uploadJson:'/article_updown_pic/', extraFileUploadParams:{ csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), }, }); }); </script>
beautifulsoup实现对desc的提取和过滤非法的标签
from bs4 import BeautifulSoup
bs=BeautifulSoup(字符串,'html.parser')
desc=bs.text[:150]+'...'(把html代码中的标签去掉,只要文字,取前150个字符,最后加...)
过滤非法标签,如script标签。
find_all()是找到所有标签,也可以直接加标签名参数,是找到对应标签。
for tag in bs.find_all():
if tag.name in ['script,...']:
删除在 ['script,...']中的标签
tag.decompose()
left join和inner join的区别:
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行
什么是url
协议:// IP:端口/路径?路径参数
***Xadmin
路由分发 视图函数部分换成 ([路由语句],None,None)
re_path(r'^test/',([
re_path(r'^test1$',test1),
],None,None)),
路径访问test/test1 才能正常访问到
路由分发中的^和$,限定必须以什么开头和结尾。
通过查看源码模仿admin自定义对表的增删改查url设计

"""xadmin_12_9 URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.0/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path,re_path from django.shortcuts import HttpResponse,render def test1(request): return HttpResponse('test1') def show_view(request): return HttpResponse('show_view') def edit_view(request,id): return HttpResponse('edit_view') def add_view(request): return HttpResponse('add_view') def delete_view(request,id): return HttpResponse('delete_view') def func_2(): lst=[] lst.append(re_path(r'^$',show_view)) lst.append(re_path(r'^add$',add_view)) lst.append(re_path(r'^(d+)/edit$',edit_view)) lst.append(re_path(r'^(d+)/delete$',delete_view)) return lst def func(): lst=[] # 查看源码知道admin.site.register(Book,book_admin)注册的类都存在_registry这个字典中 # self._registry[model] = admin_class(model, self) # 字典的键为类,如Book,值为book_admin对象,若不传,默认为ModelAdmin对象 for model,admin_class_obj in admin.site._registry.items(): app_name=model._meta.app_label #类对应的app名 model_name=model._meta.model_name #类对应的小写表名 lst.append(re_path(r'^%s/%s/'%(app_name,model_name), (func_2(),None,None)),) return lst urlpatterns = [ re_path(r'^test/',([ re_path(r'^test1',test1), ],None,None)), path('admin/', admin.site.urls), re_path(r'^xadmin/', (func(),None,None)), ]
django启动时,会把settings.py中INSTALLED_APP中导入的全部执行一次,发现执行'app01.apps.App01Config'的父类AppConfig,中含有ready方法,在子类中重写在django启动时就会运行,通过模仿admin得知autodiscover_modules('Xadmin')自动执行每一个app的Xadmin。
在类中,@property是属性,定义时像方法,使用时像字段,不伦不类。
换个角度看问题,换一种编程思想,有时候能方便很多。
HTML:
table标签
<table>
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>
from django.utils.safestring import mark_safe
使django不做处理,展示a标签
return mark_safe('<a href="%s">编辑</a>'%(reverse('edit',args=(obj.pk,))))
对model的操作:
model._meta.model_name 类名小写
model._meta.app_label 所在app的名字
model._meta.get_field(字段名).verbose_name 取字段的vervose_name
model._meta.get_field(字段名)这部分就是得到某个model(表)的字段对象。
url反向解析
url(r'^home', views.home, name='h1'),
url(r'^home/(d*)', views.index, name='h2'),
模板中使用生成URL {% url 'h2' 2012 %}
函数中使用生成URL reverse('h2', args=(2012,)) 路径:django.urls.reverse
Model中使用获取URL 自定义get_absolute_url() 方法
python 字典items()函数以列表返回可遍历的(键值)元组数组。一般用在for循环中。
ManyToMany实例:

class Tag(models.Model): '''标签表''' nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) blog = models.ForeignKey(to='Blog', to_field='nid',on_delete=models.DO_NOTHING) def __str__(self): return self.title class Meta: verbose_name = '标签' verbose_name_plural = verbose_name class Article(models.Model): '''文章表''' nid = models.AutoField(primary_key=True) user = models.ForeignKey(to='UserInfo', to_field='nid',on_delete=models.DO_NOTHING) title = models.CharField(max_length=32) desc = models.CharField(max_length=256) create_time = models.DateTimeField(auto_now_add=True) classification = models.ForeignKey(to='Classification', to_field='nid',null=True,blank=True,on_delete=models.DO_NOTHING) comment_count=models.IntegerField(default=0,verbose_name='评论数') up_like_count=models.IntegerField(default=0,verbose_name='点赞数') down_like_count=models.IntegerField(default=0,verbose_name='踩数') tags=models.ManyToManyField( # 和那张表进行关联 to='Tag', # 中间表 through='Article_to_Tags', # 关联方向是 article ->tag through_fields=('article','tag'), ) def __str__(self): return self.title class Meta: verbose_name = '文章' verbose_name_plural = verbose_name class Article_to_Tags(models.Model): '''文章-标签表''' nid=models.AutoField(primary_key=True) article=models.ForeignKey(to='Article',to_field='nid',on_delete=models.DO_NOTHING) tag=models.ForeignKey(to='Tag',to_field='nid',on_delete=models.DO_NOTHING) class Meta: verbose_name = '文章-标签' verbose_name_plural = verbose_name def __str__(self): return '%s-%s'%(self.article,self.tag)
dir()显示相关信息时,当显示的是对象时,对象的属性和方法都会显示,方法只显示方法名。
a标签添加get参数<a href="?page=..&o=.."></a>
三元运算
>>> a=10
>>> b=4
>>> c=a if a>b else b
>>> c
10
js的娱乐:
document.body.contentEditable=true;
queryset排序:
queryset.order_by('字段') -字段表示倒序
如果request.GET.get('字段‘)
是None,可以设置一个默认值,request.GET.get('字段','默认值')
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)
ORM一些进阶操作:如filter(qq__contains='')

# 获取个数 # # 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的值 # 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')) # 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) +') # Entry.objects.get(title__iregex=r'^(an?|the) +') # date # # Entry.objects.filter(pub_date__date=datetime.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)
python 动态构造类(构造类的特殊方法):
type(name, bases, dict) 返回一个新的type对象. 基本上是 class 语句的动态形式.
参数: name , 字符串, 制定要构造类的名字, 赋给新对象的 __name__ 属性;
bases,一个tuple,指定类的父类,赋给新对象的__bases__ 属性;
dict, 字典类型,类中的成员,赋给新对象的__dict__ 属性
例如:
一般情况上面的X就和后面类名X一样(习惯上,也比较方便)
在类中要知道__new__方法是在__init__方法之前执行的。
子类执行父类方法有两种方式
super(子类名,self).父类的方法名()
父类名.父类中的方法(self,....)
在ModelForm中,
ModelForm类直接在前端显示是空的标签
ModelForm类(instance=model对象), 如:.objects.get(nid=obj_nid) ,就会显示对象有的信息在标签里。
ModelForm类(request.POST,instance=model对象)
form_obj=ModelForm类(request.POST,instance=model对象)
form_obj.save()就实现对当前对象数据的更新。
form_objform_obj=ModelForm类(request.POST)
form_obj.save()实现重新创建一行数据。
表示将光标的位置回退到本行的开头位置 表示将光标的位置回退一位
有时候需要多试试:
就像m2m字段,可以models.表的类.m2m字段.rel.model.all() 获取对应m2m表的所有记录
form_obj.instance.m2m字段.all() 获得当前记录m2m字段已有的记录。
form表单中onsubmit=""是当提交表单时触发
<form method="post" onsubmit="return selected_data();">
的,要加上return ,对应函数应该返回True,才能正常提交表单。(暂时不做深究)
*arg和**kwargs
*arg表示任意多个无名参数,类型为tuple;
# *允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
**kwargs表示关键字参数,为dict
# **,关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。例如函数名(a=1,b=2)
<i> 标签显示斜体文本效果。
<i> 标签和基于内容的样式标签 <em>类似。它告诉浏览器将包含其中的文本以斜体字(italic)或者倾斜(oblique)字体显示。如果这种斜体字对该浏览器不可用的话,可以使用高亮、反白或加下划线等样式。
定位(position)
static
static 默认值,无定位,不能当作绝对定位的参照物,并且设置标签对象的left、top等值是不起作用的的。
relative(相对定位)
相对定位是相对于该元素在文档流中的原始位置,即以自己原始位置为参照物。有趣的是,即使设定了元素的相对定位以及偏移值,元素还占有着原来的位置,即占据文档流空间。对象遵循正常文档流,但将依据top,right,bottom,left等属性在正常文档流中偏移位置。而其层叠通过z-index属性定义。
注意:position:relative的一个主要用法:方便绝对定位元素找到参照物。
absolute(绝对定位)
定义:设置为绝对定位的元素框从文档流完全删除,并相对于最近的已定位祖先元素定位,如果元素没有已定位的祖先元素,那么它的位置相对于最初的包含块(即body元素)。元素原先在正常文档流中所占的空间会关闭,就好像该元素原来不存在一样。元素定位后生成一个块级框,而不论原来它在正常流中生成何种类型的框。
重点:如果父级设置了position属性,例如position:relative;,那么子元素就会以父级的左上角为原始点进行定位。这样能很好的解决自适应网站的标签偏离问题,即父级为自适应的,那我子元素就设置position:absolute;父元素设置position:relative;,然后Top、Right、Bottom、Left用百分比宽度表示。
另外,对象脱离正常文档流,使用top,right,bottom,left等属性进行绝对定位。而其层叠通过z-index属性定义。
反射的四个函数
又因为在Python中一切皆为对象,所以把反射理解为从对象中动态的获取成员。
关于反射, 其实一共有4个函数:
- hasattr(obj, str) 判断obj中是否包含str成员
- getattr(obj,str) 从obj中获取str成员。
- setattr(obj, str, value) 把obj中的str成员设置成value。这⾥的value可以是值,也可以是函数或者⽅法。
- delattr(obj, str) 把obj中的str成员删除掉。
CSRF设置的装饰器
from django.views.decorators.csrf import csrf_protect,csrf_exempt
某个函数设置CSRF 免除CSRF
from django.utils.decorators import method_decorator
CBV设置前两个装饰器,不能直接在方法加装饰器,需要在类前面加
@method_decorator(装饰器名,name=dispatch)
WSGI是Web服务器网关接口。它是一个规范,描述了Web服务器如何与Web应用程序通信,以及如何将Web应用程序链接在一起以处理一个请求。
django中设置缓存
from django.core.cache import cache
cache.set(k,v,30) 设置键值对,和存储在缓存中的秒数。
cache.get(k) 取到对应的值
string.ascii_letters是'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
string.digits是'0123456789'
在编写form的钩子函数时,self就是form生成的HTML代码,可以self.instance得到传进去的实例化对象。
<pre></pre>数据库中存储的什么样,前端显示就是什么样。
判断路径是否存在用os.path.exist()
os.makedirs(path,exist_ok=True) exist_ok=True 不存在就创建,存在不会抛异常。
从request.FILES中获得的真实的文件。这个字典的每个输入都是一个UploadedFile对象——一个上传之后的文件的简单的包装。
你通常会使用下面的几个方法来访问被上传的内容:
UploadedFile.read():从文件中读取整个上传的数据。小心整个方法:如果这个文件很大,你把它读到内存中会弄慢你的系统。你可以想要使用chunks()来代替,看下面;
UploadedFile.chunks():如果上传的文件足够大需要分块就返回真。默认的这个值是2.5M,当然这个值是可以调节的。
例如:
with open('{}{}'.format(enroll_data_dir,file_obj.name),'wb') as f: for chunk in file_obj.chunks(): f.write(chunk)
django中批量创建,不用每次创建一个都想数据库提交。
objs_list=[xxx(**kwargs),xxx(**kwargs),xxx(**kwargs),] 表名(所需参数)
models.xxx.objects.bulk_create(objs_list)
获得GET提交的字典形式request.GET.dict()
django ORM中对日期字段过滤需要传入的是datetime.datetime类型
import datetime
对时间的加减操作
datetime.datetime.now() + datetime.timedelta(hours=-hour,minutes=-minute,seconds=-second,days=-7)
pycharm 修改端口
run->edit configurations ->有端口配置
.values() 以字典形式返回queryset,括号里还可以通过字段跨表查询,括号里用__是跨表查询,用_是字段查询。(对多个queryset对象进行的。)
.distinct()对queryset进行去重。
css去除边上的空白
body{ margin: 0; }
django ORM中的DateField和DateTimeField的区别:
DateField 日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。
DateTimeField 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中datetime.datetime()实例。
django路由中的分组命名匹配
分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图。 在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。
例子:
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
捕获的值作为关键字参数而不是位置参数传递给视图函数。 例如,针对url /articles/2017/12/相当于按以下方式调用视图函数:
views.month_archive(request, year="2017", month="12")
ORM中关于文件的字段:
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.FileSystemStorage ImageField(FileField) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串)
请求方法
常用的请求方法有get(查询),post(添加),put(更新),delete(删除),patch(局部更新)
对于put和patch的一点区别:
patch
方法用来更新局部资源,这句话我们该如何理解?
假设我们有一个UserInfo
,里面有userId
, userName
, userGender
等10个字段。可你的编辑功能因为需求,在某个特别的页面里只能修改userName
,这时候的更新怎么做?
人们通常(为徒省事)把一个包含了修改后userName
的完整userInfo
对象传给后端,做完整更新。但仔细想想,这种做法感觉有点二,而且真心浪费带宽(纯技术上讲,你不关心带宽那是你土豪)。
于是patch
诞生,只传一个userName
到指定资源去,表示该请求是一个局部更新,后端仅更新接收到的字段。
而put
虽然也是更新资源,但要求前端提供的一定是一个完整的资源对象,理论上说,如果你用了put
,但却没有提供完整的UserInfo
,那么缺了的那些字段应该被清空
补充:
最后再补充一句,restful
只是标准,标准的意思是如果在大家都依此行事的话,沟通成本会很低,开发效率就高。但并非强制(也没人强制得了),所以你说在你的程序里把方法名从put
改成patch
没有任何影响,那是自然,因为你的后端程序并没有按照标准对两个方法做不同处理,她的表现自然是一样的。
补充一下,PATCH 与 PUT 属性上的一个重要区别还在于:PUT 是幂等的,而 PATCH 不是幂等的。
幂等是一个数学和计算机学概念,在计算机范畴内表示一个操作执行任意次对系统的影响跟一次是相同。
在HTTP/1.1规范中幂等性的定义是:
Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.
如:POST 方法不是幂等的,若反复执行多次对应的每一次都会创建一个新资源。如果请求超时,则需要回答这一问题:资源是否已经在服务端创建了?能否再重试一次或检查资源列表?而对于幂等方法不存在这一个问题,我们可以放心地多次请求。 来自https://segmentfault.com/q/1010000005685904
OrderedDict
使用dict
时,Key是无序的。在对dict
做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict
:
>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注意,OrderedDict
的Key会按照插入的顺序排列,不是Key本身排序。
templates顺序问题
django中render渲染页面,先去项目templates下面匹配,然后按照app注册顺序去其下的templates下匹配,匹配不到就报错。同理templatetags也是如此,所以要避免视图名称重复这个问题,一般在template下在创建一个python package,起名是app名(不会重复),视图就不会找错了。
django的settings.py中
APPEND_SLASH 默认为True,会在url后面自动加/。
当客户端发送请求,当url前面都可以匹配到,后面缺少一个/,服务端会给客户端发一个重定向,重定向的地址后面自动加/,客户端自动访问加/的地址。
静态文件路径设置
#静态文件的前缀 STATIC_URL = '/static/'
#静态文件的路径 STATICFILES_DIRS = ( os.path.join(BASE_DIR, "common_static"),
#app共有的静态文件,比如:jqurey.js '/path/to/others/static/',#其他静态文件的路径 )
CRM
js中对象[]的添加元素 对象.push()
.val()不仅可以获取值还可以设置值。
外部访问 django
开开启django时,使用0.0.0.0:xxxx,作为ip和端口例如: python manage.py runserver 0.0.0.0:9000 然后在settings里修改ALLOWED_HOSTS = [], 改为ALLOWED_HOSTS = ['*',],注意不要漏掉“,”。
Auth模块
from django.contrib import auth
auth.authenticate(username=,password=)
利用sshpass进行主机连接(输入明文密码,不用进行交互)
'sshpass -p {password} ssh {username}@{ip} -o StrictHostKeyChecking=no'
chmod变更文件或目录的权限
chown变更文件或目录的拥有者或所属群组
强制调用类的私有字段:_类名__字段名
Q查询的其他方式:

con=Q() con.connector='OR' for i in search_fields: con.children.append(('%s__contains'%i,search_content)) #可以把con当作查询条件直接加进filter()中
ORM中对时间字段进行过滤
filter({'date__gt': datetime.datetime(2019, 4, 28, 0, 0, 0, 636064)}) 注意时间类型
对该类型时间的增减 下面是今天零点的时间
time_difference=datetime.datetime.now()+datetime.timedelta(hours=-hour,minutes=-minute,seconds=-second)
prop()获取checkbox的属性,也可以设置属性