0.项目的通用流程

  1. 项目立项
  2. 需求分析
  3. 原型
  4. 前端
    1. 页面设计
    2. UI及交互实现
  5. 后端
    1. 架构设计
    2. 数据库设计
    3. 代码模板实现
    4. 单元测试
  6. 网站整合
  7. 功能及集成测试
  8. 网站发布           

1.BBS项目需求分析

  1. 需要哪些表
    1. UserInfo表
      1. username
      2. password
      3. avatar----头像  
    2. 文章表
      1. title
      2. publish_date
      3. desc----摘要
      4. author
      5. 详细内容 一对一关联,文章详情表
    3. 文章详情表
      1. info  
    4. 评论表
      1. user
      2. 评论时间
      3. 评论内容
      4. 评论和文章的关联关系(1个文章多个评论,1对多,写在多的那方)  
      5. 是谁的子评论,是不是回复别人的评论
    5. 标签
      1. 标签名
      2. 标签名和文章,多对多
    6. 分类
      1. 分类名
      2. 分类和文章的关联关系,多对多或一对多  
    7. 点赞
      1. 是赞还是踩
      2. 时间
      3. 谁点的 关联user
      4. 点的是哪个文章  

2.BBS的注册功能

  1. 基于Form表单和Ajax的注册    

form表单的作用:

  1. 生成HTML代码
  2. 验证
  3. 将验证的错误显示在页面上并保留原始的数据

创建项目和对应APP后

settings.py设置

编码格式

# coding=utf-8

静态文件所在位置

STATIC_URL = '/static/'# 静态文件夹的别名

# 静态文件夹的位置
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]

数据库连接

# 数据库相关的配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 连接的数据库类型
'HOST': '127.0.0.1', # 连接数据库的地址
'PORT': 3306, # 端口
'NAME': "bbs", # 数据库名称
'USER': 'root', # 用户
'PASSWORD': 'root' # 密码
}
} 

APP设置

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig',
]

模板设置,

因为是PyCharm创建的项目,自动设置好了

认证指定表

# 告诉Django项目用哪张表做认证
AUTH_USER_MODEL = 'blog.UserInfo'

static设置

放入必须的文件 https://pan.baidu.com/s/1EgrzvxIRGAJ_J3g77rL4rQ

__init__.py-----与settings.py同级的

# coding=utf-8
import pymysql
# 告诉Django用pymysql来代替默认的MySQLdb
pymysql.install_as_MySQLdb()

  

models.py

表结构,生成相应的迁移文件和执行迁移

# coding=utf-8
from django.db import models # Create your models here. from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser):
""" 用户信息表"""
nid = models.AutoField(primary_key=True)
phone = models.CharField(max_length=11, null=True, unique=True)
# 传入的文件都上传到avatars文件夹下
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 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 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 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 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() # 创建时间 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"), # 注意顺序!!!
) def __str__(self):
return self.title class ArticleDetail(models.Model):
"""
文章详情表
"""
nid = models.AutoField(primary_key=True)
content = models.TextField()
article = models.OneToOneField(to="Article", to_field="nid") 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") class Meta:
unique_together = (("article", "tag"),) 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"),) 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) def __str__(self):
return self.content

  生成迁移文件

python manage.py makemigrations

  执行迁移

python manage.py migrate

  

forms.py

# coding=utf-8
"""bbs用到的form类""" from django import forms
# 从from组件导入widgets属性
from django.forms import widgets
# 导入错误类
from django.core.exceptions import ValidationError # 定义一个注册的from类 class RegForm(forms.Form):
username = forms.CharField(
max_length=16,
label="用户名",
# 错误提示
error_messages={
"max_length": "用户名最长16位",
"required": "用户名不能为空",
},
# 设定input框的样式
widget=forms.widgets.TextInput(
attrs={"class": "form-control"}
)
) password = forms.CharField(
min_length=6,
label="密码",
widget=forms.widgets.PasswordInput(
attrs={"class": "form-control"},
# 输入密码不消失
render_value=True,
),
# 错误提示
error_messages={
"min_length": "密码最少6位",
"required": "密码不能为空",
}
) re_password = forms.CharField(
min_length=6,
label="确认密码",
widget=forms.widgets.PasswordInput(
attrs={"class": "form-control"},
# 输入密码不消失
render_value=True,
),
# 错误提示
error_messages={
"min_length": "密码最少6位",
"required": "密码不能为空",
}
) email = forms.EmailField(
label="邮箱",
widget=forms.widgets.EmailInput(
attrs={"class": "form-control"}
),
error_messages={
'invalid': "邮箱格式不正确",
"required": "邮箱不能为空",
}
) # 重写全局钩子函数,对确认密码做校验
def clean(self):
password = self.cleaned_data.get("password")
re_password = self.cleaned_data.get("re_password")
if re_password and re_password != password:
self.add_error("re_password", ValidationError("两次密码不一致"))
# 没错误直接返回
else:
return self.cleaned_data '''
def _clean_form(self):
try:
cleaned_data = self.clean()
except ValidationError as e:
self.add_error(None, e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
'''

  

urls.py

# coding=utf-8

from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
url(r'^admin/', admin.site.urls), # 注册
url(r'^reg/', views.register),
url(r'^index/', views.index), ]

  

views.py

# coding=utf-8
from django.shortcuts import render, redirect, HttpResponse
# 使用forms内的文件生成HTML
from blog import forms, models from django.http import JsonResponse # Create your views here. # 注册的视图函数
def register(request):
# from表单提交
if request.method == "POST":
# 定义一个字典做Ajax提交
ret = {"status": 0, "msg": "", } # 接收提交过来的数据,只有正常的键值对,没有图像
form_obj = forms.RegForm(request.POST)
print(request.POST)
# 帮我做校验,先校验内置的,在校验clean_开头的规则,整个循环走完,在调用clean()方法
if form_obj.is_valid():
# 校验成功,去数据库创建一个新的用户,models继承AbstractUser,使用creat_user
# 多一个键值对,re_password
form_obj.cleaned_data.pop('re_password')
# 自己取文件、图片的数据
avatar_img = request.FILES.get("avatar")
models.UserInfo.objects.create_user(avatar=avatar_img, **form_obj.cleaned_data)
# 注册成功 跳转页面
ret["msg"] = "/index/"
return JsonResponse(ret)
# return HttpResponse("注册成功") else:
print(form_obj.errors)
# 有错误将status给个值
ret["status"] = 1
# 错误封装到msg里面
ret["msg"] = form_obj.errors
return JsonResponse(ret)
# return render(request, "register.html", {"form_obj": form_obj})
# return HttpResponse("信息有误,注册失败")
# 生成from对象
form_obj = forms.RegForm()
return render(request, "register.html", {"form_obj": form_obj}) def index(request):
return HttpResponse("这里是index页面")

  

register.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>欢迎注册</title>
{# 方法3:使用bootstrap样式 #}
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>
<div class="container reg-form">
<div class="row">
<div class="col-md-6 col-md-offset-3">
{# 有文件类型就需要加enctype="multipart/form-data" novalidate不用H5浏览器帮你验证 #}
<form novalidate action="/reg/" method="post" class="form-horizontal" enctype="multipart/form-data">
{# 防域名伪造 #}
{% csrf_token %}
{# 方法1:标签 + input框 #}
{# {{ from_obj.username.label }}#}
{# {{ from_obj.username }}#} {# 方法2:按照forms写的顺序进行遍历 #}
{# {% for field in from_obj %}#}
{# froms中的每个字段标签+对象#}
{# {{ field.label }}#}
{# {{ field }} <br>#}
{# {% endfor %}#}
{# 方法3:bootstrap #}
<div class="form-group">
{# for属性为了聚焦,关联lable和input框 #}
<label for="{{ form_obj.username.id_for_label }}"
class="col-sm-2 control-label">{{ form_obj.username.label }}</label>
<div class="col-sm-8">
{{ form_obj.username }}
{#<input type="email" class="form-control" id="inputEmail3" placeholder="Email">#}
{# 校验状态 #}
{# 错误提示 #}
<span id="helpBlock2" class="help-block">{{ form_obj.username.errors.0 }}</span>
</div>
</div> <div class="form-group">
{# for属性为了聚焦,关联lable和input框 #}
<label for="{{ form_obj.password.id_for_label }}"
class="col-sm-2 control-label">{{ form_obj.password.label }}</label>
<div class="col-sm-8">
{{ form_obj.password }}
{#<input type="email" class="form-control" id="inputEmail3" placeholder="Email">#}
{# 校验状态 #}
<span id="" class="help-block">{{ form_obj.password.errors.0 }}</span>
</div>
</div> <div class="form-group">
{# for属性为了聚焦,关联lable和input框 #}
<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-8">
{{ form_obj.re_password }}
{#<input type="email" class="form-control" id="inputEmail3" placeholder="Email">#}
{# 校验状态 #}
<span id="helpBlock2" class="help-block">{{ form_obj.re_password.errors.0 }}</span>
</div>
</div> <div class="form-group">
{# for属性为了聚焦,关联lable和input框 #}
<label for="{{ form_obj.email.id_for_label }}"
class="col-sm-2 control-label">{{ form_obj.email.label }}</label>
<div class="col-sm-8">
{{ form_obj.email }}
{#<input type="email" class="form-control" id="inputEmail3" placeholder="Email">#}
{# 校验状态 #}
<span id="helpBlock2" class="help-block">{{ form_obj.email.errors.0 }}</span>
</div>
</div> <div class="form-group">
{# for属性为了聚焦,关联lable和input框 #}
<label for="" class="col-sm-2 control-label">头像</label>
<div class="col-sm-8">
<label for="id_avatar">
<img src="/static/img/default.png" id="avatar-img">
</label>
<input type="file" name="avatar" id="id_avatar" style="display: none"> {# 校验状态 #}
<span id="helpBlock2" class="help-block"></span>
</div>
</div> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" class="btn btn-success" id="reg-submit">注册</button>
</div>
</div>
</form>
</div>
</div>
</div> {#导入JS文件#}
<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script> <script>
// 找到头像的input标签绑定change事件
$("#id_avatar").change(function () {
// 1. 创建一个读取文件的对象
var fileReader = new FileReader();
// 取到当前选中的头像文件
// console.log(this.files[0]);
// 读取你选中的那个文件
fileReader.readAsDataURL(this.files[0]); // 读取文件是需要时间的
fileReader.onload = function () {
// 2. 等上一步读完文件之后才 把图片加载到img标签中
$("#avatar-img").attr("src", fileReader.result);
};
});
// AJAX提交注册数据
$('#reg-submit').click(function () {
//alert(123);
{#// 取得用户注册数据,向后端提交#}
//var username = $("#id_username").val();
//var password = $("#id_password").val();
//var re_password = $("#id_re_password").val();
//var email = $("#id_email").val();
// 文件类型必须使用FormData()对象
var formData = new FormData();
formData.append("username", $("#id_username").val());
formData.append("password", $("#id_password").val());
formData.append("re_password", $("#id_re_password").val());
formData.append("email", $("#id_email").val());
//提交图片数据
formData.append("avatar", $("#id_avatar")[0].files[0]);
formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val()); $.ajax({
url: "/reg/",
type: "post",
// AJAX传带文件必须设置,下面的两个参数
processData: false,
contentType: false,
data: formData,
//data: {
// 提交的数据
// username: username,
// password: password,
// re_password: re_password,
// email: email,
// csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
//},
success: function (data) {
if (data.status) {
//有错误展示错误
//console.log(data.msg);
//将错误信息填写到页面上,用each循环
$.each(data.msg, function (k, v) {
//console.log("id_"+ k,v[0]);
//找到id为id_+k的input标签,下的span标签,设置文本内容为v[0]
$("#id_" + k).next("span").text(v[0]);
//设置文本的样式为has-error
$("#id_" + k).parent().parent().addClass("has-error"); })
} else {
//没有错误,跳转指定页面
location.href = data.msg;
} }
}) }); // 将所有input框,绑定焦点事件,清空所有错误信息
$("form input").focus(function () {
// 清空内容
$(this).next().text("");
$(this).next().text("").parent().parent().removeClass("has-error") })
</script> </body>
</html>

 

....

3.API

# 根据爬虫数据库数据,生成models.py

python manage.py inspectdb

  

安装

pip install djangorestframework

  

 

<Django>博客项目的更多相关文章

  1. Django——博客项目

    博客项目 目前的目标是构建一个基于Django的前后端完整的博客系统,首先对项目流程整理如下: 1. 分析需求 1.1. 基于用户认证组件和Ajax实现登录验证 图形验证码核心代码: 模板: < ...

  2. django博客项目8:文章详情页

    首页展示的是所有文章的列表,当用户看到感兴趣的文章时,他点击文章的标题或者继续阅读的按钮,应该跳转到文章的详情页面来阅读文章的详细内容.现在让我们来开发博客的详情页面,有了前面的基础,开发流程都是一样 ...

  3. django博客项目5:博客首页视图(2)

    真正的 Django 博客首页视图 在此之前我们已经编写了 Blog 的首页视图,并且配置了 URL 和模板,让 Django 能够正确地处理 HTTP 请求并返回合适的 HTTP 响应.不过我们仅仅 ...

  4. django博客项目3:创建 Django 博客的数据库模型

    设计博客的数据库表结构 博客最主要的功能就是展示我们写的文章,它需要从某个地方获取博客文章数据才能把文章展示出来,通常来说这个地方就是数据库.我们把写好的文章永久地保存在数据库里,当用户访问我们的博客 ...

  5. django博客项目2.建立 Django 博客应用

    建立博客应用 我们已经建立了 Django 博客的项目工程,并且成功地运行了它.不过到目前为止这一切都还只是 Django 为我们创建的项目初始内容,Django 不可能为我们初始化生成博客代码,这些 ...

  6. django博客项目1.环境搭建

    安装 Python Windows 下安装 Python 非常简单,去 Python 官方网站找到 Python 3 的下载地址,根据你的系统选择 32 位或者 64 位的安装包,下载好后双击安装即可 ...

  7. 9.28 Django博客项目(一)

    2018-9-28 17:37:18 今天把博客项目 实现了注册和添加图片的功能! 放在了自己的github上面 源码! https://github.com/TrueNewBee/bbs_demo ...

  8. django博客项目6:Django Admin 后台发布文章

    在此之前我们完成了 Django 博客首页视图的编写,我们希望首页展示发布的博客文章列表,但是它却抱怨:暂时还没有发布的文章!如它所言,我们确实还没有发布任何文章,本节我们将使用 Django 自带的 ...

  9. django博客项目4:博客首页视图(1)

    Web 应用的交互过程其实就是 HTTP 请求与响应的过程.无论是在 PC 端还是移动端,我们通常使用浏览器来上网,上网流程大致来说是这样的: 我们打开浏览器,在地址栏输入想访问的网址,比如 http ...

随机推荐

  1. 软件测试工程师如何提高提BUG逼格

    某个周四早上,沏好一杯茶,刚要坐到座位上,就听开发说,你们测试怎么提的Bug,给个截图能说明啥?截图上面显示的奔溃,如果是必现还好,如果不是必现,那么我们怎么去定位?至少给个日志吧?当时我的内心活动是 ...

  2. STL unique

    1: template <class ForwardIterator> 2: ForwardIterator unique (ForwardIterator first, ForwardI ...

  3. 四(2)、springcloud之Ribbon负载均衡

    2.Ribbon负载均衡 ​ Ribbon在工作时分成两步第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server. 第二步再根据用户指定的策略,在从server取到的 ...

  4. 使用Python将字符串转换为格式化的日期时间字符串

    我正在尝试将字符串“20091229050936”转换为“2009年12月29日(UTC)” >>>import time >>>s = time.strptime ...

  5. hdu6354 /// 线段树

    题目大意: 给定n m x y z 长度为n的序列初始为0 接下来m个操作 l r v 将l r区间内比v小的数都变成v l r v由x y z和给定的函数生成 线段树维护区间 最大值 最小值 再加 ...

  6. Oracle之数据类型问题

    做项目涉及到Oracle数据库中数据类型:字符串型的问题 我不太清楚varchar(32)到底代表着什么? 通过搜索了解到:oracle中有三种常用的类型:varchar2(byte),varchar ...

  7. Vue-cli开发笔记三----------引入外部插件

    (一)绝对路径直接引入: (1)主入口页面index.html中头部script标签引入: <script type="text/javascript" src=" ...

  8. tf-idf 词条权重计算

    在文本分类问题中,某些高频词一直出现,这样的词对区分文档的作用不大,例如: D1:  'Job was the chairman of Apple Inc.' D2:  'I like to use ...

  9. Codeforces Round #563 (Div. 2) E. Ehab and the Expected GCD Problem

    https://codeforces.com/contest/1174/problem/E dp 好题 *(if 满足条件) 满足条件 *1 不满足条件 *0 ///这代码虽然写着方便,但是常数有点大 ...

  10. Spring Boot 2.X 实现文件上传(三)

    使用 SpringBoot 项目完成单个.多个文件的上传处理,并将上传的文件保存到指定目录下. 代码演示案例 所有的 HTML 页面文件 index.html <!DOCTYPE html> ...