Document

用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  

打开后台管理平台

http://127.0.0.1:8000/admin/

一些简单的后台修改可以在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'),
]

posted @ 2020-01-17 10:33  dummersoul  阅读(3023)  评论(0编辑  收藏  举报