博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
BBS项目
阅读量:5236 次
发布时间:2019-06-14

本文共 14501 字,大约阅读时间需要 48 分钟。

一、需求分析

1.首页(显示文章)

文章详情

点赞,点踩

文章评论(子评论,评论的展示)

登录功能(图片验证码)

注册功能(基于form验证,ajax)

个人站点(不同人不同样式,文章过滤)

后台管理(文章展示)

新增文章(富文本编辑器)

2.设计程序(框架,数据库设计)

UserInfo用户表

blog个人站点表

Aticle文章表

commit评论表

upanddown点赞点踩表

category文章分类表

tag文章标签表

 

表关系

userinfo跟blog一对一

article跟blog一对多

article跟category一对多

article跟tag多对多

commit跟article一对多

upanddown跟article一对多

user跟commit一对多

user跟upanddown一对多

category跟blog一对多

tag跟blog一对多

二、settings配置和models里建表

settings配置

#设置LANGUAGE_CODE = 'zh-hans',则后台管理变成中文LANGUAGE_CODE = 'zh-hans'#设置时区TIME_ZONE = 'Asia/Shanghai'USE_I18N = TrueUSE_L10N = True#不用UTC时间USE_TZ = False# Static files (CSS, JavaScript, Images)# https://docs.djangoproject.com/en/1.11/howto/static-files/STATIC_URL = '/static/'STATICFILES_DIRS=[    os.path.join(BASE_DIR,'static'),]#auth模块未登录时转到的路由LOGIN_URL='/login/'#舍弃系统给的user表,自定义auth认证的userinfo表AUTH_USER_MODEL='blog.UserInfo'EMAIL_HOST='smtp.qq.com'#设置以什么邮箱发送EMAIL_PORT=465#端口号EMAIL_HOST_USER='1500519302@qq.com'#发送者邮箱账号EMAIL_HOST_PASSWORD='odokxrzlfejkcbbh'#授权码DEFAULT_FROM_EMAIL=EMAIL_HOST_USER#显示收件人EMAIL_USE_SSL=True#使用ssl加密,qq邮箱只支持这个#  avatar = models.FileField(upload_to='avatar/', default='/static/img/default.png')#如果不配置MEDIA_ROOT,和MEDIA_URL,默认会存到avatar#如果不配置MEDIA_ROOT,和MEDIA_URL,默认会存到media/avatarMEDIA_URL='/media/'MEDIA_ROOT=os.path.join(BASE_DIR,'media')MEDIA_BBS=os.path.join(BASE_DIR,'BBS')
View Code

models建表

from django.db import modelsfrom django.contrib.auth.models import AbstractUser# Create your models here.# UserInfo这个表,继承AbstractUser,因为要用auth组件class UserInfo(AbstractUser):    nid = models.AutoField(primary_key=True)    # username=models.CharField(max_length=32,unique=True)    # 该字段可以为空,为该字段设置默认值,default='123455666'    # blank=True  只是admin中表单提交的时候,做校验,如果设置成True,就是不校验了    phone = models.CharField(max_length=32,null=True,blank=True)    # upload_to需要传一个路径(avatar文件夹会自动创建)    avatar = models.FileField(upload_to='avatar/', default='avatar/default.png')    # 如果avatar用charfield,如何处理?    #  一对一关联blog表,to_field如果不写,默认主键    # blog_id字段存的数据是什么?blog表的---nid这个字段    blog = models.OneToOneField(to='Blog', to_field='nid',null=True)    class Meta:        # admin中显示表名        verbose_name='用户表'        # admin中表名s去掉        verbose_name_plural = verbose_name    # user表    #     id name  blog_id    #     1  111    1    #     2  111    1class Blog(models.Model):    nid = models.AutoField(primary_key=True)    title = models.CharField(max_length=64)    site_name = models.CharField(max_length=32)    theme = models.CharField(max_length=64)    def __str__(self):        return self.site_nameclass Category(models.Model):    nid = models.AutoField(primary_key=True)    title = models.CharField(max_length=64)    # ForeignKey跟OneToOneField的区别?    #OneToOneField unique=True    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)    def __str__(self):        return self.titleclass Tag(models.Model):    nid = models.AutoField(primary_key=True)    title = models.CharField(max_length=64)    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)    def __str__(self):        return self.titleclass Article(models.Model):    nid = models.AutoField(primary_key=True)    # verbose_name='文章标题'  修改admin中表单的文字显示    title = models.CharField(max_length=64,verbose_name='文章标题')    # 摘要,简单描述    desc = models.CharField(max_length=255)    # 大文本TextField()    content = models.TextField()    # 存时间类型,auto_now_add每插入一条数据,时间自动写入当前时间,    # auto_now,这条数据修改的时候,会更新成当前时间    create_time = models.DateTimeField(auto_now_add=True)    # 因为查询多,写入少,所以加这三个字段,以后不需要再连表查询了    commit_num=models.IntegerField(default=0)    up_num=models.IntegerField(default=0)    down_num=models.IntegerField(default=0)    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)    category = models.ForeignKey(to='Category', to_field='nid', null=True)    # through_fields应该怎么写?    # 中介模型,手动创建第三张表,through是通过哪个表跟Tag建立关系,through_fields为了查询用的    tag = models.ManyToManyField(to='Tag', through='ArticleTOTag', through_fields=('article', 'tag'))    # 这样写,会自动创建第三张表    # tag = models.ManyToManyField(to='Tag')    def __str__(self):        return self.title+'----'+self.blog.userinfo.username# 手动创建第三张表class ArticleTOTag(models.Model):    nid = models.AutoField(primary_key=True)    article = models.ForeignKey(to='Article', to_field='nid')    tag = models.ForeignKey(to='Tag', to_field='nid')    # article和tag应不应该联合唯一?    # article_id  1    # tag_id     1class Commit(models.Model):    nid = models.AutoField(primary_key=True)    user = models.ForeignKey(to='UserInfo', to_field='nid')    article = models.ForeignKey(to='Article', to_field='nid')    content = models.CharField(max_length=255)    create_time = models.DateTimeField(auto_now_add=True)    # 这样写是可以的    # parent_id=models.IntegerField()    # 自关联    # parent_id=models.ForeignKey(to='Commit',to_field='nid')    parent = models.ForeignKey(to='self', to_field='nid',null=True,blank=True)class UpAndDown(models.Model):    nid = models.AutoField(primary_key=True)    user = models.ForeignKey(to='UserInfo', to_field='nid')    article = models.ForeignKey(to='Article', to_field='nid')    is_up = models.BooleanField()    class Meta:        # 写这些,只是为了不写脏数据,联合唯一        unique_together = (('user', 'article'),)
View Code

models中 class Meta 定义的是元数据的属性,不会生成字段

建表时,一对多关系可以这么理解:找出哪个是一,哪个是多。在多的那个对象建表,然后建字段

django中的orm自关联:表内自关联是指表内数据相关联的对象和表是相同字段,这样我们就直接用表内关联将外键关联设置成自身表的字段。比如:微博评论,每条评论都可能有自评论,但每条子评论都只有一个父评论,这就满足了,一对多的情形。父评论为关联字段,可以对应多个子评论,这就是一对多的自关联。

parent = models.ForeignKey(to='self', to_field='nid',null=True,blank=True)

三、生成图片验证码

首先要下载Pillow

后台代码如下

def login(request):    if request.method=='GET':        return render(request,'login.html')    elif request.is_ajax():        response={
'user':None,'msg':None} name=request.POST.get('name') password=request.POST.get('password') valid_code=request.POST.get('valid_code') ret=request.session.get('valid_session') print(ret) if valid_code.upper()==request.session.get('valid_session').upper(): user=auth.authenticate(request,username=name,password=password) #这里auth.authenticate如果没有登录成功,user是None。如果登录成功,user则是登录的user对象 if user: # print(user) #ajax请求,不能再返回 #校验通过,一定要登录 auth.login(request,user) response['user']=name response['msg']='登录成功' else: # print(user) response['msg']='用户名或密码错误' else: response['msg']='验证码错误' return JsonResponse(response)def get_random_color(): return (random.randint(0,255),random.randint(0,255),random.randint(0,255))def get_valid_code(request): #图片验证码 # 第一种方式,直接用服务器的图片 # with open('static/media/img/315557.jpg','rb') as f: # data=f.read() # return HttpResponse(data) #第二种方式 #生成一张图片,第一个参数是模式:'rgb',第二个参数是图片大小(宽,高),第三个参数是图片颜色 # img=Image.new('RGB',(200,35),color=get_random_color()) # with open('valid_code.png','wb') as f: # #img直接封装了保存图片的方法 # img.save(f,'png') # with open('valid_code.png','rb') as f: # data=f.read() # return HttpResponse(data) #第二种方式有个漏洞,它要将随机生成的图片保存在服务器,这不合理,因此不用第二种方法 #第三种方式 # 在内存中生成一个空文件(把它想象成 open('valid_code.png', 'wb') as f:) # 一个是在硬盘上,一个是在内存中 #先导入io模块,用内存生成文件 # from io import BytesIO # img=Image.new('RGB',(200,35),color=get_random_color()) # f=BytesIO() # #把图片保存到f中, # #放到内存中,存取比较快,而且有自动清理 # img.save(f,'png') # data=f.getvalue() # return HttpResponse(data) #然后再第三种模式的基础上,再在生成的图片上进行写出随机字符串 # img=Image.new('RGB',(200,35),color=get_random_color()) # img_draw=ImageDraw.Draw(img) # #生成一个字体对象,第一个参数是字体文件的路径,第二个参数是大小 # font=ImageFont.truetype('static/font/ss.TTF',size=25) # #第一个参数:xy(x,y)的坐标,第二个参数:要写的文字,第三个参数:写文字的颜色,第四个参数:字体 # img_draw.text((0,0),'python',get_random_color(),font=font) # f=BytesIO() # img.save(f,'png') # data=f.getvalue() # return HttpResponse(data) #终极版本 img=Image.new('RGB',(200,35),color=get_random_color()) img_draw=ImageDraw.Draw(img) font=ImageFont.truetype('static/font/ss.TTF',size=25) random_code='' for i in range(5): char_num=random.randint(0,9) char_lower=chr(random.randint(97,122)) char_upper=chr(random.randint(65,90)) char_str=str(random.choice([char_num,char_lower,char_upper])) img_draw.text((i*30+20,0),char_str,get_random_color(),font=font) random_code+=char_str #在图片上画线画点画圆等 # width = 200 # height = 35 # for i in range(2): # x1 = random.randint(0, width) # x2 = random.randint(0, width) # y1 = random.randint(0, height) # y2 = random.randint(0, height) # # 在图片上画线。x1,y1表示起始坐标,x2,y2表示终止坐标 # img_draw.line((x1, y1, x2, y2), fill=get_random_color()) # # for i in range(5): # # 画点 # img_draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) # x = random.randint(0, width) # y = random.randint(0, height) # # 画弧形 # img_draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) #把验证码保存到session中。验证码需要保存 print(random_code) request.session['valid_session']=random_code f=BytesIO() img.save(f,'png') data=f.getvalue() return HttpResponse(data)
View Code

前台代码如下

    
登录页面
{#img的src指向的可以是一个路由#}
View Code

四、注册功能

注册功能用ajax进行提交,要有局部刷新功能,使用forms组件对前台进行校验

前端html中,accept要配合input file联用,表示接收哪种格式文件

前端代码如下

    
注册
{% for foo in my_form %}
{# 这里循环时,forms组件会自动为其加上一个id,可以取前端看一看,在这里可以用{
{ foo.auto_id }}当做id #}
{
{ foo }}
{% endfor %} {# 加一个上传文件的控件,上传头像 #}
{# accpet表示可以接收哪种格式文件,只能与input file联用 #}
View Code

后台代码如下

def register(request):    if request.method=='GET':        my_form=myforms.MyForm()        return render(request,'register.html',locals())    elif request.is_ajax():        print(request.POST)        response={
'status':100,'msg':None} my_form=myforms.MyForm(request.POST)#校验POST里的所有数据,forms可以直接接字典 if my_form.is_valid(): dic=my_form.cleaned_data dic.pop('re_password') my_file=request.FILES.get('my_file') #如果上传的文件为空,这个字段不穿,数据库里存默认值。 if my_file: dic['avatar']=my_file user=models.UserInfo.objects.create_user(**dic) #看看存没存进去 print(user.username) #要跳转的路径 response['url']='/login/' else: response['status']=101 response['msg']=my_form.errors return JsonResponse(response)def check_username(request): response={
'status':100,'msg':None} name=request.POST.get('name') user=models.UserInfo.objects.filter(username=name).first() if user: response['status']=101 response['msg']='用户名已被占用' return JsonResponse(response)
View Code

 五、admin表配置

要在app下的admin.py进行配置注册一下,这样就能控制相应的表了

from django.contrib import adminfrom blog import models# Register your models here.admin.site.register(models.UserInfo)admin.site.register(models.Blog)admin.site.register(models.Article)admin.site.register(models.Tag)admin.site.register(models.Category)admin.site.register(models.Commit)admin.site.register(models.UpAndDown)admin.site.register(models.ArticleTOTag)
View Code

 六、meida路径配置

用户上传的文件最好配置一个文件夹,在路由层做一下配置。

可以可以把导入的serve当一个views函数,就是帮meidia开了一个路由接口,path分组是serve函数必须传的一个参数

from django.views.static import servefrom ff188 import settingsurlpatterns = [    url(r'^admin/', admin.site.urls),    url(r'^login/', views.login),    url(r'^get_valid_code/', views.get_valid_code),    url(r'^make/', views.make),    url(r'^register/', views.register),    url(r'^check_username/', views.check_username),    url(r'^index/', views.index),    url(r'^logout/', views.logout),    #url第一个参数,正则表达式,第二个参数是函数的内存地址,第三个参数是一个字典,它会以关键字参数的形式传到第二个参数中,第四个参数是名字,反向解析用    #当进行这些设置后,就可以访问media下的资源了    url(r'^media/(?P
.*)', serve,{
'document_root':settings.MEDIA_ROOT}),]
View Code

七、个人主页

在static文件夹下创建css文件夹,把所有用户自定义的css样式都保存进去

    
{<div></div> { username }}的个人博客

{
{ blog.title }}

我的分类

请联系:199999999

年入60w
随笔档案
View Code

 八、文章归类

#所有分类对应文章数    category_num=models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values('title','coun')    #所有标签对应文章数    tag_num=models.Tag.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values('title','coun')    #按日期分类    from django.db.models.functions import TruncMonth    #切分时间    #这样查询到的y_m是一个datetime对象,到前端用date过滤即可    time_article=models.Article.objects.filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(coun=Count('y_m')).values('y_m','coun')
View Code

这里补充一下ajax中$(this)的意思,像面向对象语言(如JAVA),简单的说,谁在调用它,它就代表谁

$("#btnConfirm").click(function(){

alert($(this).val()); //看这里,this代表的其实就是这个为btnConfirm的按钮,因为你现在点击的是为btnConfirm的按钮,那么this就是它
});

jquery中html(),text(),val()是有一些区别的

html就是可以对添加了<a></a>,<p></p>等设置值

val()是只有有value属性的才能取到或设置值

text()方法设置或返回被选元素的文本内容

 

转载于:https://www.cnblogs.com/xufengnian/p/10115591.html

你可能感兴趣的文章
【★】浅谈计算机与随机数
查看>>
解决 sublime text3 运行python文件无法input的问题
查看>>
javascript面相对象编程,封装与继承
查看>>
Atlas命名空间Sys.Data下控件介绍——DataColumn,DataRow和DataTable
查看>>
Java中正则表达式的使用
查看>>
算法之搜索篇
查看>>
新的开始
查看>>
java Facade模式
查看>>
NYOJ 120校园网络(有向图的强连通分量)(Kosaraju算法)
查看>>
Leetcode 226: Invert Binary Tree
查看>>
http站点转https站点教程
查看>>
解决miner.start() 返回null
查看>>
bzoj 2007: [Noi2010]海拔【最小割+dijskstra】
查看>>
BZOJ 1001--[BeiJing2006]狼抓兔子(最短路&对偶图)
查看>>
C# Dynamic通用反序列化Json类型并遍历属性比较
查看>>
128 Longest Consecutive Sequence 一个无序整数数组中找到最长连续序列
查看>>
定制jackson的自定义序列化(null值的处理)
查看>>
auth模块
查看>>
javascript keycode大全
查看>>
前台freemark获取后台的值
查看>>