需要哪些表
UserInfo
class UserInfo(AbstractUser): """ 用户信息表 """ nid = models.AutoField(primary_key=True) phone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to="avatars/", default="avatars/default.png", verbose_name="头像") create_time = models.DateTimeField(auto_now_add=True) blog = models.OneToOneField(to="Blog", to_field="nid", null=True) def __str__(self): return self.username
Blog
class Blog(models.Model): """ 博客信息 """ nid = models.AutoField(primary_key=True) title = models.CharField(max_length=64) # 个人博客标题 site = models.CharField(max_length=32, unique=True) # 个人博客后缀 theme = models.CharField(max_length=32) # 博客主题 def __str__(self): return self.title
Category
class Category(models.Model): """ 个人博客文章分类 """ nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) # 分类标题 blog = models.ForeignKey(to="Blog", to_field="nid") # 外键关联博客,一个博客站点可以有多个分类 def __str__(self): return self.title
Tag
class Tag(models.Model): """ 标签 """ nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) # 标签名 blog = models.ForeignKey(to="Blog", to_field="nid") # 所属博客 def __str__(self): return self.title
Article
class Article(models.Model): """ 文章 """ nid = models.AutoField(primary_key=True) title = models.CharField(max_length=50) # 文章标题 desc = models.CharField(max_length=255) # 文章描述 create_time = models.DateTimeField(auto_now_add=True) # 创建时间 category = models.ForeignKey(to="Category", to_field="nid", null=True) user = models.ForeignKey(to="UserInfo", to_field="nid") tags = models.ManyToManyField( # 中介模型 to="Tag", through="Article2Tag", through_fields=("article", "tag"), # 注意顺序!!! ) # 评论数 comment_count = models.IntegerField(default=0) # 点赞数 top_count = models.IntegerField(default=0) # 点踩数 bottom_count = models.IntegerField(default=0) def __str__(self): return self.title
ArticleDetail
class ArticleDetail(models.Model): """ 文章详情表 """ nid = models.AutoField(primary_key=True) content = models.TextField() article = models.OneToOneField(to="Article", to_field="nid")
Article2Tag
class Article2Tag(models.Model): """ 文章和标签的多对多关系表 """ nid = models.AutoField(primary_key=True) article = models.ForeignKey(to="Article", to_field="nid") tag = models.ForeignKey(to="Tag", to_field="nid") def __str__(self): return "{}-{}".format(self.article.title,self.tag.title) class Meta: unique_together = (("article", "tag"),)
ArticleUpDown
class ArticleUpDown(models.Model): """ 点赞表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey(to="UserInfo", null=True) article = models.ForeignKey(to="Article", null=True) is_up = models.BooleanField(default=True) class Meta: unique_together = (("article", "user"),) verbose_name_plural = "文章点赞"
Comment
class Comment(models.Model): """ 评论表 """ nid = models.AutoField(primary_key=True) article = models.ForeignKey(to="Article", to_field="nid") user = models.ForeignKey(to="UserInfo", to_field="nid") content = models.CharField(max_length=255) # 评论内容 create_time = models.DateTimeField(auto_now_add=True) parent_comment = models.ForeignKey("self", null=True,blank=True) def __str__(self): return self.content
注册
基于forms和ajax注册
url(r\'^register/\', views.register),
现在APP下面创建forms.py文件,存放form类,方便日后导入调用
from django import forms from django.core.exceptions import ValidationError from blog import models # 注册form类 class RegForm(forms.Form): username = forms.CharField( max_length=16, label="用户名", error_messages={ "max_length": "最长16位", "required": "不能为空" }, widget=forms.widgets.TextInput(attrs={"class": "form-control"}) ) password = forms.CharField( max_length=16, label="密码", error_messages={ "max_length": "最长16位", "required": "不能为空" }, widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}) ) re_password = forms.CharField( max_length=16, label="确认密码", error_messages={ "max_length": "最长16位", "required": "不能为空" }, widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}) ) email = forms.EmailField( label="邮箱", widget=forms.widgets.EmailInput(attrs={"class": "form-control"}), error_messages={ "required": "不能为空", "invalid": "邮箱格式不正确!" } ) # 局部钩子 校验用户名 def clean_username(self): username = self.cleaned_data.get("username") obj = models.UserInfo.objects.filter(username=username) if obj: raise ValidationError("用户已经注册!") else: return username # 局部钩子 校验邮箱 def clean_email(self): email = self.cleaned_data.get("email") obj = models.UserInfo.objects.filter(email=email) if obj: raise ValidationError("邮箱已经存在!") else: return email # 全局钩子 def clean(self): password = self.cleaned_data.get("password") re_password = self.cleaned_data.get("re_password") if password == re_password: return self.cleaned_data else: self.add_error("re_password", ValidationError("两次密码不一致"))
局部钩子校验某个字段是否满足要求,全局钩子可以拿到所有字段的值,经常用于判断两次密码是否一致
def register(request): res = {"status":None,"msg":None} if request.method == "POST": form_obj = forms.RegForm(request.POST) if form_obj.is_valid(): form_obj.cleaned_data.pop("re_password") avatar_obj = request.FILES.get("avatar") if avatar_obj: models.UserInfo.objects.create_user(**form_obj.cleaned_data,avatar=avatar_obj) else: models.UserInfo.objects.create_user(**form_obj.cleaned_data) res["status"] = True res["msg"] = "/index/" return JsonResponse(res) else: res["status"] = False res["msg"] = form_obj.errors return JsonResponse(res) form_obj = forms.RegForm() return render(request,\'register.html\',{"form_obj":form_obj})
登录
登录基于极验滑动验证码
注册头像预览
<!--头像--> <div class="form-group"> <label for="" class="col-sm-2 control-label">头像</label> <div class="col-sm-10"> <label for="avatar"> <img src="/static/images/default.jpeg" alt="" width="40"> </label> <input type="file" id="avatar" style="display: none"> </div> </div>
$("#avatar").on("change",function () {
var fileRead = new FileReader() //创建读文件对象
fileRead.readAsDataURL(this.files[0]) //读取文件
fileRead.onload = function(){
$("img").attr("src",fileRead.result) // src可以为路径,也可以为编码的数字
}
})
media的配置
一般前端传的文件都放在media目录下,服务器的静态文件放在static
先在settings.py配置
# django 用户上传的文件都在media中 MEDIA_URL = "/media/" # media配置,用户上传的文件都默认在这个文件下 MEDIA_ROOT = os.path.join(BASE_DIR,"media")
在urls.py配置
# media url(r"^media/(?P<path>.*)$",serve,{"document_root":settings.MEDIA_ROOT}),
一旦配置media,ORM文件对象指定路径的时候,无需从media开始,直接指定目录即可,因为上传的文件会默认存在media下
扩展自带的auth_user表
新建一个表,一对一关联上面的auth_user表
继承的方式
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
phone = models.CharField(max_length=11)
addr = models.CharField(max_length=128)
相当于对默认的auth_user表做了扩展, 并且代替auth_user 在settings.py中一定要加 AUTH_USER_MODEL = \'app名.类名\'
render()到底渲染的什么?
1.
渲染的时候注意在脚本里面,比如script标签里面,要加引号
var test = "{{obj.title}}"
渲染出来可能是数字,没关系,如果是字符,那就是变量了
2.
JS文件是引入的话,就不能用括号了,可以在页面创建个隐藏的div,讲要传的值赋值给div的任意属性,然后用JS动态找到生成
防止XSS攻击
html_doc = BeautifulSoup(article_content,"html.parser") for tag in html_doc: if tag.name in ["script","link"]: tag.decompose() # 删除满足条件的标签
评论楼
1楼 111
2楼 222
3楼 333
4楼 @111 444
5楼 555
6楼 @555 666
评论树
111
444
555
666
222
777
333
888
# 评论树 def comment_tree(request,article_id): comment_list = models.Comment.objects.filter(article_id=article_id).values("nid","content","parent_comment_id") comment_list = list(comment_list) return JsonResponse(comment_list,safe=False) # 当序列化非字典时使用safe
<!--评论树-->
<p>评论列表</p>
<div class="comment_tree"></div>
<!--end 评论树-->
//评论树ajax
$.ajax({
url: "/blog/comment_tree/{{ article_obj.pk }}/",
type: "GET",
success: function (comment_list) {
$.each(comment_list, function (index, dict) {
console.log(index, dict)
var s = "<div class=\'comment-item\' comment_id=" + dict.nid + "><span>" + dict.content + "</span> </div>"
if (dict.parent_comment_id) {
//存在子评论
$("[comment_id=" + dict.parent_comment_id + "]").append(s)
} else {
//只有根评论
$(".comment_tree").append(s)
}
})
}
})