用Django框架开发一个简单的企业网站
创建框架
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'),
]