Django的media配置与富文本编辑器使用的实例
效果预览
文章列表
添加文章
编辑文章|文章详情|删除文章
项目的基本文件
项目的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配置与富文本编辑器使用的实例的更多相关文章
- Django 插件之 Xadmin实现富文本编辑器
此文为前一篇文章的续写: Django 插件之 Xadmin Ueditor 介绍 UEditor 是由百度 web 前端研发部开发所见即所得富文本 web 编辑器,具有轻量,可定制,注重用户体验等特 ...
- Django学习---py3下的富文本编辑器的使用
背景说明: Ueditor HTML编辑器是百度开源的HTML编辑器,但是在Python3下调用报错,找不到widgets模块,经查发现,DjangoUeditor是基于Python 2.7的,对Py ...
- django项目中使用KindEditor富文本编辑器。
先从官网下载插件,放在static文件下 前端引入 <script type="text/javascript" src="/static/back/kindedi ...
- django项目中使用KindEditor富文本编辑器
先从官网下载插件,放在static文件下 前端引入 <script type="text/javascript" src="/static/back/kindedi ...
- 配置KindEditor富文本编辑器
第一步:首先我们要到KindEditor官网下载资源包-点击进入官网下载KindEditor资源包 第二部:在下载完了KindEditor的资源包后解压结构如下图所示: 里面包括集中语言的文件上传后台 ...
- django配置Ueditor富文本编辑器
1.https://github.com/twz915/DjangoUeditor3下载包,进入包文件夹,找到DjangoUeditor包拷贝到项目下,和xadmin同级目录 2.找到项目的setti ...
- Django Admin后台使用tinymc 富文本编辑器
1.CDN地址 <script src="//cdn.tinymce.com/4/tinymce.min.js"></script> 2.修改base.ht ...
- django2集成DjangoUeditor富文本编辑器
富文本编辑器,在web开发中可以说是不可缺少的.django并没有自带富文本编辑器,因此我们需要自己集成,在这里推荐大家使用DjangoUeditor,因为DjangoUeditor封装了我们需要的一 ...
- MVC 使用 Ueditor富文本编辑器
一.Ueditor 1.下载Ueditor富文本编辑器 官方下载地址: http://ueditor.baidu.com/website/download.html 建议下载开发版,此处我下载的是 . ...
随机推荐
- layui动态渲染select等组件并初始化赋值失败
描诉:有一个用户信息form表单,其中有部门单选框,数据库中有一张dept(部门)表,要动态渲染出所有部门,并默认选中用户所在部门 关键代码: html页面 <div class="l ...
- [BZOJ 4668]冷战(带边权并查集+启发式合并)
[BZOJ 4668]冷战(并查集+启发式合并) 题面 一开始有n个点,动态加边,同时查询u,v最早什么时候联通.强制在线 分析 用并查集维护连通性,每个点x还要另外记录tim[x],表示x什么时间与 ...
- bootstrap中的横的列
col-md-6都是可以嵌套的,所以12列都是虚拟的 所以bootstrap是怎么完成的?都是通过绝对的像素值吗?还是自动计算出了本区域的像素数,然后设置的? 看样子应该是后者,所以整个bootstr ...
- 要了解mysql原理,还是要心里有点B树才行
要了解数据库索引的底层原理,我们就得先了解一种叫树的数据结构,而树中很经典的一种数据结构就是二叉树!所以下面我们就从二叉树到平衡二叉树,再到B-树,最后到B+树来一步一步了解数据库索引底层的原理! ...
- 轮播图--js课程
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Linux 解决E: Sub-process /usr/bin/dpkg returned an error code (1)错误
在用apt-get安装软件时出现了类似于 install-info: No dir file specified; try --help for more information.dpkg: 处理 g ...
- UITextView学习笔记
=================================== UITextView =================================== 1.UITextView常用属性 ...
- Android相关资源
各类黑客大会资料 https://infocon.org/cons/ 各类课程.视频 https://github.com/Developer-Y/cs-video-courses#security ...
- [人工智能]IBM Watson人工智能API|一步步创建智能微信翻译官|第一章
最近参加了IBM可认知内部创业活动,想从零创建一个微信翻译工具,这就是我的AI翻译官.
- 细数不懂Spring底层原理带来的伤与痛
原文链接:https://www.jianshu.com/p/c9de414221ac?utm_campaign=haruki&utm_content=note&utm_medium= ...