本文将BBS+Blog项目开发中所需要的细节知识点进行补充,其中内容包括KindEditor编辑器的使用,BeautifulSoup 模块及其防XSS攻击,Django中admin管理工具的使用,media配置之MEDIA_ROOT,FBV和CBV之间的关系,Django模块之Meta选项详解,HTML中submit和button的区别等七大内容。
一:KindEditor编辑器的使用
富文本编辑器,Rich Text Editor,简称RTE,是一种可内嵌于浏览器,所见即所得的文本编辑器。
富文本编辑器不同于文本编辑器,程序员可导网上下载免费的富文本编辑器内嵌于自己的网站或程序里(当然付费的更强大些),方便用户编辑文章或信息,富文本编辑器在web开发中可以说是不可缺少的。我们可以自己集成,但在这里推荐KindEditor。
1,下载并学习KindEditor编辑器
1.1. 下载编辑器
下载 KindEditor 最新版本,下载之后打开 examples/index.html 就可以看到演示。
1.2. 部署编辑器
解压 kindeditor-x.x.x.zip 文件,将所有文件上传到您的网站程序目录里,例如:http://您的域名/editor/
Note
您可以根据需求删除以下目录后上传到服务器。
- asp - ASP程序
- asp.net - ASP.NET程序
- php - PHP程序
- jsp - JSP程序
- examples - 演示文件
1.3,修改HTML页面
1,在需要显示编辑器的位置添加 testarea 输入框
<textarea id="editor_id" name="content" style="700px;height:300px;"> <strong>HTML内容</strong> </textarea>
Note
- id在当前页面必须是唯一的值。
- 在textarea里设置HTML内容即可实现编辑,在这里需要注意的是,如果从服务器端程序(ASP、PHP、ASP.NET等)直接显示内容,则必须转换HTML特殊字符(>,<,&,”)。具体请参考各语言目录下面的demo.xxx程序,目前支持ASP、ASP.NET、PHP、JSP。
- 在有些浏览器上不设宽度和高度可能显示有问题,所以最好设一下宽度和高度。宽度和高度可用inline样式设置,也可用 编辑器初始化参数 设置。
2,在该HTML页面添加以下脚本。
<script charset="utf-8" src="/editor/kindeditor.js"></script> <script charset="utf-8" src="/editor/lang/zh-CN.js"></script> <script> KindEditor.ready(function(K) { window.editor = K.create('#editor_id'); }); </script>
Note
- 第一个参数可用其它CSS选择器,匹配多个textarea时只在第一个元素上加载编辑器。
- 通过K.create函数的第二个参数,可以对编辑器进行配置,具体参数请参考 编辑器初始化参数 。
var options = { cssPath : '/css/index.css', filterMode : true }; var editor = K.create('textarea[name="content"]', options);
1.4,获得HTML数据
// 取得HTML内容 html = editor.html(); // 同步数据后可以直接取得textarea的value editor.sync(); html = document.getElementById('editor_id').value; // 原生API html = K('#editor_id').val(); // KindEditor Node API html = $('#editor_id').val(); // jQuery // 设置HTML内容 editor.html('HTML内容');
Note
- KindEditor的可视化操作在新创建的iframe上执行,代码模式下的textarea框也是新创建的,所以最后提交前需要执行 sync() 将HTML数据设置到原来的textarea。
- KindEditor在默认情况下自动寻找textarea所属的form元素,找到form后onsubmit事件里添加sync函数,所以用form方式提交数据,不需要手动执行sync()函数。
- KindEditor默认采用白名单过滤方式,可用 htmlTags 参数定义要保留的标签和属性。当然也可以用 filterMode 参数关闭过滤模式,保留所有标签。
// 关闭过滤模式,保留所有标签 KindEditor.options.filterMode = false; KindEditor.ready(function(K)) { K.create('#editor_id'); }
2,Django中配置kindEditor
2.1 配置静态文件上传目录
编辑器中上传的文件将保存在这里
# 与用户上传相关的配置 MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = "/media/"
2.2 在项目的urls中配置文件设置
from django.contrib import admin from django.urls import path, re_path from blog import views from django.views.static import serve urlpatterns = [ ...... # media配置 re_path(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), ]
2.3 在视图函数中编写上传的函数
我们直接看我写的代码:
def upload(request): ''' 编辑器上传文件接收视图函数 :param request: :return: ''' print(request.FILES) img_obj = request.FILES.get('upload_img') print(img_obj.name) path = os.path.join(settings.MEDIA_ROOT, 'add_article_img', img_obj.name) with open(path, 'wb') as f: for line in img_obj: f.write(line) response = { 'error': 0, 'url': '/media/add_article_img/%s' % img_obj.name } import json return HttpResponse(json.dumps(response))
然后在url配置其路径
from django.contrib import admin from django.urls import path, re_path from blog import views from cnblog_review import settings from django.views.static import serve urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), # media配置 re_path(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), re_path(r'^(?P<username>w+)/articles/(?P<article_id>d+)$', views.article_detail), # 文本编辑器上传图片url path('upload/', views.upload), ]
上面这些步骤富文本编辑器应该可以正常使用了,包括上传图片,视频。
二:BeautifulSoup 模块及其防XSS攻击
1,XSS原理分析
XSS(Cross Site Script)攻击又叫做跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。他的原理是用户在使用具有XSS漏洞的网站的时候,向这个网站提交一些恶意的代码,当用户在访问这个网站的某个页面的时候,这个恶意的代码就会被执行,从而来破坏网页的结构,获取用户的隐私信息等。
2,XSS攻击场景
博客项目中用户后台添加文章时,若通过富文本编辑器输入标签内容或者JS指令的时候,会导致文章排版错乱,甚至进行XSS攻击。,我们具体一点。
3,XSS攻击的危害包括
- 1、盗取各类用户帐号,如机器登录帐号、用户网银帐号、各类管理员帐号
- 2、控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力
- 3、盗窃企业重要的具有商业价值的资料
- 4、非法转账
- 5、强制发送电子邮件
- 6、网站挂马
- 7、控制受害者机器向其它网站发起攻击
4,XSS攻击防范原理
script
字符是否存在,注意,这里不建议判断,因为随便加几个tab或者空格即可让你的判断失效!所以转义script前后的大于小于号才是重点,利用django很容易做到这件事,比如在html中去掉加入safe标签(默认情况下django是不加safe的,也就是会默认过滤转义掉几乎一切的违规字符),当然你也可以直接在view中利用django.template.defaultfilters.escape()方法,来直接对传送进来的字符串进行手动转义,这样当存进去数据库的时候就不会有"<",">",取而代之的是“<”和“>”,其他字符同理。。。下面给出了escape()默认转义的字符,它是django内定的:_html_escapes = { ord('&'): '&', ord('<'): '<', ord('>'): '>', ord('"'): '"', ord("'"): ''', }
5,基于bs4模块防御XSS攻击
将文本内容在保存数据库之前就要进行一次筛选,去除script标签,当然可以去除很多其他标签之类的,为了操纵简便,这其中需要用到BS模块。
代码如下:
from bs4 import BeautifulSoup def add_article(request): """ 后台管理的添加书籍视图函数 :param request: :return: """ if request.method == "POST": title = request.POST.get("title") content = request.POST.get("content") # 防止xss攻击,过滤script标签 soup = BeautifulSoup(content, "html.parser") # soup.find_all():获取 标签字符串所有的标签对象 for tag in soup.find_all(): print(tag.name) # tag.name获取标签名字 if tag.name == "script": # 删除script标签 tag.decompose() # 构建摘要数据,获取标签字符串的文本前150个符号 desc = soup.text[0:150]+"..." models.Article.objects.create(title=title, desc=desc, content=str(soup), user=request.user) return redirect("/cn_backend/") return render(request, "backend/add_article.html")
三:Django中admin管理工具
Django自带的后台管理是Django明显特色之一,可以让我们快速便捷的管理数据。后台管理可以在各个APP的 admin.py 文件中进行控制。Django提供了基于web 的管理工具。
Django自动管理工具是 django.contrib 的一部分。我们可以在项目的settings.py 的INSTALLED_APPS中看到其代码:
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', )
django.contrib 是一套庞大的功能集,它是Django 基本代码的组成部分。
1 激活管理工具
通常我们再生成项目时会在 urls.py 中自动设置好,我们只需要去掉注释即可。但是一般我们不用管,他就设置好了。
urls.py代码如下:
from django.conf.urls import url from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), ]
2 使用管理工具
启动开发服务器,然后在浏览器中访问 http://127.0.0.1:8000/admin/,得到如下界面:
我们可以通过下面命令来创建超级用户:
python manage.py createsuperuser
之后输入用户密码登录,界面如下:
为了让admin界面管理某个数据模型,我们需要先注册该数据模型到admin。
比如,我们之前在TestModel中已经创建了模型Test,修改TestModel/admin.py:
from django.contrib import admin from TestModel.models import Test # Register your models here. admin.site.register(Test)
刷新之后,我们可以在后台管理页面看到Testmodel数据表:
3,复杂模型
管理页面的功能强大,完全有能力处理更加复杂的数据模型。
先在TestModel/model.py中增加一个更复杂的数据模型:
from django.db import models # Create your models here. class Test(models.Model): name = models.CharField(max_length=20) class Contact(models.Model): name = models.CharField(max_length=200) age = models.IntegerField(default=0) email = models.EmailField() def __str__(self): return self.name class Tag(models.Model): contact = models.ForeignKey(Contact) name = models.CharField(max_length=50) def __str__(self): return self.name
这里有两个表,Tag以Contact为外部键,一个Contact可以应对多个Tag。
我们还可以看到许多属性类型:
在TestModel/admin.py 注册多个模型并显示:
from django.contrib import admin from TestModel.models import Test,Contact,Tag # Register your models here. admin.site.register([Test, Contact, Tag])
刷新管理页面,显示结果如下:
在以上管理工具,我们就能进行复杂模型操作。
4,自定义表单
我们可以自定义管理页面,来取代默认的页面。比如上面的add 页面。我们想只显示 name 和 email 部分。修改 TestModel/admin.py
from django.contrib import admin from TestModel.models import Test,Contact,Tag # Register your models here. class ContactAdmin(admin.ModelAdmin): fields = ('name', 'email') admin.site.register(Contact, ContactAdmin) admin.site.register([Test, Tag])
以上代码定义了一个ContactAdmin类,用以说明管理页面的显示格式。
里面的fields属性定义了要显示的字段。
由于该类对应的是Contact数据模型,我们在注册的时候,需要将他们一起注册。显示效果如下:
我们还可以将输入栏分块,每个栏也可以定义自己的格式。修改TestModel/admin.py为:
from django.contrib import admin from TestModel.models import Test,Contact,Tag # Register your models here. class ContactAdmin(admin.ModelAdmin): fieldsets = ( ['Main',{ 'fields':('name','email'), }], ['Advance',{ 'classes': ('collapse',), # CSS 'fields': ('age',), }] ) admin.site.register(Contact, ContactAdmin) admin.site.register([Test, Tag])
上面的栏目分为了Main 和 Advance两部分。classes说明它所在的部分的CSS格式,这里让Advance部分隐藏。
Advance 部分旁边有一个Show按钮,用于展开,展开后可点击Hide将其隐藏,如下图所示:
5,内联(Inline)显示
上面的Contact是Tag的外部键,所以有外部参考的关系。
而在默认的页面显示中,将两者分离开,无法体现出两者的从属关系。我们可以使用内联显示,让Tag附加在Contact的编辑页面上显示。
修改TestModel/admin.py
from django.contrib import admin from TestModel.models import Test,Contact,Tag # Register your models here. class TagInline(admin.TabularInline): model = Tag class ContactAdmin(admin.ModelAdmin): inlines = [TagInline] # Inline fieldsets = ( ['Main',{ 'fields':('name','email'), }], ['Advance',{ 'classes': ('collapse',), 'fields': ('age',), }] ) admin.site.register(Contact, ContactAdmin) admin.site.register([Test])
显示效果如下:
列表页的显示
在Contact输入数条记录后,Contact 的列表页看起来如下:
我们也可以自定义该页面的显示,比如在列表中显示更多的栏目,只需要在ContactAdmin中增加list_display属性。
from django.contrib import admin from TestModel.models import Test,Contact,Tag # Register your models here. class TagInline(admin.TabularInline): model = Tag class ContactAdmin(admin.ModelAdmin): list_display = ('name','age', 'email') # list inlines = [TagInline] # Inline fieldsets = ( ['Main',{ 'fields':('name','email'), }], ['Advance',{ 'classes': ('collapse',), 'fields': ('age',), }] ) admin.site.register(Contact, ContactAdmin) admin.site.register([Test])
刷新页面显示效果如下:
搜索功能在管理大量记录时非常有用,我们可以使用serach_fileds 为该列表页增加搜索栏:
from django.contrib import admin from TestModel.models import Test,Contact,Tag # Register your models here. class TagInline(admin.TabularInline): model = Tag class ContactAdmin(admin.ModelAdmin): list_display = ('name','age', 'email') # list search_fields = ('name',) inlines = [TagInline] # Inline fieldsets = ( ['Main',{ 'fields':('name','email'), }], ['Advance',{ 'classes': ('collapse',), 'fields': ('age',), }] ) admin.site.register(Contact, ContactAdmin) admin.site.register([Test])
四:media配置之MEDIA_ROOT
1,静态文件(static)和媒体文件(media)详细配置
1.1 Static和Media的区别
Django 有两种静态文件:
/static/ : js,css,img 这指的是服务器自己使用的文件。
/media/: 这指的是用户上传的文件。
这里区分开,解耦性更好。所以media绝对有存在的意义。那他们的区别是什么呢?
- Static:是不变的,是形成网站的核心部件,如CSS文件,JS文件,背景图片等。
- Media:是变动的,由用户定义的文件,如用户头像,用户上传的图片或者视频等。
1.2 配置静态文件(static)的方法
1,在项目根目录下新建 static文件夹。
2,在settings.py中设置如下:
# 静态文件配置部分 import os STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 这个配置是为了通过ip地址直接访问到静态文件 STATICFILES_DIRS = ( os.path.join(STATIC_ROOT, 'static') )
3,在模板文件的<!DOCTYPE html> 下面(不要写到最开头),写入{% load staticfiles %},引入静态文件。
4,在模板文件中引用:{% static '<static文件夹中的目标文件路径>' %}
1.3 配置媒体文件(media)的方法
1,在项目根目录新建 media 文件夹
2,在settings.py中设置如下:
# 媒体文件配置部分 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
3,在项目url文件中设置如下:
# 媒体文件想要通过ip地址访问到静态文件要做如下配置 from django.views.static import serve # 导入 from django.conf import settings url(r'^/media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT})
1.4 媒体文件(media)配置注意事项
我们再models里面想上传 models.FileField 或者 models.ImageField 字段的时候,他们里面通常又一个:
upload_to=属性, default=属性;
这里的 upload_to 我们通常会写 media路径,(因为都是下载的),如果要保存到 media下面,那么我们写路径的时候等于是在 media 文件夹下创建一个新的文件夹,存在我们文件default也可以,但是当我们存在别的路径下,就要重新找路径。
1.5 示例
下面我们先看视图中这段代码:
avatar_obj = request.FILES.get('avatar') user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj)
下面去settings中配置MEDIA路径
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
一旦配置了MEDIA路径,Django就会对文件对象下载到MEDIA_ROOT中avatar文件夹中(如果没有avatar文件夹,Django会自动创建)。
2,media配置之MEDIA_URL详细解析
浏览器如何能直接访问到media中的数据。
首先配置settings.py中相关路径
# 与用户上传相关的配置 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_ROOT 代表着要上传的路径和你在 models中写的上传的路径进行拼接形成的最终文件上传的路径。
MEDIA_URL 主要是映射了在前端使用media_url ,当你的 media_root 发生改变的时候不用去更改前端模板中的内容。
再配置根目录下urls.py
固定的格式,里面的内容不能改变。首先需要导入下面的库,和在settings中配置的MEDIA_ROOT上传路径。
from django.contrib import admin from django.urls import path, include, re_path from django.views.static import serve from cnblog import settings urlpatterns = [ path('admin/', admin.site.urls), path('blog/', include('blog.urls')), # media配置 re_path(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}) ]
五:FBV 和 CBV之间的关系
FBV
FBV(function base views)就是在视图里使用函数处理请求。
在之前django的学习中,我们一直使用的是这种方式。
CBV
CBV(class base views)就是在视图函数里使用类处理请求。
Python是一个面向对象的变成语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承,封装,多态)。所以Django在后来加入了Class-Base-View 。可以让我们用类写View。这样做的优点主要有下面两种:
- 1,提高了代码的复用性,可以使用面向对象的技术,比如Minxin(多继承)
- 2,可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
1,使用Class-Base-Views
如果我们要写一个处理GET方法的view,用函数写的话是下面这样:
from django.http import HttpResponse def my_view(requset): if requset.method == 'GET': return HttpResponse("OK")
如果使用class-base-view写的话,就是下面这样:
from django.http import HttpResponse from django.views import View class MyView(View): def get(self, request): return HttpResponse("OK")
Django的url是将一个请求分配给可调用的函数的,而不是一个class。针对这个问题,class-bases-views提供了一个as_view()静态方法(也就是类方法),调用这个方法,会去创建一个类的实例,然后通过实例调用dispatch(0方法,dispatch()方法会根据request的method的不同调用相应的方法来处理request(如get(),post()等)。到这里,这些方法和function-based view差不多了,要接收request,得到一个response返回。如果方法没有定义,会抛出HTTPResponseNotAllowed异常。
在url中,就是这么写:
# urls.py from django.conf.urls import url from myapp.views import MyView urlpatterns = [ url(r"^index/$", MyView.as_view()), ]
类的属性可以通过两种方法设定,第一种是常见的Python方法,可以被子类覆盖。
from django.http import HttpResponse from django.views import View class GreetingView(View): name = 'james' def get(self, request): return HttpResponse(self.name) # you can override that in a subclass class GreetingView2(GreetingView): name = 'durant'
第二种方法,你也可以在url中指定类的属性:在url中设置类的属性Python
urlpatterns = [ url(r'^index/$', GreetingView.as_view(name='harden')) ]
2,使用多继承(Mixin)
我觉得要理解django的class-based-view(以下简称CBV),首先要明白django引入CBV的目的是什么。在django1.3之前,generic view 也就是所谓的通用视图,使用的是function-based-view(FBV),也就是基于函数的视图。但是python的一大重要的特性就是面向对象,而CBV更能体现python的面向对象,CBV是通过class的方法来实现视图方法的。class相对于function,更能利用多态的特定,因此更容易从宏观层面上将项目内的比较通用的功能抽象出来。
总之,可以理解为一个东西具有多种形态的特性。CBV的实现原理通过看django的源码就很容易明白,答题急速由URL理由到这个CBV之后,通过CBV内部的dispatch方法进行分发,将get请求分发给CBV.get方法处理,将post请求分发给CBV.post方法处理。
那么怎么利用多态呢?CBV里引入了mixin的概念。Mixin就是写好了的一些基础类,然后通过不同的Mixin组合成为最终想要的类。
因此,理解CBV的基础是理解Mixin。Django使用Mixin来重用代码,一个View Class可以继承多个Mixin,但是只能继承一个View(包括View的子类),推荐把View写在最右边,多个Mixin写在左边。
六:Django 模型之Meta选项详解
Django模型类的Meta是一个内部类,它用于定义一些Django模型类的行为特性。内部类Meta对于models来说,不是必须的,但是对于用户在实际使用中具有重要的作用,有些元数据选项能给我们极大的帮助。而可用的选项大致包含以下几类。
1,abstract
这个属性是定义当前的模型是不是一个抽象类。所谓抽象类是不会对应数据表的。一般我们用它来归纳一些公共属性字段,然后继承它的子类可以继承这些字段。
Optional.abstract 如果abstract = True ,这个model就是一个抽象基类
2,app_label
这个选型只在一种情况下使用,就是你的模型不再默认的应用程序包下的models.py文件中,这时候需要指定你这个模型是哪个应用程序的。
Options.app_label 如果一个model定义在默认的models.py之外(例如,如果你的APP的models在myapp.models 子模块下),你必须定义app_label 让DJango知道他属于哪一个APP app_label = 'myapp'
3,db_table
db_table 是指定自定义数据库表明的。Django有一套默认的按照一定规则生成数据模型对应的数据库表明。
Options.db_table 定义该model在数据中的表名称: db_table = 'music_album' 如果你想使用自定义的表名,可以通过以下该属性 table_name = 'my_owner_table'
4,db_teblespace
Options.db_teblespace 定义这个model所使用的数据库表空间。如果在项目的settings.py中定义那么它会使用这个值
5,get_latest_by
Options.get_latest_by 在model中指定一个DateField或者DateTimeField。这个设置让你在使用model的 Manager上的lastest方法时,默认使用指定字段来排序
6,managed
Options.managed 默认值为True,这意味着Django可以使用syncdb和reset命令来创建或移除对应 的数据库。默认值为True,如果你不希望这么做,可以把manage的值设置为False
7,order_with_respect_to
这个选型一般用于多对多的关系中,它指向一个关联对象,就是说关联对象找到这个后它是经过排序的。指定这个属性后你会得到一个get_xxx_order()和set_xxx_order()的方法,通过它们你可以设置或者回去排序的对象。
8,ordering
这个字段是告诉Django模型对象返回的记录结果集是按照哪个字段排序的。这是一个字符串的元组或列表,没有一个字符串都是一个字段和用一个可选的表明降序的'-'构成。当字段名前面没有'-'时,将默认使用升序排列。使用'?'将会随机排列
- ordering=['order_date'] # 按订单升序排列
- ordering=['-order_date'] # 按订单降序排列,-表示降序
- ordering=['?order_date'] # 随机排序,?表示随机
- ordering=['-pub_date','author'] # 以pub_date为降序,在以author升序排列
9,permissions
permissions主要是为了在Django Admin管理模块下使用的,如果你设置了这个属性可以让指定的方法权限描述更清晰可读。Django自动为每个设置了admin的对象创建添加,删除和修改的权限。
permissions = (('can_deliver_pizzas','Can deliver pizzas'))
10,Proxy
这是为了实现代理模型使用的,如果proxy = True,表示model是其父的代理 model
11,unique_together(***)
unique_together这个选项用于:当你需要通过两个字段保持唯一性时使用。比如假设你希望,一个Person的FirstName和LastName两者的组合必须是唯一的,那么需要这样设置:
unique_together = (("first_name", "last_name"),)
一个ManyToManyField不能包含在unique_together中。如果你需要验证关联到ManyToManyField字段的唯一验证,尝试使用signal(信号)或者明确指定through属性。
使用Django中设定model的时候,常常会遇到这样的需求,对一个表的几个字段做联合唯一索引。例如student表中name和classes两个字段一起表示一个唯一记录。
class StudentModel(models.Model): name = models.CharField(max_length=50) classes = models.CharField(max_length=50) def __str__(self): return self.name class Meta: unique_together = ('name', 'classes',)
对应到MySQL中的SQL语句如下:
CREATE UNIQUE INDEX index_name ON tablename(field1, field2);
12,verbose_name
verbose_name的意思很简单,就是给你的模型类起一个更可读的名字一般定义为中文。
Django模型中的verbose_name 我们常常可能需要使用,比如将数据库里面的数据导出成 csv文件。那么csv文件的表头的名字可以通过取每个字段的verbose_name来获取,数据可以通过QuerySet语句来获取,这样制作出来的csv表就能像数据库一样,字段名和字段值一一对应了。
Options.verbose_name 指明一个易于理解和表述的对象名称,单数形式: verbose_name = 'user_name' 如果这个值没有设定,Django将会使用该model的类名的分词形式作为其对象的表述名 CamelCase将会转换为camel case
13,verbose_name_plural
这个选项是指定,模型的复数形式是什么,比如:
verbose_name_plural = "学校"
如果不指定Django会自动在模型名称后加一个’s’
一些常见的元信息的例子
class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) class Meta: # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名 db_table = "table_name" # 联合索引 index_together = [ ("pub_date", "deadline"), ] # 联合唯一索引 unique_together = (("driver", "restaurant"),) # admin中显示的表名称 verbose_name # verbose_name加s verbose_name_plural
七:HTML中submit 和 button 的区别
submit 是button 的一个特例,也是 button 的一种,它把提交这个动作自动集成了。如果表单在点击提交按钮后需要用JS进行处理(包括输入验证)后再提交的话,通常都必须把submit改成button,即取消其自动提交的行为,否则,将会造成两次的效果,对于动态网页来说,也就是对数据库操作两次。或者在使用 submit 验证时加上 return true 或者 false。
submit 和button ,两者都是以按钮的形式展示,看起来都是按钮,所不同的是type属性和触发响应的事件上,submit会提交表单,button不会提交表单。
submit 默认为form提交,可以提交表单(form)。
button则响应用户自定义的事件,如果不指定 oncllick 等事件处理函数,它是不做任何事情。当然,button也可以完成表单提交的工作。
input type = submit 即发送表单,按回车提交表单 input type = button 是的单纯的按钮功能,提交的是 inner TEXT
submit :特殊的button,会自动将表单的数据提交,onClick方法不加 return 会自动提交,并不会起到约束的作用,所以,使用submit时需要验证请加 return true 或者 false 。
<input type='submit' name='Submit' value='注册' onClick = 'return check();'> 在JS中判断的时候,写 return true;或者 return false
button:普通的按钮,不会自动提交表单数据,可以在JS中显式提交:document.form1.submit();使用场合:一个页面有多个提交按钮,需要根据用户的操作来确定到底提交到哪个控制器,这种情况下,就需要在JS中判断用户的操作,然后根据操作给 document.form1.action 赋值并且 document.form1.submit() 来提交。
参考文献:https://www.cnblogs.com/flash55/p/6265405.html