django开发第一次

django开发

需求的描述

用户端

  1. 可以通过搜索到博客内容
  2. 能够根据关键词进行搜索。然后展示出文章列表
  3. 能够根据某个分类查看所有关于这一分类的文章
  4. 首页看到新到旧的文章列表
  5. 能够订阅,能够评论,能够配置友链

作者端

  1. 后台需登录进入
  2. 创建分类和标签
  3. 能够用markdown进行编写
  4. 上传文章配图
  5. 配置导航
  6. 及时更新,收到对应的订阅

ER关系说明

image-20210116204419728

sql语句如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
CREATE TABLE `user` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT
);

CREATE TABLE `category` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`created_time` DATETIME,
`is_nav` INTEGER,
`owner` INTEGER
);

CREATE INDEX `idx_category__owner` ON `category` (`owner`);

ALTER TABLE `category` ADD CONSTRAINT `fk_category__owner` FOREIGN KEY (`owner`) REFERENCES `user` (`id`) ON DELETE SET NULL;

CREATE TABLE `link` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`href` VARCHAR(255) NOT NULL,
`status` VARCHAR(255) NOT NULL,
`created_time` DATETIME,
`weight` INTEGER,
`owner` INTEGER
);

CREATE INDEX `idx_link__owner` ON `link` (`owner`);

ALTER TABLE `link` ADD CONSTRAINT `fk_link__owner` FOREIGN KEY (`owner`) REFERENCES `user` (`id`) ON DELETE SET NULL;

CREATE TABLE `post` (
`id` INTEGER PRIMARY KEY,
`title` VARCHAR(255) NOT NULL,
`description` VARCHAR(255) NOT NULL,
`content` VARCHAR(255) NOT NULL,
`status` VARCHAR(255) NOT NULL,
`create_time` DATETIME,
`updated_time` DATETIME,
`category` INTEGER,
`user` INTEGER
);

CREATE INDEX `idx_post__category` ON `post` (`category`);

CREATE INDEX `idx_post__user` ON `post` (`user`);

ALTER TABLE `post` ADD CONSTRAINT `fk_post__category` FOREIGN KEY (`category`) REFERENCES `category` (`id`) ON DELETE SET NULL;

ALTER TABLE `post` ADD CONSTRAINT `fk_post__user` FOREIGN KEY (`user`) REFERENCES `user` (`id`) ON DELETE SET NULL;

CREATE TABLE `comment` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`nickname` VARCHAR(255) NOT NULL,
`email` VARCHAR(255) NOT NULL,
`website` VARCHAR(255) NOT NULL,
`content` VARCHAR(255) NOT NULL,
`created_time` DATETIME,
`post` INTEGER
);

CREATE INDEX `idx_comment__post` ON `comment` (`post`);

ALTER TABLE `comment` ADD CONSTRAINT `fk_comment__post` FOREIGN KEY (`post`) REFERENCES `post` (`id`) ON DELETE SET NULL;

CREATE TABLE `sidebar` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`type` VARCHAR(255) NOT NULL,
`title` VARCHAR(255) NOT NULL,
`status` INTEGER,
`content` VARCHAR(255) NOT NULL,
`created_time` DATETIME,
`owner` INTEGER
);

CREATE INDEX `idx_sidebar__owner` ON `sidebar` (`owner`);

ALTER TABLE `sidebar` ADD CONSTRAINT `fk_sidebar__owner` FOREIGN KEY (`owner`) REFERENCES `user` (`id`) ON DELETE SET NULL;

CREATE TABLE `tag` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`status` VARCHAR(255) NOT NULL,
`created_tinme` DATETIME,
`owner` INTEGER
);

CREATE INDEX `idx_tag__owner` ON `tag` (`owner`);

ALTER TABLE `tag` ADD CONSTRAINT `fk_tag__owner` FOREIGN KEY (`owner`) REFERENCES `user` (`id`) ON DELETE SET NULL;

CREATE TABLE `post_tag` (
`post` INTEGER NOT NULL,
`tag` INTEGER NOT NULL,
PRIMARY KEY (`post`, `tag`)
);

CREATE INDEX `idx_post_tag` ON `post_tag` (`tag`);

ALTER TABLE `post_tag` ADD CONSTRAINT `fk_post_tag__post` FOREIGN KEY (`post`) REFERENCES `post` (`id`);

ALTER TABLE `post_tag` ADD CONSTRAINT `fk_post_tag__tag` FOREIGN KEY (`tag`) REFERENCES `tag` (`id`)

框架基础

关于django的几个模块

参考官方的文档https://docs.djangoproject.com/en/3.1/

  1. Model部分包含

    1.1 models 模型的定义相关使用说明

    1.2 querysets 在model的基础上查看数据以及定义过滤

    1.3 model instances 单个记录,如果CRUD操作

    1.4 Migrations 表结构的调整,迁移等操作

    1.5 Advanced 使用原生sql,聚合,事务,搜索等相关操作

    1.6 其他:数据库优化等操作

  2. View部分包含

    2.1 The basics:url配置,增加缓存以及增加限制等

    2.2 reference 静态文件和404页面处理。请求和回复对象

    2.3 File uploads 文件上传下载存储等

    2.4 class-based views 构建和复用view

    2.5 advanced 导出csv和pdf格式

    2.6 中间件涉及安全和session,缓存等

  3. 模版

    3.1 the basics 基本配置模版和语法

    3.2 逻辑操作

    3.3 高效的配置渲染模版

  4. form表单

    4.1 基础语法使用

    4.2 进阶 与model一起操作

编写一个简单的学员管理系统

先安装虚拟环境

1
2
pip install virtualenvwrapper
pip install virtualenvwrapper-win

创建一个文件夹student-env为虚拟环境目录,并执行初始化虚拟环境 (默认构建在用户下面的ENV环境下。可以做更改,需要新增系统变量)

1
mkvirtualenv student-env

激活虚拟环境

1
workon student-env

停止或者删除环境

1
2
deactivate
rmvirtualenv student-env

安装django1.11

1
pip install django==1.11

创建项目

切换到自己喜欢的目录,构建django项目(注意在虚拟环境进行操作)

1
2
mkdir student_house
django-admin startproject student_sys

创建App(创建模块)(出现错误可参考https://blog.csdn.net/qq_28194001/article/details/86709006)

1
2
cd student_sys
python manage.py startapp student

image-20210117120725798

编写模块代码

VS配置环境参照https://blog.csdn.net/cierlongbu/article/details/103753452

在model.py中编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Student(models.Model):
SEX_ITEMS = [
(1,'男'),
(2,'女'),
(0,'未知')
]
STATUS_ITEMS = [
(0,'申请'),
(1,'通过'),
(2,'拒绝')
]
name = models.CharField(max_length=128, verbose_name='姓名')
sex = models.IntegerField(choices=SEX_ITEMS, verbose_name='性别')
profession = models.CharField(max_length=128, verbose_name='职业')
email =models.EmailField(verbose_name= 'Email')
qq = models.CharField(max_length=128, verbose_name='QQ')
phone = models.CharField(max_length=128, verbose_name='电话')
status = models.IntegerField(choices=STATUS_ITEMS,default=0,verbose_name='审核状态')
created_time = models.DateField(auto_now_add=True,editable=False,verbose_name='创建时间')

def __str__(self):
return '<Student: {}>'.format(self.name)

class Meta:
verbose_name = verbose_name_plural = '学员信息'

在admin.py中编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class StudentAdmin(admin.ModelAdmin):
list_display = ('id','name','sex','profession','email','qq','phone','status','created_time')
list_filter = ('sex','status','created_time')
search_fields = ('name','profession')
fieldsets = (
(None,{
'fields':(
'name',
('sex','prefession'),
('email','qq','phone'),
'status',
)
}),
)

admin.site.register(Student, StudentAdmin)

注册到setting文件中

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
'student',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
1
2
3
4
5
6
7
8
9
LANGUAGE_CODE = 'zh-Hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = True

在项目的根目录下执行:

1
2
3
4
python manage.py makemigreations # 创建数据库迁移文件
python manage.py migrate # 创建表
python manage.py createsuperuser #创建超级用户
python manage.py runserver #启动服务

效果如下:

image-20210117152057036

关于前台页面的开发

在student.py 中

1
2
3
4
# 定义了一个index方法
def index(request):
words = 'World!'
return render(request,'index.html',context={'words':words}) # 在student的templates中寻找index.html,并且上下文内容包含words

注意:在工作空间的json中加入可以解决在django中编写html的问题

1
2
3
4
5
6
7
"files.associations": {
"**/*.html": "html",
"**/templates/**/*.html": "django-html",
"**/templates/**/*": "django-txt",
"**/requirements{/**,*}.{txt,in}": "pip-requirements"
},
"emmet.includeLanguages": {"django-html": "html"}

在创建的templates的index.html中编写

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>学员管理系统</title>
</head>
<body>
Hello {{ words }}!
</body>
</html>

在urls.py中配置映射目录

1
2
3
4
5
6
7
8
from django.conf.urls import url
from django.contrib import admin

from student.views import index
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$',index,name='index') # 匹配所有映射给index函数处理 r代表使用原生字符
]

重新调整

1
2
3
4
5
6
# 定义了一个index方法
def index(request):
# words = 'World!'
# return render(request,'index.html',context={'words':words}) # 在student的templates中寻找index.html,并且上下文内容包含words
students = Student.objects.all() # 获取所有学员信息
return render(request,'index.html',context={'students':students})
1
2
3
4
5
6
7
<body>
<ul>
{% for student in students %}
<li>{{student.name}} - {{student.get_status_display}}</li>
{% endfor %}
</ul>
</body>

get_status_display自动调用带有choices

效果如下:

image-20210117162744822

提交数据(使用form)

创建一个forms.py文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django import forms

from .models import Student

class StudentForm(forms.ModelForm):
# 定义元数据,模型参照student,并且设置显示的字段
class Meta:
model = Student
fields = (
'name','sex','profession',
'email','qq','phone'
)

# 检验QQ必须为数据(示例)
def clean_qq(self):
cleaned_data = self.cleaned_data['qq']
if not cleaned_data.isdigit():
raise forms.ValidationError('必须为数字')
return int(cleaned_data)

修改view.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 定义了一个index方法
def index(request):
# words = 'World!'
# return render(request,'index.html',context={'words':words}) # 在student的templates中寻找index.html,并且上下文内容包含words
students = Student.objects.all() # 获取所有学员信息
# 以下为新增代码
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
# 对于手动创建student 可以利用form直接进行保存
# cleaned_data = form.cleaned_data
# student = Student()
# student.name = cleaned_data['name']
# student.sex = cleaned_data['sex']
# student.email = cleaned_data['email']
# student.profession = cleaned_data['profession']
# student.qq = cleaned_data['qq']
# student.phone = cleaned_data['phone']
# student.save()
form.save()
return HttpResponseRedirect(reverse('index'))
else:
form = StudentForm()

context ={
'students':students,
'form':form
}
return render(request,'index.html',context=context)

页面调整如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<ul>
{% for student in students %}
<li>{{student.name}} - {{student.get_status_display}}</li>
{% endfor %}
</ul>
<hr>
<form action="/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="提交">
</form>
</body>

注意:csrf_token是对安全性进行检验

最终效果如下:

image-20210117170653853

优化

一般将数据操作逻辑封装到model层中

1
2
3
@classmethod
def get_all(cls):
return cls.objects.all()

使用class-based view复用

在views.py中构建以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from django.views import View

## 使用class-based view 复用
class IndexView(View):
template_name = 'index.html'

def get_context(self):
students = Student.get_all()
context ={
'students':students
}
return context

## 匹配get请求
def get(self,request):
context = self.get_context()
form = StudentForm()
context.update(
{
'form':form
}
)
return render(request,self.template_name,context=context)

## 匹配post请求
def post(self,request):
form = StudentForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('index'))
context =self.get_context()
context.update({
'form':form
})
return render(request,self.template_name,context=context)

urls.py中引入:

1
2
3
4
5
6
7
from student.views import index
from student.views import IndexView
urlpatterns = [
url(r'^admin/', admin.site.urls),
## url(r'^$',index,name='index')
url(r'^$',IndexView.as_view(),name='index')
]

注意:as_view 就是对get和post方法的包装

使用中间件

构建一个middlewares.py文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class TimeItMiddleware(MiddlewareMixin):
def process_request(self,request):
strs = "1. 处理requset请求,第一个被调用,"
"处理request中认证头之类"
print(strs)
return
def process_view(self,request,func,*args,**kwargs):
strs = "2. 用于view处理,其中func即是调用的view"
print(strs)
if request.path != reverse('index'):
return None
start = time.time()
#这里调用了view做处理
response = func(request)
costed = time.time() - start
print("process view used %d seconds" % (costed,))
return response

def process_exception(self,request,Exception):
strs = "0. 用来处理全部流程中的异常"
print(strs)
pass

def process_template_response(self,request,response):
strs = "3. 如果处理渲染模板使用了render,就会调用到这里"
print(strs)
return response

def process_response(self,request,response):
strs = "4. 所有的流程执行完后会返回response给浏览器,在返回之前会调用到这里"
print(strs)
return response

加入到setting配置中的中间件中:

1
2
3
4
5
6
7
8
9
10
MIDDLEWARE = [
'student.middlewares.TimeItMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

关于单元测试

参考的文章很多,可以参考https://www.cnblogs.com/fiona-zhong/p/10554197.html