readme:
需求分析
BBS表设计
1.用户表
继承AbstractUser
扩展
phone
avatar
create_time
外键字段:
一对一 个人站点
2.个人站点表
site_name 站点名称
site_title 站点标题
site_theme 站点样式
3.文章标签表
name 标签名
外键字段:
一对多 个人站点
4.文章分类表
name 分类名
外键字段:
一对多 个人站点
5.文章表
title 文章标题
desc 文章简介
content 文章内容
create_time 发布时间
数据库字段设计优化
(虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效率差)
up_num 点赞数
down_num 点踩数
comment_num 评论数
外键字段:
一对多 个人站点
多对多 文章标签
一对多 文章分类
6.点赞点踩表
记录哪个用户给哪篇文章点了赞还是点了踩
user ForeignKey(to=‘User’)
article ForeignKey(to=‘Article’)
is_up BooleanField()
7.文章评论表
记录那个用户给哪篇文章写了哪些评论内容
user ForeignKey(to=‘User’)
article ForeignKey(to=‘Article’)
content CharField()
comment_time DateField()
parent
根评论子评论的概念
根评论就是直接评论当前发布的内容的
子评论是评论别人的评论
1.xxxxxx
1.1dddddd
1.2dddddggg
根评论与子评论是一对多的关系
models:
from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
'''
继承AbstractUser
扩展
phone
avatar
create_time
外键字段:
一对一 个人站点
'''
phone = models.BigIntegerField(verbose_name='手机号',null=True)
avatar = models.FileField(verbose_name='头像',upload_to='avatar/',default='avatar/default.png')
create_time = models.DateField(verbose_name='创建时间',auto_now_add=True)
blog = models.ForeignKey(to='Blog',null=True)
class Blog(models.Model):
'''
site_name 站点名称
site_title 站点标题
site_theme 站点样式
'''
site_name = models.CharField(verbose_name='站点名称',max_length=32)
site_title = models.CharField(verbose_name='站点标题', max_length=32)
site_theme = models.CharField(verbose_name='站点样式', max_length=64) # 存css/js文件路径
class Categroy(models.Model):
'''
name 分类名
外键字段:
一对多 个人站点
'''
name = models.CharField(verbose_name='文章分类',max_length=10)
blog = models.ForeignKey(to='Blog',null=True)
class Tag(models.Model):
'''
name 分类名
外键字段:
一对多 个人站点
'''
name = models.CharField(verbose_name='文章标签', max_length=10)
blog = models.ForeignKey(to='Blog',null=True)
class Article(models.Model):
'''
title 文章标题
desc 文章简介
content 文章内容
create_time 发布时间
数据库字段设计优化
(虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效率差)
up_num 点赞数
down_num 点踩数
comment_num 评论数
外键字段:
一对多 个人站点
一对多 文章分类
多对多 文章标签
'''
title = models.CharField(verbose_name='文章标题',max_length=20)
desc = models.CharField(verbose_name='文章简介', max_length=60)
content = models.TextField(verbose_name='文章正文')
create_time = models.DateTimeField(verbose_name='发表时间',auto_now_add=True)
up_num = models.IntegerField(verbose_name='点赞数',default=0)
down_num = models.IntegerField(verbose_name='点踩数',default=0)
comment_num = models.IntegerField(verbose_name='评论数',default=0)
blog = models.ForeignKey(to='Blog',null=True)
categroy = models.ForeignKey(to='Categroy',null=True)
tags = models.ManyToManyField(to='Tag',null=True,through='Article2Tag',through_fields=('article','tag')) # 这里使用半自动 创建多对多
class Article2Tag(models.Model):
article = models.ForeignKey(to='Article')
tag = models.ForeignKey(to='Tag')
class UpAndDown(models.Model):
'''
记录哪个用户给哪篇文章点了赞还是点了踩
user ForeignKey(to='User')
article ForeignKey(to='Article')
is_up BooleanField()
'''
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
is_up = models.BooleanField(verbose_name='点赞数')
class Comment(models.Model):
'''
记录那个用户给哪篇文章写了哪些评论内容
user ForeignKey(to='User')
article ForeignKey(to='Article')
content CharField()
comment_time DateField()
parent
根评论子评论的概念
根评论就是直接评论当前发布的内容的
子评论是评论别人的评论
1.xxxxxx
1.1dddddd
1.2dddddggg
根评论与子评论是一对多的关系
'''
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
content = models.CharField(verbose_name='评论内容',max_length=255)
comment_time = models.DateField(verbose_name='评论时间',auto_now_add=True)
# 自关联
parent = models.ForeignKey(to='self',null=True) # 写to='Comment'也可以,但是语义不那么明确
forms:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author: Frank time:2023/2/12
from django import forms
from app01 import models
class MyReg(forms.Form):
username = forms.CharField(label='用户名',min_length=3,max_length=8,error_messages={
'required':'用户名必填',
'min_length':'用户名最少三位',
'max_length':'用户名最多八位'
},
widget=forms.widgets.TextInput(attrs={'class':'form-control'}))
password = forms.CharField(label='密码',min_length=3,max_length=8,error_messages={
'required':'密码必填',
'min_length':'密码最少三位',
'max_length':'密码最多八位'
},
widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}))
repassword = forms.CharField(label='确认密码',min_length=3,max_length=8,error_messages={
'required':'确认密码必填',
'min_length':'确认密码最少三位',
'max_length':'确认密码最多八位'
},
widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}))
email = forms.EmailField(label='邮箱',error_messages={
'required':'邮箱必填',
'invalid':'邮箱输入不正确'
},
widget=forms.widgets.EmailInput(attrs={'class':'form-control'}))
def clean_username(self):
username = self.cleaned_data.get('username')
is_exist = models.UserInfo.objects.filter(username=username)
if is_exist:
self.add_error('username','用户名已存在')
return username
def clean(self):
password = self.cleaned_data.get('password')
repassword = self.cleaned_data.get('repassword')
if password != repassword:
self.add_error('repassword','两次输入密码不一致!')
return self.cleaned_data
register.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>register</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">注册页面</h1>
<form id="myform">
{% csrf_token %}
{% for form in form_obj %}
<div>
<label for="">{{ form.label }}</label>
{{ form }}
<span style="{color: red}">{{ form.errors }}</span>
</div>
{% endfor %}
<div class="form-group">
<label for="myfile">头像
{% load static %}
<img src="{% static 'img/default.jpg' %}" alt="头像图片" height="80px" id="myimg">
</label>
<input type="file" id="myfile" style="display: none" name="avatar">
</div>
<input type="button" class="btn btn-primary pull-right" value="提交注册" id="mybutton">
</form>
</div>
</div>
</div>
<script>
$('#myfile').change(function () {
//1.先生成一个文件阅读器
var myFileReaderobj = new FileReader();
//2.获取文件
var imgfile = $(this)[0].files[0];
//3.文件阅读器读取文件
myFileReaderobj.readAsDataURL(imgfile);
//4.修改图片标签属性
myFileReaderobj.onload = function () {
$('#myimg').attr('src',myFileReaderobj.result)
}
});
$('#mybutton').click(function () {
// 我们发送ajax请求 包括键值对和文件
var formDataObj = new FormData();
// 首先获取键值对
$.each($('#myform').serializeArray(),function (index,obj) { //我这里有index的目的不是要索引,只是因为这里加一个参数代表的是拿索引,两个参数才是索引+对象
//console.log(index,obj)
formDataObj.append(obj.name,obj.value)
});
// 然后获取文件
formDataObj.append('avatar',$('#myfile')[0].files[0]);
console.log(formDataObj)
// 发送ajax请求
$.ajax({
url:"",
type:'post',
data:formDataObj,
// 需要指定两个关键性参数
contentType:false,
processData:false,
success:function (args) {
alert(args)
}
})
})
</script>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
{#这里不用forms组件来写 因为需要填的东西比较少#}
<div class="container">
<div class="col-md-8 col-md-offset-2 form-group">
<div class="row"><h1 class="text-center">登录页面</h1>
<label for="">Username:</label>
<input type="text" class="form-control" name="username" id="id_username">
<label for="">Password:</label>
<input type="password" class="form-control" name="password" id="id_password">
<input type="button" id="id_button" class="btn btn-primary pull-right" value="登录" style="margin-top: 20px">
<span style="color: red" class="pull-right"></span>
</div>
</div>
</div>
{#发送ajax请求#}
<script>
$('#id_button').click(function () {
$.ajax({
url:'',
type:'post',
data:{
'username' : $('#id_username').val(),
'password' : $('#id_password').val(),
'csrfmiddlewaretoken': '{{ csrf_token }}' //这里记得要加 '' 没有加'',报错404 (Not Found),如果是404的话,首先先考虑下是不是csrf问题
},
//两个必选
{# processType:false,#}
{# contentType:false,#}
success:function (args) {
if(args.code==1000){
window.location.href = args.url
}else{
$('#id_button').next().text(args.msg)
}
}
})
})
</script>
</body>
</html>
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-inverse"> <!--或者是navbar-inverse换黑色样式-->
<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="#">BBS</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>
<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="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li><a href="#">{{ request.user.username }}</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>
<li role="separator" class="divider"></li>
<li><a href="#">退出登录</a></li>
</ul>
</li>
{% else %}
<li><a href="{% url 'login' %}">登录</a></li> <!--url反向解析-->
<li><a href="{% url 'reg' %}">注册</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
</body>
</html>
views:
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import MyRegForm
from app01 import models
from django.contrib import auth
# Create your views here.
def register(request):
# 想到用forms组件来写比较方便
form_obj = MyRegForm.MyReg()
if request.method == 'POST':
# 校验数据是否合法
form_obj = MyRegForm.MyReg(request.POST)
dict_info = {"code":1000,"msg":''}
if form_obj.is_valid():
# print(form_obj.cleaned_data) {'username': '123', 'password': '', 'repassword': '', 'email': '11@11.com'}
mydata = form_obj.cleaned_data
mydata.pop('repassword')
# print(request.FILES.get('avatar')) # 111.jpg
# mydata['avatar'] = request.FILES.get()
avatar_file = request.FILES.get('avatar')
if avatar_file:
# 这里要记得判断是否为None,如果直接存数据库的话,none就也存进去了
mydata['avatar'] = avatar_file
models.UserInfo.objects.create_user(**mydata)
dict_info['url'] = '/login/'
else:
dict_info['code'] = 2000
dict_info['msg'] = form_obj.errors
return JsonResponse(dict_info) # 第一次写的时候这里出错了,多缩进了一格
return render(request,'register.html',locals())
def login(request):
if request.method == 'POST':
dict_info = {'code':1000,'msg':''}
username = request.POST.get('username')
password = request.POST.get('password')
# 校验用户名和密码是否正确
user_obj = auth.authenticate(request,username=username,password=password)
if user_obj:
# 保存用户状态
auth.login(request,user_obj)
dict_info['url'] = '/home/'
else:
dict_info['code'] = 2000
dict_info['msg'] = '用户名或密码错误!'
return JsonResponse(dict_info)
return render(request,'login.html')
def home(request):
return render(request,'home.html')