创建文章模块
创建文章app
在虚拟环境中,apps路径下使用如下代码:
# 进入虚拟环境
workon wsl
# 进入要创建app的路径下
cd blog/blog/apps
# 创建app
python ../../manage.py startapp articles
将articles注册进配置文件dev.py中的INSTALLED_APPS
INSTALLED_APPS = [
...
'articles',
]
创建数据模型,在blog/blog/apps/articles/models.py文件中添加如下代码
from django.db import models
from user.models import BaseModel
from user.models import User
# 创建数据模型
# 博客文章数据模型 应该有哪些字段?
#
class Articles(BaseModel): # BaseModel
# 文章正文内容 TextField
body = models.TextField(verbose_name='正文')
# 文章标题 CharField
title = models.CharField(max_length=100,verbose_name='标题')
# 文章封面图片 ImageField
avatar = models.ImageField(upload_to='articles/%Y%m%d/',verbose_name='文章封面')
# 文章摘要 CharField
desc =models.CharField(max_length=255,verbose_name='文章摘要')
# 文章的浏览量 IntegerField
read_num = models.IntegerField(verbose_name='文章浏览量',default=0)
# 文章的作者 外键 文章是多的 用户是一的 外键字段写在哪一方? 多的这一方
author = models.ForeignKey(User,on_delete=models.CASCADE)
# 文章的专栏 CharField 外键字段 专栏表 一对多 多
column = models.ForeignKey('Column',on_delete=models.CASCADE)
class Meta:
db_table = 'blog_articles'
verbose_name = '文章'
verbose_name_plural = verbose_name
class Column(BaseModel):
# 专栏的名字
title = models.CharField(max_length=100, verbose_name='专栏标题')
class Meta:
db_table = 'blog_column'
verbose_name = '专栏'
verbose_name_plural = verbose_name
def __str__(self):
# 打印一个对象的时候
return self.title
执行数据库迁移,生成对应的数据表(在虚拟环境中,manage.py文件下)
python manage.py makemigrations
python manage.py migrate
将文章模块中的数据模型加入后台管理
在admin.py文件中添加如下代码:
from django.contrib import admin
from articles.models import Articles,Column
# Register your models here.
class ArticlesAdmin(admin.ModelAdmin):
list_display = ['title','author','create_time']
# 想通过作者来搜索 ==》 通过作者的名字来搜索 ===》
search_fields = ['title','author__username']
class ColumnAdmin(admin.ModelAdmin):
list_display = ['title','create_time']
# 想通过作者来搜索 ==》 通过作者的名字来搜索 ===》
search_fields = ['title']
# 将原来的数据模型和ModelAdmin结合起来
admin.site.register(Articles,ArticlesAdmin)
admin.site.register(Column,ColumnAdmin)
在apps.py文件中添加如下代码:
from django.apps import AppConfig
class ArticlesConfig(AppConfig):
name = 'articles'
verbose_name = '文章管理'
在__init__.py文件中添加如下代码:
default_app_config = 'articles.apps.ArticlesConfig'
实现创建文章
写路由(总路由)
from django.contrib import admin
from django.urls import path,include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
# 进行路由分发
path('user/',include('user.urls')),
path('password-reset',include('password_reset.urls')),
path('',include('articles.urls')),
]+static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
在articles文件夹下创建urls.py文件,添加如下代码,用作路由分发:
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
# 创建文章
path('create',views.Create.as_view(),name='create'),
path('index',views.index,name='index'),
]
写视图(view.py)
from django.http import HttpResponse
from django.shortcuts import render,redirect,reverse
from user.views import LoginRequired
from articles.models import Column,Articles
# 继承LoginRequired 只有用户登录了以后才能访问
class Create(LoginRequired):
# 展示创建文章的html页面
def get(self,requests):
# 先查出来所有的专栏,展示在html页面中,让用户去勾选
columns = Column.objects.all()
return render(requests,'articles/create.html',locals())
# 实现创建文章的功能逻辑
def post(self,requests):
# 接受请求数据
title = requests.POST.get('title')
desc = requests.POST.get('desc')
body = requests.POST.get('body')
column = requests.POST.get('column')
# 用户 就是当前登录的用户
author = requests.user
# 封面图 文件是通过FILES接受的
avatar = requests.FILES.get('avatar')
# 数据入库
try:
Articles.objects.create(author=author,title=title,body=body,desc=desc,avatar=avatar,column_id=column)
# 返回结果
return redirect(reverse('articles:index'))
except Exception as err:
return render(requests,'articles/create.html',{'errmsg':'请选择专栏'})
def index(request):
return HttpResponse('首页')
在templates/目录下创建articles/create.html,并添加如下代码:
{% extends 'base.html' %}
{% block title %}创建文章{% endblock title %}
{% block content %}
<div class="container">
<div class="row">
<div class="clo-12">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<!--文章标题 -->
<div class="form-group">
<label for="title">文章标签</label>
<input type="text" class="form-control" id="title" name="title">
</div>
<!--文章专栏 -->
<div class="form-group">
<label for="column">文章专栏</label>
<select name="column" id="column" class="form-control">
<option value="none">请选择专栏</option>
{% for column in columns %}
<option value="{{ column.id }}">{{ column.title }}</option>
{% endfor %}
</select>
</div>
<!--文章摘要 -->
<div class="form-group">
<label for="desc">文章摘要</label>
<input type="text" class="form-control" id="desc" name="desc">
</div>
<!--文章图片 -->
<div class="form-group">
<label for="avatar">文章封面</label>
<input type="file" class="form-control" id="avatar" name="avatar">
</div>
<!--文章正文 -->
<div class="form-group">
<label for="body">文章正文</label>
<textarea class="form-control" name="body" id="body" cols="30" rows="10"></textarea>
</div>
<!--提交按钮 -->
<button type="submit" class="btn btn-success">完成</button>
</form>
</div>
</div>
</div>
<!--展示错误信息 -->
{% if errmsg %}
<div class="alert alert-danger alert-dismissible">
<strong>错误!</strong>{{ errmsg }}
</div>
{% endif %}
{% endblock content %}
实现修改文章
写视图(view.py)
class Update(LoginRequired):
# 展示创建文章的html页面
def get(self,requests,id):
# 先查出来要修改的文章 展示在html中
article = get_object_or_404(Articles,pk=id)
# 先查出来所有的专栏,展示在html页面中,让用户去勾选
columns = Column.objects.all()
return render(requests,'articles/update.html',locals())
# 实现创建文章的功能逻辑
def post(self,requests,id):
# 对指定的文章进行修改
# 1. 先查出文章
article = get_object_or_404(Articles, pk=id)
# 2. 判断文章的作者是不是登录的用户,是则能修改,不是就不能修改
if requests.user == article.author:
# 接受数据
article.title = requests.POST.get('title')
article.desc = requests.POST.get('desc')
article.body = requests.POST.get('body')
column = requests.POST.get('column')
avatar = requests.FILES.get('avatar')
if avatar:
article.avatar = avatar
# 专栏是外键字段
article.column =get_object_or_404(Column,pk=column)
# 数据入库
article.save()
# 返回结果
return redirect(reverse('articles:index'))
return HttpResponse('不能修改其他作者的文章')
写路由(articles/urls.py)
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
# 创建文章
path('create',views.Create.as_view(),name='create'),
path('update/<int:id>',views.Update.as_view(),name='update'),
path('index',views.index,name='index'),
]
在templates/articles/文件夹下创建update.html文件,添加如下代码:
{% extends "base.html" %}
{% block title %} 更新文章 {% endblock title %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-12">
<br>
<form method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label for="title">文章标题</label>
<!-- 在 value 属性中指定文本框的初始值为旧的内容,即 article 对象中的 title 字段 -->
<input type="text" class="form-control" id="title" name="title" value="{{ article.title }}">
</div>
<!-- 文章栏目 -->
<div class="form-group">
<label for="column">栏目</label>
<select class="form-control" id="column" name="column">
<option value="none">请选择栏目..</option>
{% for column in columns %}
<option value="{{ column.id }}"
{% if column.id == article.column.id %}
selected
{% endif %}
>
{{ column }}
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<!-- avatar -->
<label for="avatar">文章图片</label>
<!-- 有封面则展示封面 -->
{% if article.avatar %}
<img src="{{ article.avatar.url }}" style="max-width: 20%; border-radius: 15%;" class="col-md-4">
<input type="file" class="form-control-file" name="avatar" id="avatar">
{% else %}
<h5 class="col-md-4">请上传文章图片</h5>
<input type="file" class="form-control-file" name="avatar" id="avatar">
{% endif %}
<!-- 有头像则展示头像 -->
</div>
<!-- 文章摘要 -->
<div class="form-group">
<!-- 标签 -->
<label for="desc">文章摘要</label>
<!-- 文本框 -->
<input type="text" class="form-control" id="desc" name="desc" value="{{ article.desc }}">
</div>
<div class="form-group">
<label for="body">文章正文</label>
<!-- 文本域不需要 value 属性,直接在标签体中嵌入数据即可 -->
<textarea type="text" class="form-control" id="body" name="body" rows="12">{{ article.body }}</textarea>
</div>
<button type="submit" class="btn btn-primary">完成</button>
</form>
</div>
</div>
</div>
{% endblock content %}
实现删除文章
写视图(view.py)
def delete(requests,id):
# 1. 先根据ID查出文章
article = get_object_or_404(Articles, pk=id)
# 2. 判断当前登录用户是不是作者
if requests.user == article.author:
# 3. 如果是作者就删除文章 数据入库
article.delete()
return redirect(reverse('articles:index'))
# 4. 如果不是作者,就提示不能删
return HttpResponse('不能删除其他作者的文章')
写路由(urls.py)
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
# 创建文章
path('create',views.Create.as_view(),name='create'),
path('update/<int:id>',views.Update.as_view(),name='update'),
path('delete/<int:id>',views.delete,name='delete'),
path('index',views.index,name='index'),
]
展示所有文章
基础版本(无分页)
写视图(view.py)
def index(request):
# 获取所有的文章,再展示在html页面中
articles = Articles.objects.all()
return render(request,'articles/index.html',locals())
写路由(总路由)
from django.urls import path,include
from django.conf.urls.static import static
from django.conf import settings
from articles.views import index
urlpatterns = [
path('',index,name='index'),
path('admin/', admin.site.urls),
# 进行路由分发
path('user/',include('user.urls')),
path('password-reset',include('password_reset.urls')),
path('',include('articles.urls')),
]+static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
写路由(articles/urls.py)
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
# 创建文章
path('create',views.Create.as_view(),name='create'),
path('update/<int:id>',views.Update.as_view(),name='update'),
path('delete/<int:id>',views.delete,name='delete'),
# 展示所有的博文
path('index',views.index,name='index'),
]
在templates/articles/文件夹下创建index.html文件并添加如下代码:
{% extends "base.html" %}
{% block title %} 博客正文 {% endblock title %}
{% block content %}
<div class="container">
<div class="col-12">
<div class="row mt-2">
{% for article in articles %}
<!-- 封面图-->
{% if article.avatar %}
<div class="col-3">
<img src="{{ article.avatar.url }}" alt="avatar" style="max-width: 100%;border-radius:20px ">
</div>
{% endif %}
<!-- 文章内容-->
<div class="col">
<!-- 文章栏目-->
<button type="button" class="btn btn-sm mb-2 btn-success">{{ article.column }}</button>
<!-- 文章标题-->
<h4>
<b>
<a href="{% url 'articles:detail' article.id %}">{{ article.title }}</a>
</b>
</h4>
<!-- 文章摘要-->
<div>
<p style="color: gray;">
{% if article.desc %}
{{ article.desc }}
{% else %}
还没有摘要
{% endif %}
</p>
</div>
<!-- 其他信息-->
<p>
<span style="color: green">
作者:{{ article.author }}
</span>
<span style="color: green">
浏览量:{{ article.read_num }}
</span>
<span style="color: green">
发布时间:{{ article.create_time|date:'Y-m-d' }}
</span>
<span style="color: green">
更新时间:{{ article.update_time|date:'Y-m-d' }}
</span>
</p>
</div>
<hr style="width: 100%;">
{% endfor %}
</div>
</div>
</div>
{% endblock content %}
展示文章详情页
写视图(view.py)
# 查看文章详情
def detail(request,id):
# 根据文章的ID 查出对应的文章展示在html页面中
article = get_object_or_404(Articles, pk=id)
# 没查看一次文章 就增加一次浏览量
article.read_num += 1
article.save()
return render(request,'articles/detail.html',locals())
写视图(urls.py)
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
# 创建文章
path('create',views.Create.as_view(),name='create'),
path('update/<int:id>',views.Update.as_view(),name='update'),
path('delete/<int:id>',views.delete,name='delete'),
# 展示所有的博文
path('index',views.index,name='index'),
# 查看博文详情
path('detail/<int:id>',views.detail,name='detail'),
]
在templates/articles/文件夹下创建detail.html文件:
{% extends 'base.html' %}
{% block title %}文章详情{% endblock title %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-9">
<!--标题和作者-->
<h1 class="mt-4 mb-4">{{ article.title }}</h1>
<div class="alert alert-success">
作者:{{ article.author }}
创建时间:{{ article.create_time|date:'Y-m-d' }}
文章浏览量:{{ article.read_num }}
<!--如果当前登录的用户就是作者,则展示删除文章和修改文章-->
{% if request.user == article.author %}
<a href="#" onclick="confirm_delete()">删除文章</a>
<a href="{% url 'articles:update' article.id %}">修改文章</a>
{% endif %}
<!--文章的专栏-->
<button type="button" class="btn btn-success btn-sm mb-2">{{ article.column }}</button>
</div>
<!--文章的正文-->
<div class="col-12">
<p>{{ article.body }}</p>
</div>
</div>
</div>
</div>
<script>
//删除文章的函数
function confirm_delete(){
// 调用layer弹窗组件
layer.open({
title:"确认删除",
content:'确认删除这篇文章吗?',
yes:function (index,layero){
// 指定前往的url
location.href = '{% url "articles:delete" article.id %}'
},
})
}
</script>
{% endblock content %}