DJango入门 发表于 2023-07-23 | 更新于 2023-07-24
| 字数总计: 5.2k | 阅读时长: 23分钟 | 阅读量:
新建一个Environment 新建一个project实际上是用Pycharm创建了一个Django环境
激活虚拟环境
如果没有虚拟环境,需要先执行以下命令,这样会在根目录下生成一个venv文件夹,用于记录安装的第三包和环境。
.\venv\Scripts\activate.bat
激活成功路径前会有venv
标志符。
执行完安装命令后,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
是整个项目的运行相关的模块包,里面的文件和项目的运行息息相关。
__init__.py: Python模块包(package)的标识文件,用于初始化模块包(package)的一些属性和方法。
asgi.py: 它使得 Django 能够被异步 Web 服务器接受和处理,从而使得 Django 项目可以提供异步通信服务。(Web Server Gateway Interface)
setting.py: 这个文件包括了项目的初始化设置,可以针对整个项目进行有关参数配置,比如配置数据库、添加应用等。(主要修改的文件
)
url.py: 这个文件用于定义项目的 URL 映射,将请求路径与视图函数对应起来。(主要修改的文件
)
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
文件就好了。因为这个文件包含应用程序的视图函数,用于处理用户请求并返回响应。
修改文件
注册应用(app),修改mysite/mysite/settings.py
最后一行写法的表示:
修改mysite/app/views.py
from django.shortcuts import HttpResponsedef index (request ): return HttpResponse("欢迎使用" )
修改mysite/mysite/urls.py
from django.contrib import adminfrom django.urls import pathfrom app import viewsurlpatterns = [ 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 > <img src ="/static/img/1.jpeg" > <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的数据结构如何在网页模板显示出来。
前提工作
修改mysite/mysite/urls.py
urlpatterns = [ path('admin/' , admin.site.urls), path('index/' ,views.index), path('tmpl/' ,views.tmpl) ]
修改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 >
用户登录实战
在mysite/mysite/urls.py中增加路径映射
path('login/' ,views.login)
修改mysite/app/views.py
def login (request ): if request.method == 'GET' : return render(request, 'login.html' ) 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' : '用户名或者密码错误' })
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的配合
用户登录:用户向服务器发送用户名和密码进行登录。
服务器验证通过后,创建一个Session,并将Session ID存储在Cookie中,然后将Cookie返回给用户。
用户随后的每一次请求,都会通过Cookie将Session ID传回服务器。
服务器收到Session ID后,找到前期保存的数据,确定用户的身份。
CSRF的原理 CSRF全称为:Cross-site request forgery。
CSRF(跨站请求伪造)利用了已经存储在浏览器中的cookie进行身份伪造。攻击者通过在攻击网站上插入恶意代码,当用户访问该网站时,恶意代码会向目标网站发送一个请求,该请求会携带用户的cookie信息,从而通过身份验证,进而进行攻击。
示例
图片资源中嵌入恶意的转账操作(GET请求)
<img src ="http://127.0.0.1:9000/transfer?to_user=hacker&money=2000" />
自动提交表单(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
尽量使用POST请求
加入验证码(通过加入验证码可以尽可能保证是用户的真实操作,除非黑客可以破解验证码)
验证Referer(HTTP的Referer头字段可以告诉服务器请求的来源,即该请求是从哪个页面或哪个API发送的。通过检查Referer头字段,服务器可以判断请求的来源,从而防止攻击。不过Referer也可能被篡改。
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配置
新建django
数据库
配置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都创建表。
新增表
修改mysite/app/models.py,添加两个模板类
class Department (models.Model): title = models.CharField(max_length=16 ) class Role (models.Model): caption = models.CharField(max_length=16 )
执行两条命令
python manage.py makemigrations python manage.py migrate
同理
:表的修改和字段的修改都可以通过先修改models.py
文件,再执行那两条命令就可以了。
增加字段
:如果是增加字段
给新增的字段一个默认值。size = models.IntegerField(default=2)
。
允许为空。size=models.IntegerField(null=True,blank=True)
。
增删查改
修改mysite/mysite/urls.py
修改mysite/app/views.py
from app import modelsdef orm (request ): 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 = 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("成功" )
用户管理程序 展示用户列表
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 >
删除用户
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}) 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 modelsclass Department (models.Model): """部门表""" 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) 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