需求分析
- 登陆功能 (基于Ajax,图片验证码)
- 注册功能 (基于Ajax,基于forms验证)
- 博客首页
- 个人站点
- 文章详情
- 点赞,点踩
- 评论
——根评论
——子评论 - 后台管理
——文章展示 - 发布文章
——富文本编辑器
——防止xss攻击
settings.py
#指定上传头像的根路径
MEDIA_ROOT=os.path.join(BASE_DIR,'media')
TT=os.path.join(BASE_DIR,'BBS')
modles.py
class Article(models.Model):
# 评论数、点赞数、点踩数字段
comment_num = models.IntegerField(default=0)
up_num = models.IntegerField(default=0)
down_num = models.IntegerField(default=0)
urls.py
urlpatterns = [
url(r"^$", views.index),
url(r'^get_code/$', views.get_code),
url(r'^register/$', views.register),
# media配置,输入media/后任意字符都会响应到settings.MEDIA_ROOT中的,即media文件夹下avatar
文件内的文件内容(这里指图片)
url(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}),
url(r'index/', views.index),
view.py
def register(request):
if request.is_ajax():
back_msg={'user':None,'msg':None}
name=request.POST.get('name')
pwd=request.POST.get('pwd')
re_pwd=request.POST.get('re_pwd')
email=request.POST.get('email')
myfile=request.FILES.get('myfile') #注意:文件获取的方法是FILES
print(request.POST)
form_obj=myforms.RegForms(request.POST)
if form_obj.is_valid():
if myfile:
UserInfo.objects.create_user(username=name,password=pwd,email=email,avatar=myfile)
else:
UserInfo.objects.create_user(username=name,password=pwd,email=email)
back_msg['user']=name
back_msg['msg']='注册成功'
else:
print(form_obj.errors)
back_msg['msg']=form_obj.errors
return JsonResponse(back_msg)
form_obj=myforms.RegForms()
return render(request,'register.html',{'form_obj':form_obj})
def index(request):
article_list=Article.objects.all()
return render(request,'index.html',{'article_list':article_list})
myform.py
# Django中的Form使用时一般有两种功能:
# 1、生成html标签
# 2、验证输入内容
from django import forms
# 不要将Widget与表单的fields字段混淆。表单字段负责验证输入并直接在模板中使用。而Widget负责渲染网页上HTML表单的输入元素和提取提交的原始数据。widget是字段的一个内在属性,用于定义字段在浏览器的页面里以何种HTML元素展现。
from django.forms import widgets
from mybbs import models
from django.core.exceptions import ValidationError
class RegForms(forms.Form):
name = forms.CharField(max_length=9, label='用户名',
widget=widgets.TextInput(attrs={'class': 'form-control'}),
error_messages={'required': '该字段必填', 'max_length': '不能超长'}
)
pwd = forms.CharField(max_length=9, label='密码',
widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
error_messages={'required': '该字段必填', 'max_length': '不能超长'}
)
re_pwd = forms.CharField(max_length=9, label='再次输入密码',
widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
error_messages={'required': '该字段必填', 'max_length': '不能超长'}
)
email = forms.EmailField(label='电子邮箱',
widget=widgets.EmailInput(attrs={'class': 'form-control'}),
error_messages={'required': '该字段必填', 'invalid': '不是邮箱格式'}
)
def clean_name(self): #局部钩子
name = self.cleaned_data.get('name') # cleaned_data 就是读取表单返回的值,返回类型为字典dict型
user = models.UserInfo.objects.filter(username=name)
if user:
raise ValidationError('用户名已经存在')
else:
return name
def clean(self): #全局钩子
pwd = self.cleaned_data.get('pwd')
re_pwd = self.cleaned_data.get('re_pwd')
if pwd and re_pwd:
if pwd == re_pwd:
return self.cleaned_data
else:
raise ValidationError('再次密码不一致')
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<script src="/static/jquery-3.3.1.js"></script>
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/bootstrap-3.3.7-dist/js/bootstrap.min.js">
<title>首页</title>
</head>
<body>
{#头部信息#}
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">博客园</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首页 <span class="sr-only">(current)</span></a></li>
<li><a href="#">文章</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li><a href="#"></a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">更多操作 <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">修改密码</a></li>
<li><a href="#">后台管理</a></li>
<li><a href="#">退出登录</a></li>
</ul>
</li>
{% else %}
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{#内容区域#}
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div>
<div class="panel panel-primary">
<div class="panel-heading">左侧广告</div>
<div class="panel-body">
广告区
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">左侧广告</div>
<div class="panel-body">
左侧广告
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">左侧广告</div>
<div class="panel-body">
广告
</div>
</div>
</div>
</div>
<div class="col-md-6">
{% for article in article_list %}
<div class="article_head"><a href=""><h5>{{ article.title }}</h5></a></div>
<div>
<img height="40" width="40" src="/media/{{ article.user.avatar }}" alt=""> {# 拼接图像文件路径 #}
<span >{{ article.desc }}</span>
</div>
<div class="small" style="margin-top: 10px">
<span><a href="">{{ article.user.username }}</a></span>
<span>发布于 {{ article.create_date|date:"Y-m-d" }}</span>
<span class="glyphicon glyphicon-comment">评论({{ article.comment_num }})</span>
<span class="glyphicon glyphicon-thumbs-up">点赞({{ article.up_num }})</span>
</div>
<hr>
{% endfor %}
</div>
<div class="col-md-3">
<div>
<div class="panel panel-default">
<div class="panel-heading">右侧广告</div>
<div class="panel-body">
点赞
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">右侧广告</div>
<div class="panel-body">
广告一
</div>
</div>
<div class="panel panel-success">
<div class="panel-heading">右侧</div>
<div class="panel-body">
评论
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
admin.py
from django.contrib import admin
# Register your models here.
# 将各表信息添加到admin中去,在后台可对各表数据进行操作
from mybbs import models
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Tag)
admin.site.register(models.Category)
admin.site.register(models.Article)
admin.site.register(models.ArticleUpDown)
admin.site.register(models.Article2Tag)
admin.site.register(models.Comment)
register.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<script src="/static/jquery-3.3.1.js"></script>
<title>注册页面</title>
<style>
#myimg {
margin-left: 10px;
}
#myfile {
display: none;
}
.error {
color: red
}
</style>
</head>
<body>
<form action="" id="form">
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1>注册页面</h1>
{% csrf_token %}
{% for foo in form_obj %}
<div class="form-group">
<label for="{{ foo.auto_id }}">{{ foo.label }}</label>
{{ foo }} <span class="error pull-right"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="myfile">头像
<img src="/static/img/default.png" alt="" height="60" width="60" id="myimg">
</label>
<input type="file" id="myfile">
</div>
<div>
<input type="button" id="sub_btn" class="btn btn-danger" value="注册">
</div>
</div>
</div>
</div>
</form>
<script>
$("#myfile").change(function () {
var obj = $(this)[0].files[0]
var read = new FileReader()
read.readAsDataURL(obj)
read.onload = function () {
$("#myimg").attr('src', read.result)
}
})
$("#sub_btn").click(function () {
//alert(11)
var formdata = new FormData()
//传统方式
//formdata.append('name',$("#id_name")
var tt = $("#form").serializeArray()
//console.log(tt)
$.each(tt, function (index, value) {
formdata.append(value.name, value.value)
})
formdata.append('myfile', $("#myfile")[0].files[0])
$.ajax({
url: '',
type: 'post',
processData: false,
contentType: false,
data: formdata,
success: function (data) { {# 获取后台返回的结果 #}
//console.log(data)
if (data.user) {
{# 转跳至指定路径 #}
location.href = '/login/'
} else {
$(".form-group").removeClass('has-error')
$("span.error").text("")
$.each(data.msg, function (index, value) {
console.log(index) {# index为字段名 #}
console.log(value)
console.log("#id_" + index)
if (index == '__all__') {
$("#id_re_pwd").next().text(value[0]).parent().addClass('has-error')
}
$("#id_" + index).next().text(value[0]).parent().addClass('has-error')
})
}
}
})
})
</script>
</body>
</html>