django项目1 blog博客园系统part1(建立表关系)
我们的所有项目都是建立在数据库的基础上的
为什么呢?
我们为什么要建立数据库,为什么建立一个新的项目的时候需要把表结先理清楚,把需求都放到我们的表结构里面,这是什么流程,为什么要这样,我们一开始在写抽屉页面的时候,就是按照那些前端页面的标签一个一个去拼凑出来的,然后我们拼成了一个貌似完整的页面出来,那个页面是看起来还凑合,好像是一个项目的样子,但是我们的很多图标是超链接的,需要有跳转,而不仅仅是看起来那样的,所以我们自己写的页面不能所有的标签都是copy别人的,我们要有自己的数据去填充页面,去显示出来,就像我们的学员管理系统一样,我们是把那些数据从数据库里面取出来然后通过前端模板去渲染出来,这个渲染的过程就是我们的django项目里面的一整套流程给我们做到的,先是去url里面用正则匹配url地址,然后我们在前端的浏览器里面输入端口ip和url地址之后,就通过django的流程走到我们绑定好的views函数里面,然后通过函数的一系列逻辑判断,去拿到函数的返回值,然后去渲染页面,我们的页面有专门的前端模板,有模板语言,模板语言也是一门语言,它有它自己的逻辑判断在里面,有它自己的规则在,我们按照它的规则去使用它就可以得到不同的页面效果.所以我们的那些抽屉页面里面的各种页面效果显示出来,都是本质上对数据的操作,我们的那些数据都是存入到数据库里面的,然后把他们都拿出来就像我们的学员管理系统一样,把学员信息取出来进行view函数逻辑代码去显示,去实现数据的增删改查.
我们的这一套流程本质上是要实现前端和后端的数据交互,我们回归到本质上,都是基于数据进行操作的,对数据进行增删改查,这是终极点,
我们自己的页面一切的东西都是基于我们的数据库进行操作的,如果有障碍就去回顾我们的学员管理系统,那些班级老师学员的增删改查,都是实现对数据库的操作,然后基于前后端的交互,这就是我们的整个流程.
表结构图:
项目结构图,最终完结版:
如下是我们的小组讨论结果图:
这里是老师给建立的,我们的博客园都有哪些需求,彼此之间是什么关系,都被考虑进来了
博客系统 1 表设计 #==================settings: AUTH_USER_MODEL = "app名称.UserInfo" ======================================= from django.db import models # Create your models here. 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) nickname = models.CharField(verbose_name='昵称', max_length=32) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to = 'avatar_dir/',default="/avatar/default.png") # 它就是我们的imgField一样的作用,就是它本身包容性更强,而我们的imgField只能够包容图片而已 create_time = models.DateTimeField(verbose_name='创建时间', 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(verbose_name='个人博客标题', max_length=64) site = models.CharField(verbose_name='个人博客后缀', max_length=32, unique=True) theme = models.CharField(verbose_name='博客主题', max_length=32) def __str__(self): return self.title class HomeCategory(models.Model): """ 博主个人文章分类表 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='分类标题', max_length=32) blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') def __str__(self): return self.title class Tag(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='标签名称', max_length=32) blog = models.ForeignKey(verbose_name='所属博客', 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, verbose_name='文章标题') desc = models.CharField(max_length=255, verbose_name='文章描述') create_time = models.DateTimeField(verbose_name='创建时间') comment_count= models.IntegerField(default=0) up_count = models.IntegerField(default=0) down_count = models.IntegerField(default=0) homeCategory = models.ForeignKey(to='HomeCategory', to_field='nid', null=True) tags = models.ManyToManyField( to="Tag", through='Article2Tag', through_fields=('article', 'tag'), ) user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid') 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 Comment(models.Model): """ 评论表 """ nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid') user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid') content = models.CharField(verbose_name='评论内容', max_length=255) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) parent_comment = models.ForeignKey('self', null=True) # 决定是根评论还是子评论 def __str__(self): return self.content class ArticleUpDown(models.Model): """ 点赞表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey('UserInfo', null=True) article = models.ForeignKey("Article", null=True) is_up=models.BooleanField(default=True) class Meta: unique_together = [ ('article', 'user'), ] class Article2Tag(models.Model): nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid') tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid') class Meta: unique_together = [ ('article', 'tag'), ] def __str__(self): v=self.article.title+"----"+self.tag.title return v
我们基于form组件和ajax写的注册页面效果:
form组件的类:
from django import forms from django.forms import widgets # 从django里面引入我们的form组件里面的widgets魔法插件 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from .models import User0 class LoginForm(forms.Form): name = forms.CharField( label="姓名", max_length=10, min_length=5, error_messages= { 'required': '不能为空', 'max_length': '最大长度为10', 'min_length': '最小长度为5' } ) pwd = forms.CharField( label="密码", max_length=15, min_length=6, error_messages= { 'required': '不能为空', 'max_length': '最大长度为15', 'min_length': '最小长度为6', }, widget=widgets.PasswordInput(attrs={'class': 'pw'}) # 我们的widgets是设置我们的input标签type的, # 我们使用form组件自动生成的input标签type都是默认的text类型,我们只有在这里使用widgets才能自定义type类型 ) # 注意单词不要写错了,widget没有s = 后面的才有s--->widgets repeated_pwd = forms.CharField( label='确认密码', max_length=15, min_length=6, widget=widgets.PasswordInput(), error_messages= { 'required': '不能为空', 'max_length': '最大长度为15', 'min_length': '最小长度为6', } ) email = forms.EmailField( label='邮箱', error_messages= { 'required': '不能为空', 'invalid': '格式错误', } ) tel = forms.CharField( required=True, label='电话', max_length=11, min_length=6, error_messages= { 'invalid': '格式错误', 'required': '不能为空', } ) def clean_name(self): user = self.cleaned_data.get('name') obj = User0.objects.filter(name=user) # if user.isdigit: # 放到这里的话,我们的页面一直显示用户名不能全是数字,无论你输入什么,心累 # raise ValidationError("用户名不能全是数字") if obj: raise ValidationError('该用户已经注册') # elif user.isdigit: # 放到这里的话我们的页面会有变化,名字长度不够会有对应的提示,已经注册过的也会有相应的提示信息, # 但是输入正确格式的内容就会一直显示这一句提示'用户名不能全是数字',怎么回事啊 # raise ValidationError("用户名不能全是数字") else: return user # 我们这里的返回值是要存入到数据里面的,这个值我们在后端拿到后做完校验就写入到数据库里面了,数据库都是存字符串的而不是query set集合 def clean_tel(self): ret = self.cleaned_data.get('tel') import re obj = re.match('^1[358]\d{4}$', ret) if obj: return ret else: raise ValidationError("格式错误") def clean_repeated_pwd(self): pwd = self.cleaned_data.get('pwd') pwd2 = self.cleaned_data.get('repeated_pwd') if pwd == pwd2: return pwd2 else: raise ValidationError('两次密码不一致,请重新输入')
views视图函数:
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.contrib import auth from django.contrib.auth.models import User from .pakage import LoginForm from setp1 import models # 我们下面用到了User但是这里没有导入模块 import json # 不论是否登录都显示这个页面在模板里面做判断根据登录与否显示不同的效果,如果不用auth内置方法不知道要如何去实现 # def index(request): # user = request.user # return render(request, 'step1/index.html', locals()) def login(request): if request.method == "POST": name = request.POST.get("username") pwd = request.POST.get("password") response = {'bool': True} if models.User0.objects.filter(name=name, pwd=pwd): pass else: response['bool'] = False # return HttpResponse(json.dumps(check)) # 我们这里 return HttpResponse(json.dumps(response)) return render(request, 'step1/login.html') # 先实现form组件的ajax注册, def register(request): # user = request.POST.get('name') # password = request.POST.get("pwd") # email = request.POST.get("email") # repeated_pwd = request.POST.get("repeated_pwd") # tel = request.POST.get("tel") # li = request.POST.getlist('name', 'pwd', 'repeated_pwd', 'email', 'tel') if request.method == 'POST': usinfo = LoginForm(request.POST) if usinfo.is_valid(): user = usinfo.cleaned_data.get('name') password = usinfo.cleaned_data.get('pwd') email = usinfo.cleaned_data.get('email') models.User0.objects.create(name=user, pwd=password, email=email) return HttpResponse(json.dumps(usinfo.errors)) print(usinfo.errors) return HttpResponse(json.dumps(usinfo.errors)) form_obj = LoginForm() return render(request, 'step1/register.html', {"userinfo": form_obj}) def index(request): return render(request, 'step1/index.html')
HTML模板:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>register</title> </head> <body> <h1>注册信息表</h1> {% csrf_token %} {% for field in userinfo %} <div><label for="">{{ field.label }}</label> {{ field }} <span class="sp">{{ field.errors.0 }}</span> {% if field.label == 'Repeated pwd' %} <span>{{ userinfo.non_field_errors }}</span> {% endif %} </div> {% endfor %} <button type="button">提交</button> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> $("button").click(function () { $("span").html(""); $.ajax({ url: '/step/reg/', type: 'post', data: { "name": $("#id_name").val(), "pwd": $("#id_pwd").val(), "repeated_pwd": $("#id_repeated_pwd").val(), "email": $("#id_email").val(), "tel": $("#id_tel").val(), "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); var data = JSON.parse(data); if ( !data.name && !data.pwd && !data.repeated_pwd && !data.email && !data.tel ) { location.href='/step/login'; } $("#id_name").next().html(data.name); $("#id_pwd").next().html(data.pwd); $("#id_repeated_pwd").next().html(data.repeated_pwd); $("#id_email").next().html(data.email); $("#id_tel").next().html(data.tel); } }) }) </script> </body> </html>
登录HTML页面效果:
ajax的登录
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> {% csrf_token %} <p>用户名: <input id="name" type="text" name="username"><span></span></p> <p>密码: <input id="pwd" type="password" name="password"><span></span></p> <button type="button" value="put in">提交</button> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> $("button").click(function () { $.ajax({ {#我们的ajax后面加上({参数在这里写上,url,type,data,data后面需要加上{里面写上参数判断}})#} url: '/step/login/', type: 'post', data: { "username": $("#name").val(), "password": $("#pwd").val(), "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val() }, {# 这里的data是形参,自定义即可#} success: function (ret) { console.log(ret); var obj = JSON.parse(ret); {# 我们的parse这里反序列化的是我们的形参接收过来的那个参数,所以这里的数据要和形参对应上 #} if (obj.bool) { location.href = '/step/index/'; {# location.href = '{{ request.get_full_path }}'; 在我们的js代码里面的ajax里也可以使用request方法#} } $("#name").next().html("用户名输入错误"); $("#pwd").next().html("密码输入错误"); } }); }) </script> </body> </html>