创建项目
django-admin startproject blog
settings.py中配置mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
# 数据库名字
'NAME': 'blog',
# 用户名
'USER': 'root',
# 密码
'PASSWORD': 'wxm20010428',
# 主机
'HOST': 'localhost',
# 端口
'PORT': '3306',
}
}
settings.py中配置redis
pip install django-redis
CACHES = {
"default": { # 默认
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
"session": { # session
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "session"
cmd进入Redis安装目录
redis-server.exe redis.windows.conf
settings.py中配置log
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 是否禁用已经存在的日志器
'formatters': { # 日志信息显示的格式
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
},
},
'filters': { # 对日志进行过滤
'require_debug_true': { # django在debug模式下才输出日志
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': { # 日志处理方法
'console': { # 向终端中输出日志
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': { # 向文件中输出日志
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, 'logs/blog.log'), # 日志文件的位置
'maxBytes': 300 * 1024 * 1024,
'backupCount': 10,
'formatter': 'verbose'
},
},
'loggers': { # 日志器
'django': { # 定义了一个名为django的日志器
'handlers': ['console', 'file'], # 可以同时向终端与文件中输出日志
'propagate': True, # 是否继续传递日志信息
'level': 'INFO', # 日志器接收的最低日志级别
},
}
}
settings.py中配置static
# 配置静态文件加载路径
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
python manage.py startapp user
user.apps.UserConfig
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# 导入Django的view
from django.views import View
# 注册视图
class RegisterView(View):
def get(self, request):
return render(request, 'register.html')
# 进行user子应用的视图路由
from django.urls import path
from blog.user.views import RegisterView
urlpatterns = [
# 第二个参数是视图函数 这里使用类视图转换为视图函数
path('register/', RegisterView.as_view(), name='register')
]
urlpatterns = [
path('admin/', admin.site.urls),
# 配置user子应用的路由 include的第一个参数是一个元组 包含子应用的路由和子应用名
path('', include(('user.urls', 'user'), namespace='user'))
]
from django.db import models
from django.contrib.auth.models import AbstractUser
# 用户信息
class User(AbstractUser):
# 电话号码字段
# unique 为唯一性字段
mobile = models.CharField(max_length=11, unique=True, blank=False)
# 头像
# upload_to为保存到响应的子目录中
avatar = models.ImageField(upload_to='avatar/%Y%m%d/', blank=True)
# 个人简介
user_desc = models.TextField(max_length=500, blank=True)
# 内部类 class Meta 用于给 model 定义元数据
class Meta:
db_table = 'tb_user' # 修改默认的表名
verbose_name = '用户信息' # Admin后台显示
verbose_name_plural = verbose_name # Admin后台显示
def __str__(self):
return self.mobile
# 替换系统的User 来使用我们自己定义的User
# 配置信息为 ‘子应用名.模型类型’
AUTH_USER_MODEL = 'user.User'
python manage.py makemigrations
python manage.py migrate
在项目根目录下创建libs文件夹
在网上下载图片验证码的库captcha
将captcha放到libs目录下
在user.view.py中编写验证码视图
# 验证码视图
class ImageCodeView(View):
def get(self, request):
# 接收前端传递来的uuid
uuid = request.GET.get('uuid')
# 判断uuid是否获取到
if uuid is None:
return HttpResponseBadRequest("没有传递uuid")
# 通过调用captcha来生成图片验证码
# text是图片二进制 image是图片内容
text, image = captcha.generate_captcha()
# 将图片内容保存到redis
# uuid是key 图片内容是value
redis_conn = get_redis_connection('default')
redis_conn.setex('img:%s' % uuid, 300, text)
# 返回图片内容
return HttpResponse(image, content_type='image/jpeg')
在user.urls.py中编写路由
path('imagecode/', ImageCodeView.as_view(), name='imagecode')
在浏览器输入url测试
http://127.0.0.1:8000/imagecode/?uuid=123
在register.html中找到验证码处使用:绑定register.js中的image_code_url
在浏览器中http://127.0.0.1:8000/register/进行刷新查看验证码变化
此处选择容联云平台!
注册登录绑定测试号码!
将yuntongxun文件夹解压到libs目录下!
打开sms.py修改个人相关信息!
主要修改:
_accountSid
_accountToken
_appId
ccp.send_template_sms
在项目根目录下创建utils文件夹!
将response_code.py放到utils文件夹下!
在user.view.py中编写视图类
# 日志操作
import logging
logger = logging.getLogger('django')
# 短信验证码视图
class SmsCodeView(View):
def get(self, request):
# 接收参数
mobile = request.GET.get('mobile')
image_code = request.GET.get('image_code')
uuid = request.GET.get('uuid')
# 验证参数 all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE
if not all([mobile, image_code, uuid]):
# 参数不齐全
return JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必要的参数'})
redis_conn = get_redis_connection('default')
redis_image_code = redis_conn.get('img:%s' % uuid)
if redis_image_code is None:
# 判断图片验证码是否存在
return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图片验证码已过期'})
# 如果拿到了那就删除 由于涉及到redis删除 故需要异常捕获
try:
redis_conn.delete('img:%s' % uuid)
except Exception as e:
logger.error(e)
# 比对图片验证码时注意大小写处理
if redis_image_code.decode().lower() != image_code.lower():
return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图片验证码错误'})
# 生成短信验证码
sms_code = '%06d' % randint(0, 999999)
# 为了后期比对方便 我们可以将短信验证码记录到日志中
logger.info(sms_code)
# 保存短信验证码到redis中
redis_conn.setex('sms:%s' % mobile, 300, sms_code)
# 发送短信
CCP().send_template_sms(mobile, [sms_code, 5], 1)
# 返回响应
return JsonResponse({'code': RETCODE.OK, 'errmsg': '短信发送成功'})
在user.urls.py中编写路由
path('smscode/', SmsCodeView.as_view(), name='smscode')
def post(self, request):
# 接收数据
mobile = request.POST.get('mobile')
password = request.POST.get('password')
password2 = request.POST.get('password2')
smscode = request.POST.get('sms_code')
# 验证数据
if not all([mobile, password, password2, smscode]):
return HttpResponseBadRequest('缺少必要的参数')
if not re.match(r'^1[3-9]\d{9}$', mobile):
return HttpResponseBadRequest('手机号不符合规则')
if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
return HttpResponseBadRequest('请输入8-20位密码,密码是数字字母!')
if password != password2:
return HttpResponseBadRequest('两次密码不一致')
redis_conn = get_redis_connection('default')
redis_sms_code = redis_conn.get('sms:%s' % mobile)
if redis_sms_code is None:
return HttpResponseBadRequest('短信验证码已过期')
if smscode != redis_sms_code.decode():
return HttpResponseBadRequest('短信验证码不一致')
# 保存注册信息
# create_user可以对密码加密 数据操作需要异常捕获
try:
user = User.objects.create_user(username=mobile, mobile=mobile, password=password)
except DatabaseError as e:
logger.error(e)
return HttpResponseBadRequest('注册失败')
# 返回响应跳转到指定页面
return HttpResponse('注册成功')
创建home子应用
python manage.py startapp home
注册home子应用
home.apps.HomeConfig
将index.html放在templates文件夹
在home.views.py中编写视图
# 首页视图
from django.views import View
class IndexView(View):
def get(self, request):
return render(request, 'index.html')
在home.urls.py中编写路由
from django.urls import path
from home.views import IndexView
urlpatterns = [
path('', IndexView.as_view(), name='index')
]
在项目目录下编写路由
path('', include(('home.urls', 'home'), namespace='home'))
在user.views.py中更改
return redirect(reverse('home:index'))
在上述的重定向之前,调用login。
# 调用login实现注册成功后状态保持
login(request, user)
浏览器测试的时候查看session和cookie。
查看redis。
redis-cli.exe -h 127.0.0.1 -p 6379
keys *
select 1
keys *
FLUSHdb
# 调用login实现注册成功后状态保持
login(request, user)
# 返回响应跳转到指定页面
# reverse是通过namespace:name来获取视图对应的路由
response = redirect(reverse('home:index'))
# 设置跳转到首页展示用户信息 cookie
response.set_cookie('is_login', True)
response.set_cookie('username', user.username, max_age=7 * 24 * 3600)
return response
在user.views.py文件中定义视图。
# 登录视图
class LoginView(View):
def get(self, request):
return render(request, 'login.html')
在user.urls.py文件中定义路由。
path('login/', LoginView.as_view(), name='login')
修改login.html中的资源加载方式。
{% url "app名称:路径的name" %}
在user.views.py文件中定义视图。
def post(self, request):
# 接收参数
mobile = request.POST.get('mobile')
password = request.POST.get('password')
remember = request.POST.get('remember')
# 验证参数
if not re.match(r'^1[3-9]\d{9}$', mobile):
return HttpResponseBadRequest('手机号不符合规则')
if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
return HttpResponseBadRequest('密码不符合规则')
# 认证登录
# authenticate 系统自带的认证
user = authenticate(mobile=mobile, password=password)
if user is None:
return HttpResponseBadRequest('用户名或者密码错误')
# 状态保持 拿到user 将user传递过去
login(request,user)
response=redirect(reverse('home:index'))
# 页面跳转
if remember !='on':
# 没有记住信息则是浏览器关闭后
request.session.set_expiry(0)
# 设置cookie信息
response.set_cookie('is_login',True)
response.set_cookie('username',user.username,max_age=14*24*3600)
else:
# 记住信息则是默认两周
request.session.set_expiry(None)
response.set_cookie('is_login', True,max_age=14*24*3600)
response.set_cookie('username', user.username, max_age=14 * 24 * 3600)
# 返回响应
return response
在user.models.py文件中修改模型类。
# 修改认证字段为手机号
USERNAME_FIELD = 'mobile'
在浏览器中测试http://127.0.0.1:8000/login/登录。
在index.html中修改admin。
[[username]]
在index.js的mounted()中修改is_login。
this.is_login=getCookie('is_login')
在user.views.py文件中定义视图。
# 退出登录视图
class LogoutView(View):
def get(self, request):
# session数据清除
logout(request)
# 删除部分session数据
response = redirect(reverse('home:index'))
response.delete_cookie('is_login')
# 跳转到首页
return response
在index.html中修改href。
退出登录
在user.views.py中编写视图。
# 忘记密码视图
class ForgetPasswordView(View):
def get(self, request):
return render(request, 'forget_password.html')
在user.urls.py中编写路由。
path('forgetpassword/', ForgetPasswordView.as_view(), name='forgetpassword'),
在user.views.py中编写视图。
def post(self, request):
# 接收数据
mobile = request.POST.get('mobile')
password = request.POST.get('password')
password2 = request.POST.get('password2')
smscode = request.POST.get('sms_code')
# 验证数据
if not all([mobile, password, password2, smscode]):
return HttpResponseBadRequest('缺少必要的参数')
if not re.match(r'^1[3-9]\d{9}$', mobile):
return HttpResponseBadRequest('手机号不符合规则')
if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
return HttpResponseBadRequest('请输入8-20位密码,密码是数字字母!')
if password != password2:
return HttpResponseBadRequest('两次密码不一致')
redis_conn = get_redis_connection('default')
redis_sms_code = redis_conn.get('sms:%s' % mobile)
if redis_sms_code is None:
return HttpResponseBadRequest('短信验证码已过期')
if smscode != redis_sms_code.decode():
return HttpResponseBadRequest('短信验证码不一致')
# 保存注册信息
# create_user可以对密码加密 数据操作需要异常捕获
try:
user = User.objects.get(mobile=mobile)
except User.DoesNotExist:
try:
# 没有查出则创建新用户
User.objects.create_user(username=mobile, mobile=mobile, password=password)
except Exception:
return HttpResponseBadRequest("修改失败,下次再试试!")
else:
# 查出则修改用户密码
user.set_password(password)
# 保存 用户信息
user.save()
# 返回响应跳转到指定页面
# reverse是通过namespace:name来获取视图对应的路由
response = redirect(reverse('user:login'))
return response
在浏览器测试修改密码并再次登录。
将center.html放到templates文件夹。
在user.views.py中编写视图。
# 用户中心视图
class UserCenterView(View):
def get(self,request):
return render(request,'center.html')
在user.urls.py中编写路由。
path('usercenter/', UserCenterView.as_view(), name='usercenter')
在settings.py中修改用户未登录url。
# 修改系统的未登录跳转链接
LOGIN_URL = '/login/'
在登录视图中设置页面未登录逻辑判断。
# 根据next参数来进行页面的跳转
next_page = request.GET.get('next')
if next_page:
response = redirect(next_page)
else:
response = redirect(reverse('home:index'))
这样就可以实现,当用户未登录时,若想要查看个人信息,就会跳转到登录页面,然后登录后,就到个人中心。
在user.views.py中修改视图。
# 用户中心视图
# LoginRequiredMixin封装了判断用户是否登录
# 如果未登录直接跳转到 http://127.0.0.1:8000/accounts/login/?next=/usercenter/
class UserCenterView(LoginRequiredMixin, View):
def get(self, request):
# 获取用户信息
user = request.user
# 组织获取用户的信息
context = {
'username': user.username,
'mobile': user.mobile,
'avatar': user.avatar.url if user.avatar else None,
'user_desc': user.user_desc
}
return render(request, 'center.html', context=context)
在center.html中修改渲染内容。
{% if avatar %}
头像
{% else %}
暂无头像
{% endif %}
在浏览器端测试。
在user.views.py中修改视图。
def post(self, request):
# 获取参数
user = request.user
# 如果没有获取到修改 就使用原来的
username = request.POST.get('username', user.username)
user_desc = request.POST.get('user_desc', user.user_desc)
avatar = request.FILES.get('avatar')
# 保存参数 凡是涉及到数据库操作的都异常捕获
try:
user.username = username
user.user_desc = user_desc
if avatar:
# avatar是图片路径 类型为ImageField
user.avatar = avatar
user.save()
except Exception as e:
logger.error(e)
return HttpResponseBadRequest('修改失败,请稍后再试!')
# 更新cookie
# 刷新当前页面
response = redirect(reverse('user:usercenter'))
response.set_cookie('username', user.username, max_age=14 * 24 * 3600)
# 返回响应
return response
在settings.py文件中设置图片上传的路径并新建文件夹media。
# 设置上传的头像到media
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
MEDIA_URL = '/media/'
在工程的urls.py文件中设置设置路由匹配规则。
# 设置media图片
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# 写博客视图
class WriteBlogView(View):
def get(self, request):
return render(request, 'write_blog.html')
path('writeblog/', WriteBlogView.as_view(), name='writeblog'),
在home.models.py中编写分类模型类。
from django.db import models
# Create your models here.
# 文章模型类
from django.utils import timezone
class ArticleCategory(models.Model):
# 分类标题
title = models.CharField(max_length=100, blank=True)
# 分类的创建时间
created = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
class Meta:
db_table = 'tb_category'
verbose_name = '类别管理'
verbose_name_plural = verbose_name
在控制台生成迁移文件。
python manage.py makemigrations
python manage.py migrate
Django后台管理:http://127.0.0.1:8000/admin/。
使用Django的管理模块, 需要按照如下步骤操作 :
1.管理界面本地化。
2.创建管理员。
3.注册模型类。
4.发布内容到数据库。
在settings.py中设置。
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'
在user.models.py中修改User类。
# 创建超级管理员必须输入的字段
REQUIRED_FIELDS = ['username','email']
在控制台创建超级用户并按要求输入相应信息。
python manage.py createsuperuser
登录后台管理。
在home.admin.py中注册模型。
from django.contrib import admin
# Register your models here.
# 注册模型
from home.models import ArticleCategory
admin.site.register(ArticleCategory)
在user.views.py中修改视图。
# 写博客视图 登录用户才可以访问视图
class WriteBlogView(LoginRequiredMixin,View):
def get(self, request):
# 查询所有分类模型
categories = ArticleCategory.objects.all()
context = {
'categories':categories,
}
return render(request, 'write_blog.html',context=context)
在write_blog.html中修改接口。
在浏览器测试。
在home.views.py中编写视图。
# 文章模型类
class Article(models.Model):
# 作者
author = models.ForeignKey(User, on_delete=models.CASCADE)
# 标题图
avatar = models.ImageField(upload_to='article/%Y%m%d/', blank=True)
# 标题
title = models.CharField(max_length=20, blank=True)
# 分类
# blank表示填写表单时不能为空 null表示数据库不能为空 related_name用于外键反向查询
category = models.ForeignKey(ArticleCategory, null=True, blank=True, on_delete=models.CASCADE,
related_name='article')
# 标签
tags = models.CharField(max_length=20, blank=True)
# 摘要信息
summary = models.CharField(max_length=200, null=False, blank=False)
# 文章正文
content = models.TextField()
# 浏览量
total_views = models.PositiveIntegerField(default=0)
# 评论量
comments = models.PositiveIntegerField(default=0)
# 文章的创建时间
created = models.DateTimeField(default=timezone.now)
# 文章的修改时间
updated = models.DateTimeField(auto_now=True)
# 修改表名以及admin展示的配置信息
class Meta:
db_table = 'tb_article'
ordering = ('-created',)
verbose_name = '文章管理'
verbose_name_plural = verbose_name
def __str__(self):
return self.title
生成迁移文件。
python manage.py makemigrations
python manage.py migrate
在home.views.py中编写视图。
# 写博客视图 登录用户才可以访问视图
class WriteBlogView(LoginRequiredMixin, View):
def get(self, request):
# 查询所有分类模型
categories = ArticleCategory.objects.all()
context = {
'categories': categories,
}
return render(request, 'write_blog.html', context=context)
def post(self, request):
# 接收数据
avatar=request.FILES.get('avatar')
title = request.POST.get('title')
category_id = request.POST.get('category')
tags = request.POST.get('tags')
summary = request.POST.get('sumary')
content = request.POST.get('content')
user = request.user
# 验证数据
if not all([avatar,title,category_id,summary,content]):
return HttpResponseBadRequest('参数不全')
try:
category=ArticleCategory.objects.get(id=category_id)
except ArticleCategory.DoesNotExist:
return HttpResponseBadRequest('没有此分类')
# 数据入库
try:
article=Article.objects.create(author=user,avatar=avatar,category=category,tags=tags,summary=summary,content=content)
except Exception as e:
logger.error(e)
return HttpResponseBadRequest('发布失败,请稍后再试!')
# 跳转页面
return redirect(reverse('home:index'))
在home.views.py中编写视图。
```
class IndexView(View):
def get(self, request):
# 获取所有分类信息
categories = ArticleCategory.objects.all()
# 接受用户点击的分类id
cat_id = request.GET.get('cat_id', 1)
# 根据分类id进行分类的查询
try:
category = ArticleCategory.objects.get(id=cat_id)
except ArticleCategory.DoesNotExist:
return HttpResponseNotFound('没有此分类')
# 组织数据传递给模板
context = {
'categories': categories,
'category': category,
}
return render(request, 'index.html', context=context)
```
在home.views.py中编写视图。
class IndexView(View):
def get(self, request):
# 获取所有分类信息
categories = ArticleCategory.objects.all()
# 接受用户点击的分类id
cat_id = request.GET.get('cat_id', 1)
# 根据分类id进行分类的查询
try:
category = ArticleCategory.objects.get(id=cat_id)
except ArticleCategory.DoesNotExist:
return HttpResponseNotFound('没有此分类')
# 获取分页参数
page_num = request.GET.get('page_num', 1)
page_size = request.GET.get('page_size', 10)
# 根据分类信息查询文章数据
articles = Article.objects.filter(category=category)
# 创建分页器
paginator = Paginator(articles, per_page=page_size)
# 进行分页处理
try:
page_articles = paginator.page(page_num)
except EmptyPage:
return HttpResponseNotFound('empty page')
# 总页数
total_page = paginator.num_pages
# 组织数据传递给模板
context = {
'categories': categories,
'category': category,
'articles': articles,
'page_size': page_size,
'total_page': total_page,
'page_num': page_num,
}
return render(request, 'index.html', context=context)
在index.html中编写渲染接口。
{% for article in articles %}
{{article.category.title}}
{{article.tags}}
{{article.title}}
{{article.summary}}
{{article.total_views}}
{{article.comments}}
{{article.created|date}}
{% endfor %}
在home.views.py中编写视图。
# 详情页面视图
class DetailView(View):
def get(self, request):
return render(request, "detail.html")
在home.urls.py中编写路由。
path('detail/', DetailView.as_view(), name='detail'),
将detail.html放到templates文件夹,并且更改detail.html中的资源加载方式。
在home.views.py中编写视图。
# 详情页面视图
class DetailView(View):
def get(self, request):
# 接受文章id信息
id = request.GET.get('id')
# 根据文章id进行文章数据的查询
try:
article = Article.objects.get(id=id)
except Article.DoesNotExist:
pass
# 查询分类数据
categories = ArticleCategory.objects.all()
# 组织模板数据
context = {
'categories': categories,
'article': article,
'category': article.category,
}
return render(request, "detail.html", context=context)
在detail.html中编写渲染接口。
{{article.title}}
作者:{{article.author.username}}
浏览:{{article.total_views}}
{{article.content|safe}}
错误的页面
- 将404.html放到templates文件夹,并且修改资源加载路径。
- 在home.views.py中编写视图。
except Article.DoesNotExist:
return render(request, '404.html')
- 在浏览器测试一个不存在的id。
http://127.0.0.1:8000/detail/?id=30
文章的推荐
-
在home.views.py中编写视图(文章点击一次,访问量加一)。
# 详情页面视图
class DetailView(View):
def get(self, request):
# 接受文章id信息
id = request.GET.get('id')
# 根据文章id进行文章数据的查询
try:
article = Article.objects.get(id=id)
except Article.DoesNotExist:
return render(request, '404.html')
else:
# 让浏览量加一
article.total_views += 1
article.save()
# 查询分类数据
categories = ArticleCategory.objects.all()
# 查询浏览量前十的文章数据
hot_articles = Article.objects.order_by('-total_views')[:9]
# 组织模板数据
context = {
'categories': categories,
'article': article,
'category': article.category,
'hot_articles': hot_articles,
}
return render(request, "detail.html", context=context)
-
在detail.html中编写渲染接口。
评论模型类
-
在home.models.py中编写评论模型类。
# 评论模型类
class Comment(models.Model):
# 评论内容
content = models.TextField()
# 评论文章
article = models.ForeignKey(Article, on_delete=models.SET_NULL, null=True)
# 评论用户
user = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)
# 评论时间
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.article.title
# 修改表名以及admin展示的配置信息
class Meta:
db_table = 'tb_comment'
verbose_name = '评论管理'
verbose_name_plural = verbose_name
-
执行迁移数据库文件。
python manage.py makemigrations
python manage.py migrate
-
在浏览器端测试。
评论的发布
-
在home.views.py中编写视图。
# 详情页面视图
class DetailView(View):
def get(self, request):
# 接受文章id信息
id = request.GET.get('id')
# 根据文章id进行文章数据的查询
try:
article = Article.objects.get(id=id)
except Article.DoesNotExist:
return render(request, '404.html')
else:
# 让浏览量加一
article.total_views += 1
article.save()
# 查询分类数据
categories = ArticleCategory.objects.all()
# 查询浏览量前十的文章数据
hot_articles = Article.objects.order_by('-total_views')[:9]
# 组织模板数据
context = {
'categories': categories,
'article': article,
'category': article.category,
'hot_articles': hot_articles,
}
return render(request, "detail.html", context=context)
def post(self, request):
# 接受用户信息
user = request.user
# 判断用户是否登录
if user and user.is_authenticated:
# 登录用户则可以接收form数据
# 文章的id
id = request.POST.get('id')
# 评论的内容
content = request.POST.get('content')
# 文章是否存在
try:
article = Article.objects.get(id=id)
except Article.DoesNotExist:
return HttpResponseNotFound('没有此文章')
# 保存评论数据
Comment.objects.create(content=content, article=article, user=user)
# 修改文章的评论数据
article.comments += 1
article.save()
# 刷新当前页面
path = reverse('home:detail') + '?id={}'.format(article.id)
return redirect(path)
else:
# 未登录用户则跳转到登录页面
return redirect(reverse('user:login'))
-
在detail.html中编写页面。
{% csrf_token %}
评论的显示
-
在home.views.py中编写视图。
# 详情页面视图
class DetailView(View):
def get(self, request):
# 接受文章id信息
id = request.GET.get('id')
# 根据文章id进行文章数据的查询
try:
article = Article.objects.get(id=id)
except Article.DoesNotExist:
return render(request, '404.html')
else:
# 让浏览量加一
article.total_views += 1
article.save()
# 查询分类数据
categories = ArticleCategory.objects.all()
# 查询浏览量前十的文章数据
hot_articles = Article.objects.order_by('-total_views')[:9]
# 获取分页请求参数
page_num = request.GET.get('page_num', 1)
page_size = request.GET.get('page_size', 10)
# 根据文章信息查询评论数据
comments = Comment.objects.filter(article=article).order_by('-created')
# 获取评论总数
total_count = comments.count()
# 创建分页器
paginator = Paginator(comments, page_size)
# 进行分页处理
try:
page_comments = paginator.page(page_num)
except EmptyPage:
return HttpResponseNotFound('empty page')
# 总页数
total_page = paginator.num_pages
# 组织模板数据
context = {
'categories': categories,
'article': article,
'category': article.category,
'hot_articles': hot_articles,
'total_count': total_count,
'comments': page_comments,
'page_size': page_size,
'total_page': total_page,
'page_num': page_num,
}
return render(request, "detail.html", context=context)
def post(self, request):
# 接受用户信息
user = request.user
# 判断用户是否登录
if user and user.is_authenticated:
# 登录用户则可以接收form数据
# 文章的id
id = request.POST.get('id')
# 评论的内容
content = request.POST.get('content')
# 文章是否存在
try:
article = Article.objects.get(id=id)
except Article.DoesNotExist:
return HttpResponseNotFound('没有此文章')
# 保存评论数据
Comment.objects.create(content=content, article=article, user=user)
# 修改文章的评论数据
article.comments += 1
article.save()
# 刷新当前页面
path = reverse('home:detail') + '?id={}'.format(article.id)
return redirect(path)
else:
# 未登录用户则跳转到登录页面
return redirect(reverse('user:login'))
-
在detail.html中编写页面。
共有{{total_count}}条评论
{% for comment in comments %}
{{comment.user.username}} {{comment.created|date:'Y-m-d H:i'}}
{{comment.content|safe}}
{% endfor %}
大功告成!
2022年3月14日 王晓曼!
等到毕业了我再发这篇文章!