新建一个Environment

新建一个project实际上是用Pycharm创建了一个Django环境

激活虚拟环境

  • 如果没有虚拟环境,需要先执行以下命令,这样会在根目录下生成一个venv文件夹,用于记录安装的第三包和环境。
python -m venv venv
  • 激活虚拟环境
.\venv\Scripts\activate.bat

激活成功路径前会有venv标志符。

  • 安装DJango
pip install django

执行完安装命令后,venv下Lib包就会出现django,Scripts文件夹下也会有django-admin.exe这个文件。

  • 将虚拟环境的包导入requirements.txt文件中。
pip freeze > requirements.txt
  • 以上这个命令执行后,恢复虚拟环境只需要将requirements.txt中的配置重新导入安装就好了,命令如下。
pip install -r requirements.txt

创建Django项目

django-admin startproject mysite

此时的文件路径应该是如下模样的:

最外面的mysite是真正项目的根目录,和venv同一级别

里面的mysite是整个项目的运行相关的模块包,里面的文件和项目的运行息息相关。

  1. __init__.py: Python模块包(package)的标识文件,用于初始化模块包(package)的一些属性和方法。

  2. asgi.py: 它使得 Django 能够被异步 Web 服务器接受和处理,从而使得 Django 项目可以提供异步通信服务。(Web Server Gateway Interface)

  3. setting.py: 这个文件包括了项目的初始化设置,可以针对整个项目进行有关参数配置,比如配置数据库、添加应用等。(主要修改的文件)

  4. url.py: 这个文件用于定义项目的 URL 映射,将请求路径与视图函数对应起来。(主要修改的文件)

  5. wsgi.py: 它使得 Django 能够被 Web 服务器接受和处理,从而使得 Django 项目可以对外提供 Web 服务。(Asynchronous Server Gateway Interface)

启动Django项目

进入项目的根目录(…\DJango_Environment\mysite),执行以下命令。

python manage.py runserver

写出第一个自定义页面

新建一个应用

项目和应用的区别: 一个项目(project)通常包含多个应用(application)。每个application实现一种功能。

django-admin startapp app

执行完以上命令后,会在根目录创建出一个app文件夹。重点关注views.py文件就好了。因为这个文件包含应用程序的视图函数,用于处理用户请求并返回响应。

修改文件

  1. 注册应用(app),修改mysite/mysite/settings.py

最后一行写法的表示:

  • app是app模块包

  • apps是app模块包下的一个apps.py文件

  • AppConfig是apps.py文件下的一个class

  1. 修改mysite/app/views.py
from django.shortcuts import HttpResponse


def index(request):
return HttpResponse("欢迎使用")
  1. 修改mysite/mysite/urls.py
from django.contrib import admin
from django.urls import path
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/',views.index)
]

注意: path(‘index/‘,views.index)中index后面记得加/

重新启动

重新启动项目,并访问127.0.0.1:8000/index

页面响应的几种方式

HttpResponse

def index(request):
return HttpResponse("欢迎使用")

redirect

返回一个重定向

def index(request):
return redirect("https:www.baidu.com")

render

返回一个网页模板

def index(request):
return render(request, 'index.html')

为了正确返回页面,应该在app文件夹下新建templates文件夹(用于存放网页模板)。然后在templates文件夹下新建index.html

如何在网页模板中引用静态资源

新建资源文件夹

在app目录下新建static文件夹,里面再依次新建img,css,js等文件夹,然后再在img里面放置图片资源,css里面放置样式表,js里面放置脚本文件。

引用静态资源

<!DOCTYPE html>
<html lang="en">
<head>
{% load static %}
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" type="text/css" href="/static/css/bootstrap.min.css">
</head>
<body>
<!--引用方式1-->
<img src="/static/img/1.jpeg">
<!--引用方式2-->
<!--前提是要在head里面load-->
<img src="{% static 'img/1.jpeg' %}">
<div class="alert alert-primary" role="alert">
A simple primary alert—check it out!
</div>
<button type="button" class="btn btn-primary btn-lg btn-block">Block level button</button>
<script type="text/javascript" src="/static/jQuery/jquery.slim.min.js"></script>
<script type="text/javascript" src="/static/js/bootstrap.bundle.min.js"></script>
</body>
</html>

更改模板目录

一般情况下模板目录为每个应用下的templates文件夹,有时候我们想要把模板目录放在根目录下,那就需要在根目录下新建一个templates文件夹,然后再修改settings.py。

注意:索引顺序变为先找根目录下的templates,如果找不到在找app下的templates。

动态页面编写

如果只是简单呈现一些静态的资源就太没意思了,网页一般提供交互功能。这时候就需要把数据嵌入网页模板中了。这就涉及到python的数据结构如何在网页模板显示出来。

前提工作

  1. 修改mysite/mysite/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('index/',views.index),
path('tmpl/',views.tmpl)
]
  1. 修改mysite/app/views.py
def tmpl(request):
# 普通变量
name = 'xxx'
# 列表
roles = ['student', 'teacher']
# 字典
user_info = {"name": "Tom", "age": 16, "salary": 1000}
# 列表套字典
data_list = [
{"name": "Tom", "age": 16, "salary": 1000},
{"name": "Jane", "age": 18, "salary": 2000},
{"name": "Jack", "age": 17, "salary": 3000}
]
return render(request, 'tmpl.html', {"v1": name, "v2": roles, "v3": user_info, "v4": data_list})

注意:render的第三个参数为字典,可以将python的数据结构以字典的形式传递给网页模板。

在网页模板中使用变量

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>普通变量</h1>
<div>{{v1}}</div>

<h1>列表</h1>
<div>{{v2}}</div>
<div>{{v2.0}}</div>
<div>{{v2.1}}</div>
<div>
{% for item in v2 %}
<span>{{item}}</span>
{% endfor %}
</div>

<h1>字典</h1>
<div>
{{v3}}
{{v3.name}}
{{v3.age}}
{{v3.salary}}
</div>
<ul>
{% for k,v in v3.items %}
<li>{{k}}={{v}}</li>
{% endfor %}
</ul>

<h1>列表嵌套字典</h1>
{% for item in v4 %}
<div>{{item.name}} {{item.age}} {{item.salary}}</div>
{% endfor %}

<h1>判断语句</h1>
{% if n1 == "Tom" %}
<h1>I am Tom</h1>
{% elif n1 == "Jane" %}
<h1>I am Jane</h1>
{% else %}
<h1>I am nobody</h1>
{% endif %}
</body>
</html>

用户登录实战

  1. 在mysite/mysite/urls.py中增加路径映射
path('login/',views.login)
  1. 修改mysite/app/views.py
def login(request):
if request.method == 'GET':
return render(request, 'login.html')

# 获取用户POST的数据
print(request.POST)
username = request.POST.get('username')
password = request.POST.get('pwd')

if username == 'root' and password == '123':
# 登录成功,应该跳转到其他页面
return redirect('https://www.baidu.com')

# 登录失败也应该返回一个页面, 不过要有提示信息
return render(request, 'login.html', {'error_msg': '用户名或者密码错误'})
  1. mysite/templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户登录</h1>
<form method="post" action="/login/">
{% csrf_token %}
<input type="text" name="username" placeholder="用户名">
<input type="text" name="pwd" placeholder="密码">
<input type="submit" value="提交">
<span style="color:red"> {{ error_msg }} </span>
</form>
</body>
</html>

为什么要在form表单下添加一行{% csrf_token %}呢,这就是为了防止CSRF攻击了,以确保用户的请求来自于正确的网站。

CSRF攻击

session和cookie的配合

  1. 用户登录:用户向服务器发送用户名和密码进行登录。

  2. 服务器验证通过后,创建一个Session,并将Session ID存储在Cookie中,然后将Cookie返回给用户。

  3. 用户随后的每一次请求,都会通过Cookie将Session ID传回服务器。

  4. 服务器收到Session ID后,找到前期保存的数据,确定用户的身份。

CSRF的原理

CSRF全称为:Cross-site request forgery。

CSRF(跨站请求伪造)利用了已经存储在浏览器中的cookie进行身份伪造。攻击者通过在攻击网站上插入恶意代码,当用户访问该网站时,恶意代码会向目标网站发送一个请求,该请求会携带用户的cookie信息,从而通过身份验证,进而进行攻击。

示例

  1. 图片资源中嵌入恶意的转账操作(GET请求)
<img src="http://127.0.0.1:9000/transfer?to_user=hacker&money=2000" />
  1. 自动提交表单(POST请求)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<iframe style="display: none;" name="csrf-frame"></iframe>
<form method="post" action="http://127.0.0.1:9000/transfer" target="csrf-frame" id="csrf-form">
<input type="hidden" name="to_user" value="hacker">
<input type="hidden" name="money" value="10000">
<input type="submit" value="submit">
</form>
<script>document.getElementById("csrf-form").submit()</script>
</body>
</html>

防御CSRF

  1. 尽量使用POST请求

  2. 加入验证码(通过加入验证码可以尽可能保证是用户的真实操作,除非黑客可以破解验证码)

  3. 验证Referer(HTTP的Referer头字段可以告诉服务器请求的来源,即该请求是从哪个页面或哪个API发送的。通过检查Referer头字段,服务器可以判断请求的来源,从而防止攻击。不过Referer也可能被篡改。

  4. Anti CSRF Token。(可靠性最高)

    • 在form表单或者头信息中传递token

    • token存储在服务端中

    • 服务端通过拦截器验证有效性

    • 校验失败的拒绝请求

第四个方案的原理:增强服务器的功能,服务端每次都将随机生成一个唯一的token放在session中,正确请求的用户网页中也有一个隐藏的相匹配的token。

请求时服务器验证请求是否存在token且token是否正确,只有正确的token才能有正确的响应。token每次用完即销毁,保证随机和安全。

ORM

  • 定义:ORM是对象关系映射(Object Relational Mapping,ORM)的缩写,是一种将数据库中的数据映射到Python对象中的技术,它使得开发者可以通过使用Python代码来操作数据库,而无需直接编写SQL语句。

  • 详细介绍:ORM通过将数据库表映射到Python类来实现访问数据库。在ORM中,每个数据库表都对应一个Python类,每个表的字段都对应一个类的属性。ORM提供了一些方法来查询和修改数据库中的数据,例如查询、插入、更新和删除等。具体实现上,ORM通常会使用一些数据库驱动程序来连接数据库,例如MySQLclient、psycopg2等。ORM通过这些驱动程序连接到数据库,并使用SQL语句与数据库进行交互。同时,ORM还会提供一些高级功能,例如查询优化、数据验证等,以提升开发效率和代码质量。

  • 使用前提,安装mysqlclient这个数据库驱动程序。

ORM有什么用

  • 创建、修改、删除数据库的表。【无法创建数据库】

  • 操作表中的数据(不用写SQL语句)。

ORM配置

  1. 新建django数据库

  2. 配置settings.py

DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "django", # 数据库名字
"USER": "root",
"PASSWORD": "123456",
"HOST": "127.0.0.1",
"PORT": "8888",
}
}

注意:MySQL默认端口号为3306,可以在C:\Program Files\MySQL\MySQL Server 8.0\my.ini修改。

ORM操作

创建表

修改mysite/app/models.py

class UserInfo(models.Model):
name = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField()


"""
create table app_userinfo(
id bigint auto_increment primary key,
name varchar(32),
password varchar(64),
age int
)
"""

此时还需要执行以下命令,表才能创建完毕。

python manage.py makemigrations
python manage.py migrate

第一条的作用会在mysite/app/migrations文件夹下生成迁移文件,第二条就是执行迁移操作。(在执行makemigrations命令时,Django会自动将应用程序的模型类转换为迁移文件,并保存到该文件夹中。在执行migrate命令时,Django会读取该文件夹中的迁移文件,并将其应用到数据库中,以实现数据库结构的更新。)

注意:此时不仅生成了app_userinfo表,还生成了其他表。这是因为Django会为在settings.py中注册的app所拥有的models都创建表。

新增表

  1. 修改mysite/app/models.py,添加两个模板类
class Department(models.Model):
title = models.CharField(max_length=16)


class Role(models.Model):
caption = models.CharField(max_length=16)
  1. 执行两条命令
python manage.py makemigrations
python manage.py migrate

同理:表的修改和字段的修改都可以通过先修改models.py文件,再执行那两条命令就可以了。

增加字段:如果是增加字段

  • 给新增的字段一个默认值。size = models.IntegerField(default=2)

  • 允许为空。size=models.IntegerField(null=True,blank=True)

增删查改

  1. 修改mysite/mysite/urls.py
path('orm/',views.orm)
  1. 修改mysite/app/views.py
from app import models
def orm(request):
# 测试ORM操作
# 新建数据 insert into app_department(title) values("销售部")
models.Department.objects.create(title="销售部")
models.Department.objects.create(title="IT部")
models.Department.objects.create(title="质检部")
models.UserInfo.objects.create(name="Tom",password="123",age=19)
models.UserInfo.objects.create(name="Jane", password="123", age=19)
models.UserInfo.objects.create(name="Jack", password="123", age=19)

# 删除(先筛选后删除)
models.UserInfo.objects.filter(id=1).delete()
models.Department.objects.all().delete()


# 查找
# data_list = [对象、对象、对象]
data_list = models.UserInfo.objects.all()
print(data_list)
for obj in data_list:
print(obj.id,obj.name,obj.password,obj.age)

# 对查找使用过滤器
row_obj = models.UserInfo.objects.filter(id=2).first()
print(row_obj.id,row_obj.name,row_obj.password,row_obj.age)

# 更新数据
models.UserInfo.objects.all().update(password=999)

# 对更新使用筛选器
models.UserInfo.objects.filter(name="Tom").update(age=100)
return HttpResponse("成功")

用户管理程序

展示用户列表

  • url

  • 函数

    • 获取所有用户信息
  • HTML渲染

path('info/list/',views.info_list)
def info_list(request):
data_list = models.UserInfo.objects.all()
print(data_list)
return render(request, "info_list.html", {"data_list": data_list})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>INFO列表</h1>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
{% for obj in data_list %}
<tr>
<td>{{obj.id}}</td>
<td>{{obj.name}}</td>
<td>{{obj.password}}</td>
<td>{{obj.age}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>

添加用户

  • url

  • 函数

    • GET,看到页面,输入内容

    • POST,提交,写入数据库

  • HTML渲染

path('info/add',views.info_add)
def info_list(request):
data_list = models.UserInfo.objects.all()
print(data_list)
return render(request, "info_list.html", {"data_list": data_list})


def info_add(request):
if request.method == "GET":
return render(request, "info_add.html")

# 获取用户提交的数据
user = request.POST.get("user")
pwd = request.POST.get("pwd")
age = request.POST.get("age")

# 添加到数据库
models.UserInfo.objects.create(name=user,password=pwd,age=age)

# 自动跳转到列表页面
return redirect("/info/list")
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加用户</h1>
<form method="post" action="/info/add/">
{% csrf_token %}
<input type="text" name="user" placeholder="用户名">
<input type="text" name="pwd" placeholder="密码">
<input type="text" name="age" placeholder="年龄">
<input type="submit" value="提交">
</form>
</body>
</html>

删除用户

  • url

  • 函数

http://127.0.0.1:8000/info/delete/?nid=2

path('info/delete/',views.info_delete)
def info_delete(request):
nid = request.GET.get('nid')
models.UserInfo.objects.filter(id=nid).delete()
# 自动跳转到列表页面
return redirect("/info/list/")

修改info_list.html中的表格

<table border="1">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>年龄</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for obj in data_list %}
<tr>
<td>{{obj.id}}</td>
<td>{{obj.name}}</td>
<td>{{obj.password}}</td>
<td>{{obj.age}}</td>
<td>
<a href="/info/delete/?nid={{obj.id}}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>

用户修改

  • url

  • 函数

    • GET,看到页面,输入内容

    • POST,提交,写入数据库

  • HTML渲染

http://127.0.0.1:8000/info/update/?nid=3

path('info/update/',views.info_update)
def info_update(request):
if request.method == 'GET':
nid = request.GET.get('nid')
row_obj = models.UserInfo.objects.filter(id=nid).first()
return render(request, 'info_update.html', {"row_obj": row_obj})

# 如果是POST请求
id_num = request.POST.get('id')
user = request.POST.get('user')
pwd = request.POST.get('pwd')
age = request.POST.get('age')

# 数据库更新
models.UserInfo.objects.filter(id=id_num).update(name=user, password=pwd, age=age)
return redirect('/info/list/')
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>修改用户</h1>
<form method="post" action="/info/update/">
{% csrf_token %}
<span>编号:</span><input type="text" name="id" value="{{row_obj.id}}" readonly="readonly"><br>
<span>姓名:</span><input type="text" name="user" value="{{row_obj.name}}"><br>
<span>密码:</span><input type="text" name="pwd" value="{{row_obj.password}}"><br>
<span>年龄:</span><input type="text" name="age" value="{{row_obj.age}}"><br>
<input type="submit" value="提交">
</form>
</body>
</html>

为了方便更改,还需要修改列表页。在操作栏增加一个a标签。

<a href="/info/update/?nid={{obj.id}}">更改</a>

员工管理系统

新建项目和应用

django-admin startproject emp_manage
django-admin startapp app

注意:记得在settings.py中注册app。

创建表结构

在models.py修改

from django.db import models


class Department(models.Model):
"""部门表"""
# id = models.BigAutoField(verbose_name="ID",primary_key=True)
title = models.CharField(verbose_name='标题', max_length=32)


class UserInfo(models.Model):
"""员工表"""
name = models.CharField(verbose_name="姓名", max_length=16)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄")
account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name="入职时间")
gender_choices =(
(1,"男"),
(2,"女")
)
gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
# 有约束
# - to, 与哪张表关联
# - to_field, 表中的那一列关联
# - 自动生成depart_id,自动带上id的原因是外键为id
# 1.部门被删除,级联删除
# depart = models.ForeignKey(to="Department", to_field="id",on_delete=models.CASCADE)
# 2. 部门被删除,置空
depart = models.ForeignKey(to="Department", to_field='id',null=True,blank=True,on_delete=models.SET_NULL)

生成数据库

  • 创建数据库emp_manage

  • 修改settings.py

DATABASES = {
'default':{
'ENGINE': 'django.db.backends.mysql',
'NAME': 'emp_manage', # 数据库名称
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': 3306
}
}
  • 执行命令
python manage.py makemigrations
python manage.py migrate