效果预览

文章列表

添加文章

编辑文章|文章详情|删除文章

项目的基本文件

项目的Model

from django.db import models
# 导入富文本编辑器相关的模块
from ckeditor_uploader.fields import RichTextUploadingField
class Category(models.Model):
name = models.CharField(max_length=12,verbose_name='分类名称') def __str__(self):
return self.name class ArticleDetail(models.Model):
# 使用富文本编辑器
content = RichTextUploadingField(verbose_name='文章详情') class Article(models.Model):
title = models.CharField(verbose_name='文章标题',max_length=56)
summary = models.CharField(verbose_name='文章摘要',max_length=256)
create_at = models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
# 相对路径
img = models.ImageField(upload_to='img/article/',verbose_name='显示图片',blank=True,default='img/article/default.jpg') category = models.ForeignKey(to=Category,blank=True,null=True,verbose_name='文章分类')
# 如果设置成null=True,会出现“文章详情有数据但是其中的文章的这个字段为空”的情况
detail = models.OneToOneField(to=ArticleDetail,verbose_name='文章详情',null=True,blank=True)

项目的url配置

项目的url做了“伪html页面配置”~~注意正则匹配的时候把点号用\转义一下~

from django.conf.urls import url

from backend.views import article

urlpatterns = [

    url(r'^article_list\.html/$', article.article_list,name='article_list'),
url(r'article_add\.html/$',article.article_add,name='article_add'),
url(r'article_edit\.html/(?P<pk>\d+)/$',article.article_edit,name='article_edit'),
url(r'article_del\.html/(?P<pk>\d+)/$',article.article_del,name='article_del'),
url(r'article_detail\.html/(?P<pk>\d+)/$',article.article_detail,name='article_detail'), ]

项目的url

项目的视图

# -*- coding:utf-8 -*-
import os
import re
from django.shortcuts import render,redirect
from django.conf import settings from repository import models
from backend import forms def article_list(request):
if request.method == 'GET':
all_articles = models.Article.objects.all()
return render(request,'article/article_list.html',locals()) # 添加文章有一个异常情况:
# 文章详情的content中不输入数据也能通过校验!———— 所以必须判断一下content中是否有输入!
def article_add(request):
# 一个页面加两个form校验表单:文章与文章详情
form_obj = forms.ArticleForm()
detail_form = forms.ArticleDetailForm()
title = '新增文章'
if request.method == 'GET':
return render(request,'article/article_form.html',{'title':title,'form_obj':form_obj,'detail_form':detail_form})
elif request.method == 'POST':
# 这里有个异常情况:文章详情的content中不输入数据也能通过校验!———— 所以必须判断一下content中是否有输入!
if request.POST.get('content'):
# 文章详情
detail_form = forms.ArticleDetailForm(request.POST)
if detail_form.is_valid():
detail_form.save()
# 需要给文章表中加入一个“文章详情”的键值对!~因为前端没有添加文章详情id
# QueryDict需要copy()一下才能修改!
qd = request.POST.copy()
# 注意!键是Article表做外键关联的那个属性值;值是对应的ArticleDetail中的对应的对象的pk值~先用instance取出文章详情的对象
qd['detail'] = detail_form.instance.pk
print(qd)
# 文章表中有数据和文件,文件需要用request.FILES获取
# 注意~这里的数据应该是qd了!!!
form_obj = forms.ArticleForm(data=qd,files=request.FILES)
if form_obj.is_valid():
# print(111111111111111111111111)
form_obj.save()
return redirect('backend:article_list') # 这种情况是:文章详情添加成功但是文章添加失败了~需要把文章详情添加的数据删除了
# 文章与文章详情的数据保持一致
elif detail_form.is_valid() and detail_form.instance:
detail_form.instance.delete()
# print(222222222222222222) return render(request, 'article/article_form.html',
{'title': title, 'form_obj': form_obj, 'detail_form': detail_form})
else:
return render(request, 'article/article_form.html',
{'title': title, 'form_obj': form_obj, 'detail_form': detail_form}) # 编辑文章——如果用户上传了新的图片,应该把之前的图片删掉
def article_edit(request,pk):
# 一个页面显示两个表单
# 文章对象
article_obj = models.Article.objects.filter(pk=pk).first()
# 提前将原文章中的图片对象取出来
img_obj = article_obj.img form_obj = forms.ArticleForm(instance=article_obj)
# 基于对象的跨表查询
detail_form = forms.ArticleDetailForm(instance=article_obj.detail)
title = '编辑文章'
if request.method == 'GET':
return render(request,'article/article_form.html',{'title':title,'form_obj':form_obj,'detail_form':detail_form}) elif request.method == 'POST':
# 文章详情
detail_form =forms.ArticleDetailForm(data=request.POST,instance=article_obj.detail)
if detail_form.is_valid():
detail_form.save()
# 文章的处理
# 需要给文章表中加入一个“文章详情”的键值对!~因为前端没有添加文章详情id
# QueryDict需要copy()一下才能修改!
qd = request.POST.copy()
qd['detail'] =detail_form.instance.pk
# 注意:data应该是qd了!!!~~还得加上article_obj~否则成了添加了!
form_obj = forms.ArticleForm(data=qd,files=request.FILES,instance=article_obj)
if form_obj.is_valid():
# 判断一下新传入的图片对象跟之前的是否相等
# 如果没有传入图片就继续
if not form_obj.cleaned_data.get('img'):
pass
if form_obj.cleaned_data.get('img') != img_obj:
try:
# 将之前的图片删除了
os.remove(img_obj.path)
except Exception as e:
pass
form_obj.save()
return redirect('backend:article_list')
return render(request, 'article/article_form.html',
{'title': title, 'form_obj': form_obj, 'detail_form': detail_form}) # 删除文章~~
# 把Article中的图片以及文章详情的content中的图片同时也删掉~~
def article_del(request,pk):
# 这里注意:一对一的关系属性加在了Article类中
# “级联删除”的功能应该是:删除ArticleDetail表中的数据后Article表中的数据跟着删除
# 但是删除Article中的数据的话ArticleDetail中对应的数据不会跟着删除
# 先找到Article对象,注意删除的是ArticleDetail的对象!
article_obj = models.Article.objects.filter(pk=pk).first()
# 基于对象的跨表查询
detail_obj = article_obj.detail
# 找到图片对象
img_file_obj = article_obj.img
# path方法找到文件的绝对路径
img_file_path = img_file_obj.path content = detail_obj.content
print(content)
# 如果有图片~把文章中的图片也删除了
if 'img' in content:
# 异常情况是:服务器中没有图片文件而数据库中有记录(人为误删导致)
try:
# 匹配多个格式的图片文件~~~注意取消分组优先!!!
result = re.findall(r'src=".+\.(?:jpg|png|jpeg|PNG|JPG|JPEG)"',content)
print(result) #['src="/media/ckeditor/2019/07/23/itachi3.PNG"', 'src="/media/ckeditor/2019/07/23/default.jpg"']
for src in result:
# 去掉前面的前缀(前面那个斜杠也去掉!)以及后面的那个引号
path = src[6:-1]
file_img_path = os.path.join(settings.BASE_DIR,path)
# # 删除这张图片
os.remove(file_img_path)
except Exception as e:
pass # 删除Article中的图片文件
os.remove(img_file_path)
# 删除文章详情————有级联删除~文章表对应的记录也删了(字段是在文章表中定义的)
detail_obj.delete()
return redirect('backend:article_list') # 文章表详情~前端模板中用safe渲染!
def article_detail(request,pk):
article_obj = models.Article.objects.filter(pk=pk).first() title = article_obj.title
content = article_obj.detail.content
return render(request,'article/article_detail.html',{'content':content,'title':title})

项目的视图

项目的模板文件

1、文章列表展示的页面:

{% extends 'layout.html' %}

{# {% get_media_prefix %}方法需要先load static!!!#}
{% load static %} {% block content %} <h2 class="text-danger">文章列表</h2> <a href="{% url 'backend:article_add' %}" class="btn btn-success">添加文章</a>
<table class="table table-condensed table-bordered" style="margin-top: 22px">
<thead>
<tr>
<th>序号</th>
<th>图像</th>
<th>标题</th>
<th>分类</th>
<th>操作</th>
</tr>
</thead> <tbody>
{% for article in all_articles %}
<tr>
<td>{{ forloop.counter }}</td> {# 不要用 {{ article.img/url }} 如果没有传图片的话会报错! #}
<td><img src="{% get_media_prefix %}{{ article.img }}" alt="" width="52px" height="52px"></td>
<td>{{ article.title }}</td>
<td>{{ article.category }}</td>
<td class="tpl-table-black-operation">
<a href="{% url 'backend:article_edit' article.pk %}">
<i class="am-icon-pencil"></i> 编辑
</a>
<a href="{% url 'backend:article_detail' article.pk %}"
class="tpl-table-black-operation">
<i class="am-icon-random"></i> 文章详情
</a>
<a href="{% url 'backend:article_del' article.pk %}"
class="tpl-table-black-operation-del">
<i class="am-icon-trash"></i> 删除
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table> {% endblock content %}

article_list.html

2、用于校验的页面:

一个页面放了2个校验的表单,一个是文章的,一个是文章详情的

{% extends 'layout.html' %}

{% load staticfiles %}

{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ title }}</h3>
</div> <div class="panel-body">
<form action="" class="form-horizontal" method="post" enctype="multipart/form-data" novalidate>
{% csrf_token %} {# 一个页面显示2个校验表单 #} {# 添加文章的表单 #}
{% for field in form_obj %}
{# 注意这里不显示Article表的detail属性 #}
{# field.name 标签对应的name属性的值 就是数据库中的字段的值 #}
{% if field.name != 'detail' %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
<label {% if not field.field.required %} style="color: #777777" {% endif %}
for="{{ field.id_for_label }}"
class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-8">
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
</div>
{% endif %}
{% endfor %} {# 添加文章详情的表单 #}
{% for field in detail_form %} <div class="form-group {% if field.errors %}has-error{% endif %}"> <div class="col-lg-offset-1 col-sm-10">
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %} <button class="btn btn-success pull-right" style="margin-right: 244px">确认</button>
</form>
<div class="text-danger text-center"> {{ form_obj.non_field_errors.0 }} </div>
<div class="text-danger text-center"> {{ detail_form.non_field_errors.0 }} </div> </div> </div> {% endblock content %} {% block js %}
<script src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
<script src="{% static 'ckeditor/ckeditor-init.js' %}"></script>
{% endblock js %}

article_form.html

3、文章详情的页面:

{% extends 'layout.html' %}

{% block content %}

    <h2 class="text-danger">{{ title }}</h2>

    <div>
{# 加上safe #}
{{ content|safe }}
</div> {% endblock content %}

article_detail.html

项目的forms校验的类

# -*- coding:utf-8 -*-
from django import forms
# 存放Model的应用是repository
from repository import models class ArticleForm(forms.ModelForm):
class Meta:
model = models.Article
fields = '__all__' def __init__(self,*args,**kwargs):
super(ArticleForm, self).__init__(*args,**kwargs) for field in self.fields.values():
field.widget.attrs['class'] = 'form-control' class ArticleDetailForm(forms.ModelForm):
class Meta:
model = models.ArticleDetail
fields = '__all__'

settings文件相关的配置

STATIC_URL = '/static/'
# 不写dirs,默认会去从名为“static”的文件夹中去找静态文件
# STATICFILES_DIRS = [
# os.path.join(BASE_DIR, 'web/static'),
# ] ### 媒体配置
MEDIA_URL ='/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media') ### 富文本编辑器的配置
CKEDITOR_UPLOAD_PATH = 'ckeditor/'

media的配置及访问说明

本项目主要在两个地方使用到了media的配置:一个是在添加文章时选择图片那里;另外一个地方是文章列表那里显示用户上传的图像。

之前的博客

我之前有个博客也介绍了Django的media的相关配置及使用:https://www.cnblogs.com/paulwhw/p/9551151.html

本项目的具体配置

创建文件夹以及在settings中的配置

Django的媒体配置主要是对用户上传的文件进行统一的 管理。约定俗成的,我们习惯将媒体配置的文件夹命名为“media”,配置的路径的名字也命名为media

(1)首先,在项目的跟目录下新建一个名为media的目录;

(2)然后,在项目的settings中进行如下配置:

### 媒体配置
MEDIA_URL ='/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')

项目“总路由”里的配置

这里建议大家不要在分发的路由里配置~

from django.conf.urls import url,include
from django.contrib import admin
from django.conf import settings
from django.views.static import serve
urlpatterns = [ # 媒体配置
# media配置——配合settings中的MEDIA_ROOT的配置,就可以在浏览器的地址栏访问media文件夹及里面的文件了
url(r'^media/(?P<path>.*)', serve, {'document_root'
: settings.MEDIA_ROOT}), ]

项目Model的配置

项目的Article这个Model里的img属性对应的是用户上传的文件,注意用的是ImageField:

class Article(models.Model):
title = models.CharField(verbose_name='文章标题',max_length=56)
summary = models.CharField(verbose_name='文章摘要',max_length=256)
create_at = models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
# upload_to用相对路径,不能用/img/article/这样的绝对路径指定!
img = models.ImageField(upload_to='img/article/',verbose_name='显示图片',blank=True,default='img/article/default.jpg'
)

category = models.ForeignKey(to=Category,blank=True,null=True,verbose_name='文章分类')
# 如果设置成null=True,会出现“文章详情有数据但是其中的文章的这个字段为空”的情况
detail = models.OneToOneField(to=ArticleDetail,verbose_name='文章详情',null=True,blank=True)

这里需要注意:media文件夹是我们上传文件的“根目录”,如果我们想再为这个“根目录”指定“子目录”的话需要通过参数upload_to去指定,也就是说,我们上传的文件会保存在media/img/article/目录下,后面的参数default表示默认图像————比如说用户不指定图像的时候就用default参数指定的图片。以后用户上传的图片会存放在服务器的media/img/article/这个目录下。

form表单校验时的注意事项

添加与编辑功能用到了ModelForm的校验。需要特别注意了:由于我们这里有文件的上传,需要把form表单的enctype改成multipart/form-data

<form action="" class="form-horizontal" method="post" enctype="multipart/form-data" novalidate>
{% csrf_token %}
{# 一个页面显示2个校验表单 #}
{# 添加文章的表单 #}
{% for field in form_obj %}
{# 注意这里不显示Article表的detail属性 #}
{# field.name 标签对应的name属性的值 就是数据库中的字段的值 #}
{% if field.name != 'detail' %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
<label {% if not field.field.required %} style="color: #777777" {% endif %}
for="{{ field.id_for_label }}"
class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-8">
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
</div>
{% endif %}
{% endfor %} {# 添加文章详情的表单 #}
{% for field in detail_form %} <div class="form-group {% if field.errors %}has-error{% endif %}"> <div class="col-lg-offset-1 col-sm-10">
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %} <button class="btn btn-success pull-right" style="margin-right: 244px">确认</button>
</form>

视图函数处理时的注意事项

视图函数中接收文件类型的数据要用request.FILES!request.POST中只有用户输入的数据!

注意一下form_obj的写法(这里只截取添加功能的片段代码~~编辑的话还有一个instance=的关键字参数)

 # 需要给文章表中加入一个“文章详情”的键值对!~因为前端没有添加文章详情id
# QueryDict需要copy()一下才能修改!
qd = request.POST.copy()
# 注意!键是Article表做外键关联的那个属性值;值是对应的ArticleDetail中的对应的对象的pk值~先用instance取出文章详情的对象
qd['detail'] = detail_form.instance.pk
print(qd)
# 文章表中有数据和文件,文件需要用request.FILES获取
# 注意~这里用户输入的数据应该是qd了!!!
form_obj = forms.ArticleForm(data=qd,files=request.FILES)

图片文件的访问

关于文件的访问我上面的那篇博客介绍了一种通过“注册中间件”的方式。

下面介绍一下我在本项目中的“文章列表”实现图片展示的方法:

(1)首先,在模板中load static

{# {% get_media_prefix %}方法需要先load static!!!#}
{% load static %}

(2)然后,在进行for循环遍历的时候通过 get_media_prefix 方法加上图片的相对路径去展示图片

{% for article in all_articles %}
<tr>
<td>{{ forloop.counter }}</td>

       {# 谨慎使用用 {{ article.img.url }} 如果没有传图片的话会报错!但是在Model中给每个用户设置一个默认的图像也是可以的! #}
<td><img src="{% get_media_prefix %}{{ article.img }}" alt="" width="52px" height="52px"></td>


          <td>{{ article.title }}</td>
<td>{{ article.category }}</td>
<td class="tpl-table-black-operation">
<a href="{% url 'backend:article_edit' article.pk %}">
<i class="am-icon-pencil"></i> 编辑
</a>
<a href="{% url 'backend:article_detail' article.pk %}"
class="tpl-table-black-operation">
<i class="am-icon-random"></i> 文章详情
</a>
<a href="{% url 'backend:article_del' article.pk %}"
class="tpl-table-black-operation-del">
<i class="am-icon-trash"></i> 删除
</a>
</td>
</tr>
{% endfor %}

富文本编辑器ckeditor的使用说明

下载

pip install django-ckeditor

在settings中注册

INSTALLED_APPS = [

    'ckeditor',
'ckeditor_uploader',
]

在Model中使用字段

from ckeditor_uploader.fields import RichTextUploadingField

class ArticleDetail(models.Model):
content = RichTextUploadingField(verbose_name='文章详情')

在项目的“总路由”中配置

from ckeditor_uploader import views

urlpatterns = [

    # 上传文件
url(r'^ckeditor/upload/', views.upload),
url(r'^ckeditor/', include('ckeditor_uploader.urls')), ]

数据库迁移

如果你是在中途把富文本编辑器加进来的话,记得要进行数据库的迁移!

模板中的配置

注意在模板中的配置有一个坑!

看下面的代码可知,我们是从自己配置的static文件存放的目录下再去找:ckeditor/ckeditor/ckeditor.js文件与ckeditor/ckeditor-init.js文件。

但是,这两个文件其实是在我们的解释器下面的site-packages文件夹中的!而且里面存放这两个文件的根目录叫static!

如果你在settings中配置了STATICFILES_DIRS,并且存放静态文件的目录改成了staticfiles(反正不叫static),那么是无法找到这两个文件的!

对于这种情况,我们可以在site-packages中找到这两个文件,然后把他两存放在我们自己的staticfiles文件夹中就好了!

{{ field }}   富文本编辑框的字段

<script src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
<script src="{% static 'ckeditor/ckeditor-init.js' %}"></script>

其他细节的说明

Article表的detail字段设置成可以为空的一个坑

Model是这样设计的

class ArticleDetail(models.Model):
# 使用富文本编辑器
content = RichTextUploadingField(verbose_name='文章详情') class Article(models.Model):
title = models.CharField(verbose_name='文章标题',max_length=56)
summary = models.CharField(verbose_name='文章摘要',max_length=256)
create_at = models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
# 相对路径
img = models.ImageField(upload_to='img/article/',verbose_name='显示图片',blank=True,default='img/article/default.jpg') category = models.ForeignKey(to=Category,blank=True,null=True,verbose_name='文章分类') # 如果设置成null=True,会出现“文章详情有数据但是其中的文章的这个字段为空”的情况
detail = models.OneToOneField(to=ArticleDetail,verbose_name='文章详情',null=True,blank=True)

ModelForm中对所有的字段都进行校验

# -*- coding:utf-8 -*-
from django import forms from repository import models class ArticleForm(forms.ModelForm):
class Meta:
model = models.Article
fields = '__all__' def __init__(self,*args,**kwargs):
super(ArticleForm, self).__init__(*args,**kwargs) for field in self.fields.values():
field.widget.attrs['class'] = 'form-control' class ArticleDetailForm(forms.ModelForm):
class Meta:
model = models.ArticleDetail
fields = '__all__'

文章与文章详情的ModelForm校验

模板中用一个form表单对两个Model进行了校验

这里需要特别说明一下:Article表中的detail属性存放的是ArticleDetail表中的每项记录的id!

我在这里没有把Article的detail显示出来!

因为前面设置了null=True与blank=True,所以这里可以通过校验。

所以detail_id往后台传的其实是一个空的值!

然后我在后台进行数据处理的时候往按照ArticleDetail的内容往request.POST中添加了detail的键值对——由于QueryDict不能直接修改,所以需要先copy以后再往里面添加数据!

{% extends 'layout.html' %}

{% load staticfiles %}

{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ title }}</h3>
</div> <div class="panel-body">
<form action="" class="form-horizontal" method="post" enctype="multipart/form-data" novalidate>
{% csrf_token %}
{# 一个页面显示2个校验表单 #}
{# 添加文章的表单 #}
{% for field in form_obj %}
{# 注意这里不显示Article表的detail属性 #}
{# field.name 标签对应的name属性的值 就是数据库中的字段的值 #}
{% if field.name != 'detail' %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
<label {% if not field.field.required %} style="color: #777777" {% endif %}
for="{{ field.id_for_label }}"
class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-8">
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
</div>
{% endif %}
{% endfor %} {# 添加文章详情的表单 #}
{% for field in detail_form %} <div class="form-group {% if field.errors %}has-error{% endif %}"> <div class="col-lg-offset-1 col-sm-10">
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %} <button class="btn btn-success pull-right" style="margin-right: 244px">确认</button>
</form>
<div class="text-danger text-center"> {{ form_obj.non_field_errors.0 }} </div>
<div class="text-danger text-center"> {{ detail_form.non_field_errors.0 }} </div> </div> </div> {% endblock content %} {% block js %}
<script src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
<script src="{% static 'ckeditor/ckeditor-init.js' %}"></script>
{% endblock js %}

后台视图函数的处理及这个坑的处理

一个坑

先说遇到的坑:如果文章详情(就是富文本编辑框)什么也不填,往后台还是能传入数据的——此时会遇到这种情况~文章详情没有数据但是文章表中有数据。

为了避免这种情况,我在后台做了一下判断,如果request.POST.get('content')(取到的就是富文本编辑框中的内容)中的值为空的话不让他校验成功~重新返回当前的页面~当然这里做的简单了,聪明的你也许会想到更好的解决方式~这里只是说一下这个问题。

添加与编辑后台视图的处理

添加功能

(1)需要考虑一种情况:文章详情添加成功但是文章添加失败了~~这种情况需要把文章详情添加的数据删除了!

(2)给request.POST中添加键值对的话需要先copy一下!因为它是一个QueryDict类型的数据!

(3)注意form_obj的写法!有文件上传需要加files=request.FILES!

# 添加文章有一个异常情况:
# 文章详情的content中不输入数据也能通过校验!———— 所以必须判断一下content中是否有输入!
def article_add(request):
# 一个页面加两个form校验表单:文章与文章详情
form_obj = forms.ArticleForm()
detail_form = forms.ArticleDetailForm()
title = '新增文章'
if request.method == 'GET':
return render(request,'article/article_form.html',{'title':title,'form_obj':form_obj,'detail_form':detail_form})
elif request.method == 'POST':
# 这里有个异常情况:文章详情的content中不输入数据也能通过校验!———— 所以必须判断一下content中是否有输入!
if request.POST.get('content'):
# 文章详情
detail_form = forms.ArticleDetailForm(request.POST)
if detail_form.is_valid():
detail_form.save()
# 需要给文章表中加入一个“文章详情”的键值对!~因为前端没有添加文章详情id
# QueryDict需要copy()一下才能修改!
qd = request.POST.copy()
# 注意!键是Article表做外键关联的那个属性值;值是对应的ArticleDetail中的对应的对象的pk值~先用instance取出文章详情的对象
qd['detail'] = detail_form.instance.pk
print(qd)
# 文章表中有数据和文件,文件需要用request.FILES获取
# 注意~这里的数据应该是qd了!!!
form_obj = forms.ArticleForm(data=qd,files=request.FILES)
if form_obj.is_valid():
# print(111111111111111111111111)
form_obj.save()
return redirect('backend:article_list') # 这种情况是:文章详情添加成功但是文章添加失败了~需要把文章详情添加的数据删除了
# 文章与文章详情的数据保持一致
elif detail_form.is_valid() and detail_form.instance:
detail_form.instance.delete()
# print(222222222222222222) return render(request, 'article/article_form.html',
{'title': title, 'form_obj': form_obj, 'detail_form': detail_form})
else:
return render(request, 'article/article_form.html',
{'title': title, 'form_obj': form_obj, 'detail_form': detail_form})

编辑功能

(1)如果编辑了新的图片的话,把之前的图片删除了

(2)添加功能的说明(1)不用加了~其他的都跟添加差不多

(3)需要注意!编辑的form_obj一定要加instance=xxx~~老生常谈的问题了!

# 编辑文章——如果用户上传了新的图片,应该把之前的图片删掉
def article_edit(request,pk):
# 一个页面显示两个表单
# 文章对象
article_obj = models.Article.objects.filter(pk=pk).first()
# 提前将原文章中的图片对象取出来
img_obj =
article_obj.img form_obj = forms.ArticleForm(instance=article_obj)
# 基于对象的跨表查询
detail_form = forms.ArticleDetailForm(instance=article_obj.detail)
title = '编辑文章'
if request.method == 'GET':
return render(request,'article/article_form.html',{'title':title,'form_obj':form_obj,'detail_form':detail_form}) elif request.method == 'POST':
# 文章详情
detail_form =forms.ArticleDetailForm(data=request.POST,instance=article_obj.detail)
if detail_form.is_valid():
detail_form.save()
# 文章的处理
# 需要给文章表中加入一个“文章详情”的键值对!~因为前端没有添加文章详情id
# QueryDict需要copy()一下才能修改!
qd = request.POST.copy()
qd['detail'] =detail_form.instance.pk
# 注意:data应该是qd了!!!~~还得加上article_obj~否则成了添加了!
form_obj = forms.ArticleForm(data=qd,files=request.FILES,instance=article_obj)
if form_obj.is_valid():
# 判断一下新传入的图片对象跟之前的是否相等
# 如果没有传入图片就继续
if not form_obj.cleaned_data.get('img'):
pass
if form_obj.cleaned_data.get('img') != img_obj:
try:
# 将之前的图片删除了
os.remove(img_obj.path)
except Exception as e:
pass

form_obj.save()
return redirect('backend:article_list')
return render(request, 'article/article_form.html',
{'title': title, 'form_obj': form_obj, 'detail_form': detail_form})

删除功能注意需要把文章以及文章详情的图片删了 ***

这里用到了正则表达式去匹配文章详情中的图片

# 删除文章~~
# 把Article中的图片以及文章详情的content中的图片同时也删掉~~
def article_del(request,pk):
# 这里注意:一对一的关系属性加在了Article类中
# “级联删除”的功能应该是:删除ArticleDetail表中的数据后Article表中的数据跟着删除
# 但是删除Article中的数据的话ArticleDetail中对应的数据不会跟着删除
# 先找到Article对象,注意删除的是ArticleDetail的对象!
article_obj = models.Article.objects.filter(pk=pk).first()
# 基于对象的跨表查询
detail_obj = article_obj.detail
# 找到图片对象
img_file_obj =
article_obj.img
# path方法找到文件的绝对路径
img_file_path =
img_file_obj.path content = detail_obj.content
print(content)
# 如果有图片~把文章中的图片也删除了
if 'img' in content:
# 异常情况是:服务器中没有图片文件而数据库中有记录(人为误删导致)
try:
# 匹配多个格式的图片文件~~~注意取消分组优先!!!
result = re.findall(r'src=".+\.(?:jpg|png|jpeg|PNG|JPG|JPEG)"',content)
print(result) #['src="/media/ckeditor/2019/07/23/itachi3.PNG"', 'src="/media/ckeditor/2019/07/23/default.jpg"']
for src in result:
# 去掉前面的前缀(前面那个斜杠也去掉!)以及后面的那个引号
path = src[6:-1]
file_img_path = os.path.join(settings.BASE_DIR,path)
# # 删除这张图片
os.remove(file_img_path)
except Exception as e:
pass
# 删除Article中的图片文件
os.remove(img_file_path)
# 删除文章详情————有级联删除~文章表对应的记录也删了(字段是在文章表中定义的)
detail_obj.delete()
return redirect('backend:article_list')

~

Django的media配置与富文本编辑器使用的实例的更多相关文章

  1. Django 插件之 Xadmin实现富文本编辑器

    此文为前一篇文章的续写: Django 插件之 Xadmin Ueditor 介绍 UEditor 是由百度 web 前端研发部开发所见即所得富文本 web 编辑器,具有轻量,可定制,注重用户体验等特 ...

  2. Django学习---py3下的富文本编辑器的使用

    背景说明: Ueditor HTML编辑器是百度开源的HTML编辑器,但是在Python3下调用报错,找不到widgets模块,经查发现,DjangoUeditor是基于Python 2.7的,对Py ...

  3. django项目中使用KindEditor富文本编辑器。

    先从官网下载插件,放在static文件下 前端引入 <script type="text/javascript" src="/static/back/kindedi ...

  4. django项目中使用KindEditor富文本编辑器

    先从官网下载插件,放在static文件下 前端引入 <script type="text/javascript" src="/static/back/kindedi ...

  5. 配置KindEditor富文本编辑器

    第一步:首先我们要到KindEditor官网下载资源包-点击进入官网下载KindEditor资源包 第二部:在下载完了KindEditor的资源包后解压结构如下图所示: 里面包括集中语言的文件上传后台 ...

  6. django配置Ueditor富文本编辑器

    1.https://github.com/twz915/DjangoUeditor3下载包,进入包文件夹,找到DjangoUeditor包拷贝到项目下,和xadmin同级目录 2.找到项目的setti ...

  7. Django Admin后台使用tinymc 富文本编辑器

    1.CDN地址 <script src="//cdn.tinymce.com/4/tinymce.min.js"></script> 2.修改base.ht ...

  8. django2集成DjangoUeditor富文本编辑器

    富文本编辑器,在web开发中可以说是不可缺少的.django并没有自带富文本编辑器,因此我们需要自己集成,在这里推荐大家使用DjangoUeditor,因为DjangoUeditor封装了我们需要的一 ...

  9. MVC 使用 Ueditor富文本编辑器

    一.Ueditor 1.下载Ueditor富文本编辑器 官方下载地址: http://ueditor.baidu.com/website/download.html 建议下载开发版,此处我下载的是 . ...

随机推荐

  1. FastDFS分布式文件服务器

    5.分布式文件服务器FastDFS(阿里巴巴) 5.1什么是FastDFS FastDFS 是用 c 语言编写的一款开源的分布式文件系统.FastDFS 为互联网量身定制,充分考虑了冗余备份.负载均衡 ...

  2. js面向过程-拖拽

    1.步骤分析: 1.1 获取id 1.2 当鼠标点击时执行的js 1.3当鼠标移动时执行的js 1.4当鼠标放开时执行的js 2.代码实现 <!DOCTYPE html> <html ...

  3. oracle给用户赋dblink权限

    create database link 别名(可任意起) connect to 需要连接库的用户名identified by 需要连接库的用户名 using '(DESCRIPTION =(ADDR ...

  4. 18.Linux-CentOS系统根目录空间使用率100%问题?

    问题描述:发现服务器根目录爆满100%? 排查步骤:1.先检查文件索引节点iNode使用率情况,[root@localhost ~]# df -hTi2.查看无用文件是否居多:[root@localh ...

  5. 05.Linux系统-WCP知识共享平台安装部署(旗舰版)

    WCP知识共享平台部署 一.环境准备 操作系统:CentOS Linux release 7.5.1804 (Core) Java:jdk-7u79-linux-x64.tar.gz 中间件:apac ...

  6. Java并发——DCL问题

    转自:http://www.iteye.com/topic/875420 如果你搜索网上分析dcl为什么在java中失效的原因,都会谈到编译器会做优化云云,我相信大家看到这个一定会觉得很沮丧.很无助, ...

  7. [转]0day零距离

    前言: 想起这个话题,还要从早年网上的一则新闻说起--"美国联邦官员于2001年12月11日宣布,已破获一起以因特网为犯罪手段的特大软件盗版案--盗版软件的总价值至少高达10亿美元.据悉,该 ...

  8. Tensorflow 多gpu训练

    Tensorflow可在训练时制定占用那几个gpu,但如果想真正的使用多gpu训练,则需要手动去实现. 不知道tf2会不会改善一下. 具体参考:https://wizardforcel.gitbook ...

  9. 矩阵快速幂 求斐波那契第N项

    #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> us ...

  10. GUI学习之十五——QAbstractSpinBox学习总结

    QAbstractSpinBox是一个抽象类,是将所有步长调节器的通用的功能抽象出了一个父类.虽然QAbstractSpinBox是一个抽象类,但是可以直接实例化使用.QAbstractSpinBox ...