创建框架
django-admin startproject testdj # 创建一个Django项目 cd testdj python manage.py startapp my_app # 创建一个app
配置文件
在urls.py下添加app的路径
from django.contrib import admin from django.urls import path, re_path, include urlpatterns = [ path(\'admin/\', admin.site.urls), re_path(r\'^\', include(\'my_app.urls\')), ]
在app目录下新建urls.py ,用于配置这个app下所有的路由
from django.urls import path, re_path from my_app import views urlpatterns = [ re_path(r\'^\', views.index), ]
在app内的views.py文件中写一个index处理函数
from django.shortcuts import render, HttpResponse def index(request): return HttpResponse(\'你好啊\')
在项目目录下的settings.py内添加创建的app
INSTALLED_APPS = [ \'django.contrib.admin\', \'django.contrib.auth\', \'django.contrib.contenttypes\', \'django.contrib.sessions\', \'django.contrib.messages\', \'django.contrib.staticfiles\', \'my_app\', # 新加入的一行 ]
测试一下
python manage.py runserver 0.0.0.0:8000
查看网页
加入数据库
新创建一个名叫my_app的mysql数据库,之后开启MySQL服务并在settings.py内配置数据库连接信息
DATABASES = { \'default\': { \'ENGINE\': \'django.db.backends.mysql\', # 数据库引擎 \'NAME\': \'my_app\', # 数据库名,事先要创建 \'USER\': \'root\', # 数据库用户名 \'PASSWORD\': \'123456\', # 密码 \'HOST\': \'localhost\', # 主机 \'PORT\': \'3306\', # 数据库使用的端口 } }
配置好之后,运行manage命令更新数据表,Django会生成一些默认的表(原本是没有一张表的)
python manage.py migrate
结果报错
raise MigrationSchemaMissing("Unable to create the django_migrations table (%s)" % exc)
原因是:Django2.1不再支持MySQL5.5,必须5.6版本以上
解决办法二选一:
(1)Django降级到2.0
pip install Django==2.0.0 -i https://pypi.douban.com/simple
(2)MySQL升级
我选择降级之后再更新表
auth_user表示是保存后台管理员用户信息的
现在创建一个超级管理员
python manage.py createsuperuser
按照命令行的提示,依次输入用户名,邮箱,密码(两次),其中邮箱可以为空(直接回车跳过)
数据表中已经可以看到管理员的信息了
在settings.py指定语音
LANGUAGE_CODE = \'zh-hans\' # 指定语言(注意不要写错,否则无法启动服务器) TIME_ZONE = \'Asia/Shanghai\'
运行
python manage.py runserver
打开后台管理平台
一些简单的后台修改可以在admin.py内设置,例如标题和网页的title:
from django.contrib import admin # Register your models here. admin.site.site_header = \'我的后台管理\' admin.site.site_title = \'后台管理\'
如果涉及到更复杂的后台定制,例如模板布局,样式等就需要小折腾一番了,首先修改settings.py:
TEMPLATES = [ { \'BACKEND\': \'django.template.backends.django.DjangoTemplates\', # 优先使用项目模板路径 \'DIRS\': [BASE_DIR + "/templates"], \'APP_DIRS\': True, \'OPTIONS\': { \'context_processors\': [ \'django.template.context_processors.debug\', \'django.template.context_processors.request\', \'django.contrib.auth.context_processors.auth\', \'django.contrib.messages.context_processors.messages\', ], }, }, ]
# 优先使用项目路径 STATIC_URL = \'/static/\' STATICFILES_DIRS = (os.path.join(BASE_DIR, \'static\'),)
Django默认在当前项目目录static路径下查找静态文件(js,css等),templates路径下查找模板文件(html文件),例如后台的模板页面和样式文件;没有找到文件就会在Django的安装目录下调用。
把static,templates两个文件夹复制到项目目录下
加入数据模型类
后台和数据库已经搞定,现在可以写我们网站的功能了;就写一个最简单的功能,发布企业新闻。打开app内的models.py文件,写两个数据模型类:
from django.db import models class NewsClass(models.Model): name = models.CharField(\'分类名称\', max_length=10) def __str__(self): return self.name class Meta: verbose_name = \'资讯类别\' verbose_name_plural = \'资讯类别\' class News(models.Model): # 默认添加 # id = models.AutoField(primary_key=True) title = models.CharField(\'文章标题\', max_length=30) content = models.TextField(\'文章内容\') date = models.DateTimeField(\'发布时间\') show = models.BooleanField(\'是否显示\') news_class = models.ForeignKey( NewsClass, verbose_name=\'文章分类\', on_delete=models.CASCADE) def __str__(self): return self.title class Meta: verbose_name = \'资讯\' verbose_name_plural = \'资讯\'
关于数据模型类,可以理解为一张数据表,类里面的属性就是表的字段;id字段如果没有特殊要求可以不写,Django会自动创建。为了后台显示美观,这里我给字段和类都设置了中文别名;更多关于django的models模型类的常用数据类型和选项,留个链接https://www.jianshu.com/p/651aa9fe5d1a,有点数据库基础应该不难理解。
写好数据模型类,运行manage命令,会在app目录migrations/和migrations/__pycache__/路径下各生成一个记录文件:
python manage.py makemigrations
运行更新数据表会多出两个
python manage.py migrate
对数据模型做了任何修改都要执行上面两个manage命令,同步数据库;如果直接在数据库里修改字段,删除表或者删除了之前的数据模型记录文件,可能会导致python manage.py migrate命令无法从数据模型更新表,不过也没关系,运行下面的命令:
python manage.py sqlmigrate \'your_app_name\' 0001查看框架自动生成的sql语句
控制台显示0001记录文件转化的sql语句,复制sql语句可以直接在数据库中操作。
现在有了新闻分类和新闻内容2张数据表,在后台注册这两个数据模型类,这样就能直接通过后台向数据表里添加数据了,打开app内admin.py:
from web_app.models import * admin.site.register(News) admin.site.register(NewsClass)
然后python manage.py runserver,打开后台
点击添加咨询
这里Django默认的大容量文本字段是通过一个textarea作为输入方式,所以我们还需要引入一个富文本编辑器,这里我推荐KindEditor(http://kindeditor.net/demo.php),配置简单,功能也齐全。
下载KindEditor,将此静态文件放入static中,
config.js内容
KindEditor.ready(function(k) { window.editor = k.create(\'#id_content\', { resizeType: 1, allowPreviewEmoticons: false, allowImageRemote: false, width: \'700px\', height: \'400px\', }); })
然后打开admin.py写入一个配置类,之后再与相关模型一起注册,然后现在的admin.py文件为
from django.contrib import admin from my_app.models import * class NewsAdmin(admin.ModelAdmin): # 写一个配置类 list_display = (\'title\', \'news_class\', \'date\', \'show\') class Media: # 在管理后台的相关HTML文件中加入js文件 js = ( \'/static/kindeditor/kindeditor-all-min.js\', \'/static/kindeditor/lang/zh-CN.js\', \'/static/kindeditor/config.js\', ) admin.site.site_header = \'我的后台管理\' admin.site.site_title = \'后台管理\' admin.site.register(News, NewsAdmin) # 与相关模型一起注册(必须写一起才行) admin.site.register(NewsClass)
然后运行网站,打开后台就会发现,那个文本编辑器已经换成我们添加进去的KindEditor了,如下图
编辑器自带的上传图片和文件选项还不能使用,我们需要配置一个post上传url;首先在项目settings.py内添加两个配置,设置上传文件目录:
首先在项目settings.py内添加两个配置,设置上传文件目录:
# 上传设置 MEDIA_URL = \'/static/upload/\' MEDIA_ROOT = os.path.join(BASE_DIR, \'static/upload\')
在app文件夹内新建一个upload.py处理文件上传:
import os import uuid import json import datetime from django.http import HttpResponse from django.conf import settings # 上传post请求地址 # http://127.0.0.1:8000/upload_file/?dir=media def upload(request): # kindeditor图片上传返回数据格式说明: # {\'error\': 1, \'message\': \'出错信息\'} # {\'error\': 0, \'url\': \'图片地址\'} result = {\'error\': 1, \'message\': \'上传失败\'} # input type="file" 中name属性对应的值为imgFile files = request.FILES.get(\'imgFile\', None) type = request.GET[\'dir\'] # 获取资源类型 if files: result = process_upload(files, type) # 结果以json形式返回 return HttpResponse(json.dumps(result), content_type=\'application/json\') def is_ext_allowed(type, ext): # 根据类型判断是否支持对应的扩展名 ext_allowed = {} ext_allowed[\'image\'] = [\'jpg\', \'jpeg\', \'bmp\', \'gif\', \'png\'] ext_allowed[\'flash\'] = [\'swf\', \'flv\'] ext_allowed[\'media\'] = [\'swf\', \'flv\', \'mp3\', \'wav\', \'wma\', \'wmv\', \'mid\', \'avi\', \'mpg\', \'asf\', \'rm\', \'rmvb\', \'mp4\'] ext_allowed[\'file\'] = [\'doc\', \'docx\', \'xls\', \'xlsx\', \'ppt\', \'htm\', \'html\', \'txt\', \'zip\', \'rar\', \'gz\', \'bz2\', \'pdf\'] return ext in ext_allowed[type] def get_relative_file_path(): # 获取相对路径 dt = datetime.datetime.today() relative_path = \'%s/%s/\' % (dt.year, dt.month) absolute_path = os.path.join(settings.MEDIA_ROOT, relative_path) print(absolute_path) if not os.path.exists(absolute_path): os.makedirs(absolute_path) return relative_path def process_upload(files, type): dir_types = [\'image\', \'flash\', \'media\', \'file\'] # 判断是否支持对应的类型 if type not in dir_types: return {\'error\': 1, \'message\': \'上传类型不支持[必须是image,flash,media,file]\'} cur_ext = files.name.split(\'.\')[-1] # 当前上传文件的扩展名 # 判断是否支持对应的扩展名 if not is_ext_allowed(type, cur_ext): return {\'error\': 1, \'message\': \'扩展名不支持 %s类型不支持扩展名%s\' % (type, cur_ext)} relative_path = get_relative_file_path() # linux中一切皆文件 file_name = str(uuid.uuid1()) + \'.\' + cur_ext base_name = os.path.join(settings.MEDIA_ROOT, relative_path) # windows中的路径以\分隔 file_full_path = os.path.join(base_name, file_name).replace(\'\\\', \'/\') file_url = settings.MEDIA_URL + relative_path + file_name with open(file_full_path, \'wb\') as f: if files.multiple_chunks() == False: # 判断是否大于2.5M f.write(files.file.read()) else: for chunk in files.chunks(): f.write(chunk) return {\'error\': 0, \'url\': file_url}
注意返回的json数据格式不要写错,否则KindEditor编辑器上传会报错。上传的文件我们保存在static/upload目录内,有了上传处理函数后,就可以在app的url.py内配置路由了:
from web_app import upload as u # app url 配置 urlpatterns = [ re_path(r\'^$\', views.index), re_path(r\'^upload_file/$\', u.upload, name=\'upload\'), ]
在KindEditor的参数js内(config.js)添加上传url参数:
// 上传请求路径 uploadJson: \'/upload_file/\',
完整config.js
KindEditor.ready(function(k) { var csrf_token = document.getElementsByName(\'csrfmiddlewaretoken\')[0].value; window.editor = k.create(\'#id_content\', { resizeType: 1, allowPreviewEmoticons: false, allowImageRemote: false, // 上传请求路径 uploadJson: \'/upload_file/\', width: \'700px\', height: \'400px\', // 处理csrf验证 extraFileUploadParams: { csrfmiddlewaretoken: csrf_token }, }); })
由于Django自带CSRF验证(有兴趣的同学可以了解下:https://www.jianshu.com/p/a178f08d9389),所以上传图片失败了,但是有2种方法解决:
- 在settings.py内注释掉\'django.middleware.csrf.CsrfViewMiddleware\',关闭CSRF验证。
- post上传请求头内添加CSRF参数(推荐方法)
我选择了第一张方法,然后上传图片成功了
前台数据展现
views.py内写一个处理函数
from django.shortcuts import render, HttpResponse from my_app.models import * def news_content(request): # http://127.0.0.1:8000/news/?id=1 id_num = request.GET.get(\'id\') context = {} try: context[\'News\'] = News.objects.get(id=id_num) context[\'title\'] = context[\'News\'].title except News.DoesNotExist: context[\'News\'] = \'404\' context[\'title\'] = \'404\' return render(request, \'my_app/article.html\', context) return render(request, \'my_app/article.html\', context)
根据url内id参数查询新闻内容,然后返回一个模板文件与dict对象;接着在templates路径下创建我们的html模板文件:
(这里我用Bootstrap可视化布局系统简单演示一下)
这些网页布置文件全在templates下的my_app内
{% include "my_app/head.html" %} <div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <h3>{{News.title}}</h3> {% autoescape off %} {{News.content}} {% endautoescape %} </div> </div> </div> {% include "my_app/foot.html" %}
head.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{title}}</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap-combined.min.css"> </head> <body>
url.py中加上路由:
from django.urls import path, re_path
from my_app import views
from my_app import upload as u
urlpatterns = [
re_path(r\'^$\', views.index),
re_path(r\'^news/$\', views.news_content),
re_path(r\'^upload_file/$\', u.upload, name=\'upload\'),
]