Django博客园知识点整理
Django博客园知识点整理
时区的问题
- 在Django的配置文件
settings.py中,有两个配置参数是跟时间与时区有关的,分别是TIME_ZONE和USE_TZ - 如果
USE_TZ设置为True时,Django会使用系统默认设置的时区,即America/Chicago,此时的TIME_ZONE不管有没有设置都不起作用。 - 如果
USE_TZ设置为False,而TIME_ZONE设置为None,则Django还是会使用默认的America/Chicago时间。若TIME_ZONE设置为其它时区的话,则还要分情况,如果是Windows系统,则TIME_ZONE设置是没用的,Django会使用本机的时间。如果为其他系统,则使用该时区的时间,入设置USE_TZ = False,TIME_ZONE = 'Asia/Shanghai', 则使用上海的UTC时间。
数据库变更 -- 转为 MySql
配置文件 settings 设置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'alwaysblog', #你的数据库名称
'USER': 'root', #你的数据库用户名
'PASSWORD': 'root', #你的数据库密码
'HOST': '', #你的数据库主机,留空默认为localhost
'PORT': '3306', #你的数据库端口
}
}
项目目录中 __init__ 配置
Django中默认使用mysqldb模块进行python与mysql的交互, python3 中,模块 mysqldb 被 pymysql 替代,所以要在项目所在的 __init__ 文件中加入如下代码,达到替换效果
import pymysql pymysql.install_as_MySQLdb()
models
表的创建
由于在 Django 中,进行表格数据的查询,往往需要各种使用各种方法配合(尤其在进行第三张表查询的时候),有的时候需要使用 类 的属性,以及 字段 的属性。
所以,这个时候,我们可以在定义的 类 中使用 models.ManyToManyField() 让系统自己创建表的时候,自己再使用 类 来创建第三张表。
注: 在 Django 中, 创建第三章多对多关系的表的时候,可以通过关键字 ManyToManyField 来进行,也可以通过自定义类通过 models.ForeignKey 来创建关联关系
1 class Article(models.Model): 2 3 nid = models.BigAutoField(primary_key=True) 4 title = models.CharField(max_length=50, verbose_name='文章标题') 5 desc = models.CharField(max_length=255, verbose_name='文章描述', default="") 6 read_count = models.IntegerField(default=0) 7 comment_count= models.IntegerField(default=0) 8 up_count = models.IntegerField(default=0) 9 down_count = models.IntegerField(default=0) 10 category = models.ForeignKey(verbose_name='文章类型', to='Category', to_field='nid', null=True) 11 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 12 blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') 13 tags = models.ManyToManyField( 14 to="Tag", 15 through='Article2Tag', 16 through_fields=('article', 'tag'), 17 ) 18 19 type_choices = [ 20 (1, "Python"), 21 (2, "Linux"), 22 (3, "OpenStack"), 23 (4, "GoLang"), 24 ] 25 26 article_type_id = models.IntegerField(choices=type_choices, default=None) 27 def __str__(self): 28 return self.title 29 30 # ====================================================== 31 32 class Tag(models.Model): 33 nid = models.AutoField(primary_key=True) 34 title = models.CharField(verbose_name='标签名称', max_length=32) 35 blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') 36 37 def __str__(self): 38 return self.title 39 40 # ====================================================== 41 42 class Article2Tag(models.Model): 43 nid = models.AutoField(primary_key=True) 44 article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid') 45 tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid') 46 47 class Meta: 48 unique_together = [ 49 ('article', 'tag'), 50 ]
字段参数说明 -- 部分
primary_key=True : 主键 不设置时,默认给一个 id
verbose_name='昵称': admin 中显示的名称
blank=True: admin 中可以为空
null=True : 数据库中可以为null
unique=True : 唯一
upload_to = './upload/headPic/' : 文件存放位置
auto_now_add=True : 自增
--多对多--
to='UserInfo' : 与 UserInfo 表 进行关联
through='UserFans': 与 UserFans 表 进行关联
through_fields=('user', 'follower') : 通过哪个关键字
--一对多--
to='UserInfo'
to_field='nid'
related_name='followers' : 反调名称,可以如果没有,通过 小写类名 + _set
继承系统自带的表 -- 以User 表为例
在使用 Django 的时候,Django 会默认创建一张用户表,在 Blog 这个项目中,我们也需要一个 UserInfo 的表,如果使用 Django 创建的,name可以节省我们代码的同时,还可以增强我们 验证 的功能
models 处理
引用:from django.contrib.auth.models import AbstractUser
from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
"""
用户信息
"""
nid = models.BigAutoField(primary_key=True)
nickname = models.CharField(verbose_name='昵称', max_length=32)
telephone = models.CharField(max_length=11, blank=True, null=True, unique=True, verbose_name='手机号码')
headPic = models.FileField(verbose_name='头像',upload_to = './upload/headPic/')
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
fans = models.ManyToManyField(verbose_name='粉丝们',
to='UserInfo',
through='UserFans',
through_fields=('user', 'follower'))
def __str__(self):
return self.username
settings配置
在继承 user 这个原来的类的时候,需要在配置 settings 中加入一条 AUTH_USER_MODEL 的配置
AUTH_USER_MODEL = 'myBlog.UserInfo'
验证码
使用一个 img 标签, 用 src 属性访问服务端下面的函数,获取数据
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import random
def varCode(request):
f = BytesIO()
img = Image.new(mode='RGB', size=(120, 30),
color=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
draw = ImageDraw.Draw(img, mode='RGB')
font = ImageFont.truetype("static/dist/fonts/kumo.ttf", 28)
code_list = []
for i in range(5):
char = random.choice([chr(random.randint(65, 90)), str(random.randint(1, 9))])
code_list.append(char)
draw.text([i * 24, 0], char, (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
font=font)
img.save(f, "png")
valid_code = ''.join(code_list)
request.session["valid_code"] = valid_code
# return HttpResponse("OK...")
return HttpResponse(f.getvalue())
验证码刷新
原理: 在 img 标签中 src 属性后面加一个问号
Ajax实现前端页面跳转
window.location.href="" 全局
location.href=""
解决Django 第一次post请求失败问题
Django 有个安全机制是禁止用户在第一次访问的时候就使用 post 请求,可以通过一下方法实现请求
方式 1
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
方式2
<form>
{% csrf_token %} # 在form中定义一个标签
</form>
$.ajax({
data:{
"csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val(); # 用 name 属性取值
}
})
方式3
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$.ajax({
headers:{"X-CSRFToken":$.cookie('csrftoken')},
})
Django Form
创建需要使用的元素的类(username、password、email等)
1 from django import forms # 创建 Django 表单,并写 自定义验证规则 2 from django.core.exceptions import ValidationError # Django 在验证表单时会捕捉的错误类型 3 import re 4 5 class UserForm(forms.Form): 6 7 def __init__(self, *args, request=None, **kwargs): 8 super().__init__(*args, **kwargs) 9 self.request = request 10 11 username = forms.CharField(widget=forms.TextInput(attrs={"class":"form-control username", "placeholder":"UserName"}), 12 min_length=5, 13 max_length=20, 14 error_messages={"required": "请输入用户名"}) 15 password = forms.CharField(widget=forms.PasswordInput(attrs={"class":"form-control password", "placeholder":"PassWord"}), 16 min_length=5, 17 max_length=20, 18 error_messages={"required": "请输入密码"}) 19 rePassword = forms.CharField(widget=forms.PasswordInput(attrs={"class":"form-control rePassword", "placeholder":"rePassword"}), 20 required=False) 21 email = forms.EmailField(widget=forms.TextInput(attrs={"class":"form-control email", "placeholder":"E-mail"}), 22 error_messages={"required": "请输入邮箱", 23 "invalid": "邮箱格式错误"}) 24 verCode = forms.CharField(widget=forms.TextInput(attrs={"class":"form-control verCode","placeholder":"verCode"}), 25 required=False) 26 27 def clean_username(self): 28 username = self.data["username"] 29 if re.search("[^a-zA-Z0-9]", username) or re.search("^[^a-zA-Z]", username): 30 raise ValidationError("用户名只能由字母数字组成,并以字母开头") 31 return self.data["username"] 32 33 34 def clean_password(self): 35 password = self.data["password"] 36 if re.search("^[a-zA-Z0-9]+$", password): 37 raise ValidationError("密码应该由数字、字母、特殊字符组成") 38 return self.data["password"] 39 40 41 def clean_verCode(self): 42 if self.request.session["verCode"].upper() != self.data["verCode"].upper(): 43 raise ValidationError("验证码错误") 44 45 return self.data["verCode"] 46 47 48 def clean(self): 49 if self.data["password"] != self.data["rePassword"]: 50 raise ValidationError("两次密码输入不一致") 51 return self.data
属性说明
widget=forms.TextInput() 输入框的类型
attrs={} 输入框的属性,键值对
error_messages={} 自定义错误信息,键值对,也可以将默认的英文设置为中文
required 必填
invalid 格式错误
有些时候,系统自带的检测并不能够完全满足我们生产或功能实现时候的需要,这个时候我们需要自定义一些功能函数来判断用户输入的信息规则是否符合我们预期的要求,这个时候我们就用到 钩子 这个概念,
钩子 分为全局钩子和局部钩子,定义的局部的函数只能进行本条信息的判断与处理,错误的时候,我们需要通过 raise 出
ValidationError 错误。

如果需要进行多天数据的时候(判断两次输入的密码是否正确),这个时候我们需要用到全局钩子,

如果我们需要通过其他数据对本次的验证进行判断的话(验证码的判断),那么我们还需要传入参数来解决,这种情况下,我们可以把参数传入到 __init__ 这个函数里面,这种情况下我们需要注意继承父类的 __init__ 的方法

局部钩子函数的命名规则为: clean_ + 字段名(如username)

全局钩子函数的命名规则为: clean

注意,验证正确情况下的返回值
Django 表单信息验证
在验证的时候,我们实例化一个 UserForm 的一个实例,并把我们收到的参数传进去,这样的话,Django 就会根据自己的以及我们定义的规则进行判断处理

Django 表单创建用户名
Django 的表单自带的 user 这张表,自带了密码加密等一些功能,因为我们继承了这张表,所以我们在使用我们创建的 UserInfo 的时候,可以使用以下命令,这样的话 Django 会把密码加密后存到数据库中,这就意味着,你在进行用户名密码校验的时候需要用到 Django 自带的认证的方法,这样的话才能拿到加密的密码,进行正确的密码对比。
以下是创建用户信息的时候使用的方法: create_user

Django 表单登录
用户登录的时候应该使用 Django 带的方法来进行验证,它会将你的密码进行加密后和数据库的密码进行对比,成功后拿到这个用户的对象

Django 表单保存登录状态
登录状态的保持原理是通过 cookie 以及 session 来实现的,原理如下
# 生成随机字符串
# 写浏览器cookie: session_id: 随机字符串
# 写到服务端session:
# {
# "随机字符串": {'user_info':'alex}
# }
进行上面操作,可以使用 Django 自带的一个方法

Django 表单用户退出登录

Django 表单登录状态验证
可以通过一个装饰器 @login_required 来实现,这个装饰器会自动判断用户登录状态,如果为未登录状态的话,则跳转到 “指定位置”,如果不设置,会跳转到默认地址,并在地址后面用get请求记录跳转之前的地址

这个指定位置可以在 settings 里面设置
LOGIN_URL = "/login/"
登录页面实现跳转到之前地址
方案 1
request.path 获取访问路径 重定向到 login/后面拼接这个路径
由于在请求页面的时候通常使用的是 get 请求,所以我们可以在判断用户未登陆的情况,可以给它一个 return redirect('路径'),路径的内容为login?自定义变量名=之前访问的路径;,这样的话,我么在每次登陆成功后就判断代码后面是否有这个变量,如果有的话就跳转到这个页面,如果没有的话,那么我们就跳转到 index,之类的页面
方案 2
放在 cookie 中
原理和上面大同小异,只不过是把信息存在了cookie中而已,这里需要注意的是,每次用完应该清空cookie中的值,这样的话,如果是正常登陆,就不会重定向到莫名其妙的位置了,这只cookie的值,要在返回的对象中设置,如
json_response_obj = JsonResponse(errorDict)
json_response_obj.set_cookie("next", False)
return json_response_obj
取值的时候,就可以直接通过request进行了
request.COOKIES.get("next")
注册时的头像设置
首先是前端,在前端的处理上,用户在请求注册页面的时候,我顺便发送了一张服务器中的一个默认的头像过去,这样的话即使你懒得设置头像也可我,我就会使用这张默认的图片作为用户的头像;
在前端展示头像我用的是<img>标签,用户本地选取我的是<input>标签,为了做到点击图片就实现选择头像的效果,我为他们设置了一个父级标签,并做了相对定位,这两个标签是用绝对定位(他们的尺寸我设置成了一样的),然后讲<input>标签的透明度设置为0,这样就可以实现看到的是图片,点击触发的是input事件,如果这个<input>发生了 change 事件,那么我就修改<img>标签的src属性,具体看代码
数据库中存放的字段
headPic = models.FileField(verbose_name='头像',upload_to = './upload/headPic/')
settings中的配置
MEDIA_ROOT=os.path.join(BASE_DIR, "static/media")
标签
<div class="headPic">
<label>头像:</label>
<img src="/static/media/downLoad/default/defaultHeadPic.jpg" alt="头像" id="headPic_img">
<input type="file" id="headPic_file">
</div>
css
.headPic{
width: 200px;
height: 230px;
position: relative;
}
#headPic_img,#headPic_file{
border:1px solid grey;
padding: 1px;
position: absolute;
width: 197px;
height: 197px;
top: 20px;
left: 0;
}
#headPic_file{
opacity: 0;
}
js
存放头像的<input>修改的时候执行的代码
$("#headPic_file").change(function(){
var reader = new FileReader();
var fileObj = $(this)[0].files[0];
reader.readAsDataURL(fileObj);
reader.onload = function(){
$("#headPic_img")[0].src = this.result
}
});
在发送注册信息的时候,这里采用的ajax的方式,由于发送的时候携带了头像,这里我们采用了FormData的方式进行发送
$("#registButton").click(function(){
$(".error_tip").hide(); // 隐藏所有错误标签
var formData = checkValues(); // 检查用户名的参数是否合法, 并返回结果
if(!formData){
return false;
}
$.ajax({
url: "",
data: formData,
processData: false, // 告诉jQuery不要去处理发送的数据
contentType: false, // 告诉jQuery不要去设置Content-Type请求头
type: "POST",
success: function(data){
if(data["flag"]){
console.log("ajax successful!!!");
window.location.href = "/login/"
}
else{
$.each(data["data"], function(k, v){
// 错误字典形式 {"username": "This field is required."}
$("."+k).first().siblings().last().html(v).fadeIn();
})
}
}
})
});
function checkValues(){
// 创建FormData及向里面添加数据
var formData = new FormData();
var username = $("#id_username").val();
var password = $("#id_password").val();
var rePassword = $("#id_rePassword").val();
var email = $("#id_email").val();
var verCode = $("#id_verCode").val();
var headPicObj = $("#headPic_file")[0].files[0];
formData.append("username", username);
formData.append("password", password);
formData.append("rePassword", rePassword);
formData.append("email", email);
formData.append("verCode", verCode);
formData.append("headPicObj", headPicObj);
formData.append("csrfmiddlewaretoken", csrf);
return formData
}
通过这种方式发送数据的时候,需要注意应该设置下面两个选项,另外POST请求还应该注意CSRF_TOKEN的问题
processData: false, // 告诉jQuery不要去处理发送的数据 contentType: false, // 告诉jQuery不要去设置Content-Type请求头
后端Python接收文件数据
def setHeadPic(request):
# 下面的那个路径是我头像希望存储的位置
basePath = os.path.join("static/media/upLoad", request.user.username)
if not os.path.exists(basePath):
os.mkdir(basePath)
# 上传的头像(图片)文件并不在request的POST中,我们需要通过request.FILES来获取文件,里面的名字是前端传过来的时候定义的
fileObj = request.FILES.get("headPicObj")
# 如果顺利的取到这个对象,说明用户上传的头像文件,如果取不到,那么久给他们设置一个默认的头像
if fileObj:
fileName = os.path.join(basePath, ("headPic." + fileObj.name.split(".")[-1]))
else:
fileObj = open("static/media/downLoad/default/defaultHeadPic.jpg", "rb")
fileName = os.path.join(basePath, "headPic.jpg")
with open(fileName, "wb") as f:
for line in fileObj:
f.write(line)
fileObj.close()
models.UserInfo.objects.filter(nid=request.user.nid).update(headPic=os.path.join("/", fileName))
return HttpResponse("OK....")
使用 FormData
上面我们提到了如何使用FormData将文件发送给后端,更多的使用方法可以点击下面链接;
模板处理器
富文本编辑框
注:
jq对象转成 jdoucment 对象
数据库中自加
查看访问的地址
format
有序字典
锚点
浙公网安备 33010602011771号