Django之博客系统(基于django的内容管理系统)

网友投稿 324 2022-09-01


Django之博客系统(基于django的内容管理系统)

项目流程:

1、搞清楚需求

基于用户认证组件auth和Ajax实现登录验证(图片验证码)基于forms组件和Ajax实现注册功能设计博客系统首页(文章列表渲染)设计个人站点页面文章详情页实现文章点赞功能实现文章的评论

文章的评论评论的子评论

富文本编辑框以及xss攻击

2、设计表结构

from django.contrib.auth.models import User,AbstractUser class UserInfo(AbstractUser): """ 用户信息 """ nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) blog = models.OneToOneField(to='Blog', to_field='nid', null=True,on_delete=models.CASCADE) 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_name = models.CharField(verbose_name='站点名称', max_length=64) theme = models.CharField(verbose_name='博客主题', max_length=32) def __str__(self): return self.title class Category(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',on_delete=models.CASCADE) 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',on_delete=models.CASCADE) 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='创建时间',auto_now_add=True) content = models.TextField() comment_count=models.IntegerField(default=0) up_count=models.IntegerField(default=0) down_count=models.IntegerField(default=0) user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid',on_delete=models.CASCADE) category = models.ForeignKey(to='Category', to_field='nid', null=True,on_delete=models.CASCADE) tags = models.ManyToManyField( to="Tag", through='Article2Tag', through_fields=('article', 'tag'), ) def __str__(self): return self.title class Article2Tag(models.Model): nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid',on_delete=models.CASCADE) tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid',on_delete=models.CASCADE) class Meta: unique_together = [ ('article', 'tag'), ] def __str__(self): v = self.article.title + "---" + self.tag.title return v class ArticleUpDown(models.Model): """ 点赞表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey('UserInfo', null=True,on_delete=models.CASCADE) article = models.ForeignKey("Article", null=True,on_delete=models.CASCADE) is_up = models.BooleanField(default=True) class Meta: unique_together = [ ('article', 'user'), ] class Comment(models.Model): """ 评论表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid',on_delete=models.CASCADE) article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid',on_delete=models.CASCADE) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) content = models.CharField(verbose_name='评论内容', max_length=255) parent_comment=models.ForeignKey("self",null=True,on_delete=models.CASCADE) def __str__(self): return self.content 根评论:对文章的评论 子评论:对评论的评论 111 444 555 222 333 Comment nid user_id article_id content parent_comment_id(null=True) 1 1 1 111 null 2 2 1 222 null 3 3 1 333 null 4 4 1 444 1 5 5 1 555 4

models.py

3、按照每一个功能分别进行开发

Myforms.py

# -*- coding:utf-8 -*-# Author : yuchao# Data : 2018/7/12 09:19from django import formsfrom django.forms import widgetsfrom blog.models import UserInfofrom django.core.exceptions import NON_FIELD_ERRORS, ValidationError# widgets属性是django对html输入元素的表示,负责渲染html和提取get/post的字典数据class UserForm(forms.Form): user = forms.CharField(max_length=32, error_messages={"required": "该字段不能为空"}, label="用户名", # 渲染后,widget将设置属性attrs={"class": "form-control"} widget=widgets.TextInput(attrs={"class": "form-control"}, ) ) pwd = forms.CharField( max_length=32, label="密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}, )) re_pwd = forms.CharField(max_length=32, label='确认密码', widget=widgets.PasswordInput(attrs={"class": "form-control"}, )) email = forms.EmailField(max_length=32, label="邮箱", widget=widgets.EmailInput(attrs={"class": "form-control"}, )) def clean_user(self): # clean_data是提交成功后的字典数据 val = self.cleaned_data.get("user") user = UserInfo.objects.filter(username=val).first() if not user: return val else: raise ValidationError("该用户已注册") # 描述错误的信息 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("两次密码不一致") # 可以传入元祖数据,定义错误信息,错误代码,传递给错误信息的参数 else: return self.cleaned_data

自定义form

models.py

from django.db import modelsfrom django.contrib.auth.models import AbstractUser# verbose_name 给类模型,起一个可读的名字class UserInfo(AbstractUser): """ 用户信息 """ nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) # 电话 avatar = models.FileField(upload_to='avatars/', default='avatars/default.png') # 默认头像 # 创建时间,auto_now_add作用是无法修改时间 create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) blog = models.OneToOneField(to="Blog", to_field='nid', null=True, on_delete=models.CASCADE) # UserInfo对象的返回结果,以用户名显示 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_name = models.CharField(verbose_name="站点名称", max_length=64) theme = models.CharField(verbose_name="博客主题", max_length=32) def __str__(self): return self.title# 博客分类class Category(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',on_delete=models.CASCADE) def __str__(self): return self.title# 标签分类# 一个blog对应多个tagclass 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', on_delete=models.CASCADE) # tag对象返回tag名字 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="创建时间", auto_now_add=True) content = models.TextField() # 文章内容 comment_count = models.IntegerField(default=0) # 评论数 up_count = models.IntegerField(default=0) # 点赞数 down_count = models.IntegerField(default=0) # 点踩数 # 一对多,一个作者可以有多个文章,外键放在'多'的表里 user = models.ForeignKey(verbose_name="作者", to="UserInfo", to_field="nid", on_delete=models.CASCADE) # 一对多,一个分类可以有多个文章, category = models.ForeignKey(to="Category", to_field="nid", null=True, on_delete=models.CASCADE) # 一个文章可以有多个标签,一个标签可以有多个文章,多对多 ''' manytomanyfield字段会自动生成第三张表 使用through参数,找到自己编写的class,创建第三张表模型,方便扩展字段 ''' tags = models.ManyToManyField( to="Tag", through="Article2Tag", through_fields=("article", "tag"), ) def __str__(self): return self.title# 文章多对多关联标签表class Article2Tag(models.Model): nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name="文章", to="Article", to_field="nid", on_delete=models.CASCADE) tag = models.ForeignKey(verbose_name="标签", to="Tag", to_field="nid", on_delete=models.CASCADE) class Meta: unique_together = [ ('article', 'tag'), ] def __str__(self): v = self.article.title + "---" + self.tag.title return vclass ArticleUpDown(models.Model): ''' 点赞表 哪个用户对哪张表,进行点赞,还是踩灭 ''' nid = models.AutoField(primary_key=True) # 哪个用户操作的,关联用户表 user = models.ForeignKey("UserInfo", null=True, on_delete=models.CASCADE) # 对哪个文章操作的,关联文章表 article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE) is_up = models.BooleanField(default=True) class Meta: # 通过两个字段保持唯一性,文章和用户的组合必须唯一 unique_together = [ ('article', 'user'), ]# 评论class Comment(models.Model): ''' 评论表 ''' nid = models.AutoField(primary_key=True) # 关联用户表,一个用户可以有多条评论 user = models.ForeignKey(verbose_name="评论者", to="UserInfo", to_field="nid", on_delete=models.CASCADE) # 关联文章表,一个文章能有多个文章 article = models.ForeignKey(verbose_name="评论文章", to="Article", to_field='nid', on_delete=models.CASCADE) create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) # 内容 content = models.CharField(verbose_name="评论内容", max_length=255) # 父评论,自关联写法,关联comment,可以写 self parent_comment = models.ForeignKey('self', null=True,on_delete=models.CASCADE) def __str__(self): return self.content ''' 根评论:对文章的评论 子评论:对评论的评论 --------- 如此结构,无法存储子评论 comment表 nid user_id article_id create_time content 这里应该有个parent_comment父评论(自关联) 1 1 1 xx 11111 2 2 1 xx 2222 3 3 1 xx 33333 '''

自定义models.py

settings.py

"""Django settings for project.Generated by 'django-admin startproject' using Django 2.0.1.For more information on this file, seethe full list of settings and their values, seeos# Build paths inside the project like this: os.path.join(BASE_DIR, ...)BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))# Quick-start development settings - unsuitable for production# See SECURITY WARNING: keep the secret key used in production secret!SECRET_KEY = 'eclvh_i(m99c*)$)j$5+pgpjhuv!4lc#z9t3j00d)x3c&*js)x'# SECURITY WARNING: don't run with debug turned on in production!DEBUG = TrueALLOWED_HOSTS = []# Application definitionINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', "blog",]MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',]ROOT_URLCONF = '.urls'TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, },]WSGI_APPLICATION = '.wsgi.application'# Database# DATABASES = {# 'default': {# 'ENGINE': 'django.db.backends.sqlite3',# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),# }# }DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'', # 要连接的数据库,连接前需要创建好 'USER':'root',# 连接数据库的用户名 'PASSWORD':'',# 连接数据库的密码 'HOST':'127.0.0.1', # 连接主机,默认本级 'PORT':3306 # 端口 默认3306 }}# Password validation# = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', },]'''这里不写这个,模板继承AbstractUser报错:auth.User.groups: (fields.E304)'''AUTH_USER_MODEL="blog.UserInfo"# Internationalization# = 'en-us'TIME_ZONE = 'Asia/Shanghai'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = False# Static files (CSS, JavaScript, Images)# = '/static/'STATICFILES_DIRS=[ os.path.join(BASE_DIR,"static"),]# 与用户上传相关的配置MEDIA_ROOT=os.path.join(BASE_DIR,"media")MEDIA_URL="/media/"LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, }}EMAIL_HOST = 'smtp.exmail.qq.com' # 如果是 163 改成 smtp.163.comEMAIL_PORT = 465EMAIL_HOST_USER = '' # 帐号EMAIL_HOST_PASSWORD = '' # 密码# DEFAULT_FROM_EMAIL = EMAIL_HOST_USEREMAIL_USE_SSL = TrueLOGIN_URL="/login/"

django配置文件

urls.py

""" URL ConfigurationThe `urlpatterns` list routes URLs to views. For more information please see: views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home')Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))"""from django.contrib import adminfrom django.urls import path,re_pathfrom django.views.static import servefrom blog import viewsfrom import settingsfrom django.urls import includeurlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), path('index/', views.index), path('logout/', views.logout), re_path('^$', views.index), path('get_validCode_img/', views.get_valid_code_img), path('register/', views.register), # 文本编辑器上传图片url path('upload/', views.upload), # 后台管理url re_path("cn_backend/$",views.cn_backend), re_path("cn_backend/add_article/$",views.add_article), # 点赞 path("digg/",views.digg), # 评论 path("comment/",views.comment), # 获取评论树相关数据 path("get_comment_tree/",views.get_comment_tree), # media配置: re_path(r"media/(?P.*)$",serve,{"document_root":settings.MEDIA_ROOT}), re_path('^(?P\w+)/articles/(?P\d+)$', views.article_detail), # article_detail(request,username="yuan","article_id":article_id) # 个人站点的跳转 re_path('^(?P\w+)/(?Ptag|category|archive)/(?P.*)/$', views.home_site), # home_site(reqeust,username="yuan",condition="tag",param="python") # 个人站点url re_path('^(?P\w+)/$', views.home_site), # home_site(reqeust,username="yuan")]

路由控制器

模板文件配置

templates文件夹

{% extends "base.html" %}{% block content %} {% csrf_token %}

{% endblock %}

article_detail.html

Title

{{ blog.title }} 管理

{% block content %} {% endblock %}

base.html

我的标签
{% for tag in tag_list %}

{{ tag.0 }}({{ tag.1 }})

{% endfor %}
随笔分类
{% for cate in cate_list %}

{{ cate.0 }}({{ cate.1 }})

{% endfor %}
随笔归档
{% for date in date_list %}

{{ date.0 }}({{ date.1 }})

{% endfor %}

classification.html

{% extends 'base.html' %}{% block content %}

{% for article in article_list %}
{{ article.title }}
{{ article.desc }}
发布于   {{ article.create_time|date:"Y-m-d H:i" }}   评论({{ article.comment_count }})   点赞({{ article.up_count }})  

{% endfor %}
{% endblock %}

home_site.html

Title

Panel heading without title
Panel content
Panel heading without title
Panel content
Panel heading without title
Panel content
{% for article in article_list %}
{{ article.title }}
{{ article.desc }}
{{ article.user.username }}     发布于   {{ article.create_time|date:"Y-m-d H:i" }}   评论({{ article.comment_count }})   点赞({{ article.up_count }})  

{% endfor %}
Panel heading without title
Panel content
Panel heading without title
Panel content

index.html

Title

登录页面

{% csrf_token %}
注册

login.html

Title

404. 抱歉! 您访问的资源不存在!

请确认您输入的网址是否正确,如果问题持续存在,请发邮件至 404042726@qq.com 与 老村长 联系。

返回网站首页

not_found.html

Title

注册页面

{% csrf_token %} {% for field in form %}
{{ field }}
{% endfor %}

register.html

backend文件夹

{% extends 'backend/base.html' %}{% block content %}

{% csrf_token %}
添加文章
{% endblock %}

add_article.html

{% extends 'backend/base.html' %}{% block content %}

{% for article in article_list %} {% endfor %}
标题 评论数 点赞数 操作 操作
{{ article.title }} {{ article.comment_count }} {{ article.up_count }} 编辑 删除
{% endblock %}

backend.html

博客后台管理 - 博客园

后台管理 注销   {{ request.user.username }}

{% block content %} {% endblock %}

base.html

4、功能测试

5、项目部署上线

努力成为一个开发者 个人站点:pythonav.cn


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:代码风格与文件模板(文件格式模板)
下一篇:Java BigDecimal类的一般使用、BigDecimal转double方式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~