Django学习

引言:

Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。

使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 本身基于 MVC 模型,即 Model(模型)+ View(视图)+ Controller(控制器)设计模式,MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。

Python 加 Django 是快速开发、设计、部署网站的最佳组合。

Django 的 MTV 模式本质上和 MVC 是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django 的 MTV 分别是指:

  • M 表示模型(Model):编写程序应有的功能,负责业务对象与数据库的映射(ORM)。
  • T 表示模板 (Template):负责如何把页面(html)展示给用户。
  • V 表示视图(View):负责业务逻辑,并在适当时候调用 Model和 Template。

图解以及用户操作详细流程图

  • 1.安装:pip install django

  • 2.创建项目:

    django中会有一些默认的文件和文件夹

    添加django安装位置到环境变量(我的是D:\Program Files\Anaconda\Scripts\django-admin.exe)

    • 初始化:django-admin startproject 项目名称

      目录结构:

      D:.
      │ manage.py 【项目管理,启动项目、创建app、数据管理】【不要动】【常常用】
      └─Django_Manage_Web

      asgi.py          【接收网络请求】 【不要动】
      settings.py   【项目配置,比如连接数据库的库名、密码】【经常操作】
      urls.py          【URL和函数的对应关系】 【经常操作】
      wsgi.py         【接收网络请求】 【不要动】
      __init__.py

      tips: windows下打印目录结构,使用Windows PowerShell输入tree /f 输出所有文件夹以及文件

    • 一个项目下可以有多个app

      这个项目只有一个app

      创建app命令:python manage.py startapp app01

      ├─app01
      │ │ admin.py 【固定,不动】 django默认提供了admin后台管理
      │ │ apps.py 【固定,不动】 app启动类
      │ │ models.py 【重要】 对数据库操作
      │ │ tests.py 【固定,不动】单元测试
      │ │ views.py 【重要】函数
      │ │ init.py
      │ │
      │ └─migrations 【固定,不动】数据库变更记录
      init.py

      └─Django_Manage_Web

  • 3.运行项目

    • 注册app (setting.py)

      在Django_Manage_Web(项目名称)/settings.py中

      1
      2
      3
      4
      5
      6
      7
      8
      9
      INSTALLED_APPS = [
      'django.contrib.admin',
      'django.contrib.auth',
      'django.contrib.contenttypes',
      'django.contrib.sessions',
      'django.contrib.messages',
      'django.contrib.staticfiles',
      'app01.apps.App01Config' # 写上app的App01Config类引用
      ]
    • 编写URL和View的对应关系 (urls.py)

    1
    2
    3
    4
    5
    from app01 import views
    # 网址输入www.com/admin --> 找views里面index函数
    urlpatterns = [
    path('admin/', views.index)
    ]
    • 编写views函数 (views.py)

      1
      2
      3
      4
      from django.shortcuts import render, HttpResponse

      def index(request):
      return HttpResponse('欢迎来到Django的世界,Hello World!')
    • 启动项目

      python manage.py runserver

      访问浏览器 看到页面效果


  • 4.开发过程

    • 编写HTML

      编写views函数(views.py)

      1
      2
      3
      4
      def user_list(request):
      # 根据app的注册顺序,逐一去他们的templates目录中找
      # 并不是只是在它自己的templates中找user_list.html
      return render(request,'user_list.html')

      编写add01/templates/user_list.html 此时访问对应URL返回HTML页面内容

      But if add the DIRS: [os.path.join(BASE_DIR, 'templates')] in settings.py

      优先在项目根目录下的templates下找

    • 静态文件(img,css,js…)

      存放在app01/static

      html中引用时:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      {% load static %}
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}">
      </head>
      <body>
      <h1>用户列表</h1>
      <script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
      <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.js' %}"></script>
      </body>
      </html>
    • 模板语法

      在views.py中传参

      1
      2
      3
      4
      def user_list(request):
      name = "学生"
      role = ['班长','团支书','心理委员']
      return render(request,'user_list.html',{"name": name,"r": role})

      在html中接收name

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      <h1>{{ name }}列表</h1>
      <!--如果是列表,通过.索引来获取-->
      <text>{{ r.0 }}</text>
      <text>{{ r.1 }}</text>
      <text>{{ r.2 }}</text>
      <!--如果想要循环取值-->
      {% for i in r %}
      <span>{{ i }}</span>
      {% endfor %}
      <!--if条件判断-->
      {% if name == '老师' %}
      <span>老师好</span>
      {% else %}
      <span>这是学生表</span>
      {% endif %}

      其实就是python的语法,写法上加了{% %}

      视图函数的render内部:

      1.读取含有模板语法的HTML文件

      2.内部进行渲染,得到只包含HTML标签的字符串

      3.将结果返回给浏览器

  • 5.请求和响应

    • 调接口

      views.py中获取数据传给html

      1
      2
      3
      4
      5
      def connect_data(req):
      import requests
      res = requests.get('http://www.baidu.com') # 写一个有效的接口地址
      data = res.json()
      return render(req,'user_list.html',{"data": data})

      html中进行接收

    • req对象

      views.js中默认的一个参数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      # req是一个对象,封装了用户发送来的所有请求相关的数据
      # 1.获取请求方式 GET/POST
      print(req.method)

      # 2.在URL上传递值 /admin/data/?n1=12&b1=34
      print(req.GET)

      # 3.在请求体中提交数据
      print(req.POST)

      GET传参是明面上的写在URL中的,POST传参是悄悄传的

  • 6.案例:用户登录

    • views.js

      1
      2
      3
      4
      5
      6
      7
      8
      def login(req):
      if req.method == 'GET':
      return render(req,'user_login.html')
      else:
      print(req.POST)
      username = req.POST.get('username')
      pwd = req.POST.get('password')
      return HttpResponse('登录成功')
    • user_login.html

      1
      2
      3
      4
      5
      6
      <form method="post" action="/login/">
      {% csrf_token %} # Django需要加上配置
      <input type="text" name="username" placeholder="用户名"/>
      <input type="password" name="password" placeholder="密码"/>
      <input type="submit" placeholder="提交">
      </form>
    • urls.py中注册路由

    • 访问登录,点击提交,报错403

      1
      2
      3
      4
      5
      6
      Forbidden (403)
      CSRF verification failed. Request aborted.

      You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.

      If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for “same-origin” requests.

      这么写在Flask中是可以成功登录的,但是由于Django有CRSF_TOKEN校验,所以需要在html的表单内加上{% csrf_token %}

    • 加上CSRF配置后

      登录成功,print<QueryDict: {'csrfmiddlewaretoken': ['nvFM5J854IPPbNq2WZLZs2s3q5L8kCnFosjDVERLLFlXmCwSQtObcdIK8FWL5LDk'], 'username': ['123'], 'password': ['123']}>


  • 7.连接数据库Mysql

    • python使用pymsql连接数据库

    • Django开发操作数据库更简单,内部提供了ORM框架

      最新版的Django对pymsql的支持性不好,安装第三方库mysqlclient效果更佳

      Windows系统下直接pip install mysqlclient会有报错

      解决方式:

      • 镜像下载对应的wheel文件 pip install wheel文件存储地址
  • 8.ORM

    创建、修改、删除数据库中的表以及操作表中的数据(不用写SQL

    • 启动MySQL服务(此处我使用Navicat Premium)

      create database django_web default charset utf8;创建django_web数据库

    • Django连接数据库

      • 在setting.py中进行配置:

        不同的数据库,配置不一样。Mysql配置如下:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        DATABASES = {
        'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_web',
        'USER': 'root',
        'PASSWORD': 'XXX',
        'HOST': '127.0.0.1',
        'PORT': 3306
        }
        }
    • Django操作表

      • 1.创建表

      • 在models.py中通过类创建表

        1
        2
        3
        4
        5
        6
        7
        8
        from django.db import models

        # Create your models here.
        class UserInfo(models.Model):
        name = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
        age = models.IntegerField(default=22) # 默认值为22
        data = models.IntegerField(null=True, blank=True) # 默认为空
      • 执行命令:

        python manage.py makemigrations

        python manage.py migrate

      • 在数据库中show tabels可以看到新建的app01_userinfo

    • Django增删改查

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # 1.增加数据 insert into app01_department(title) values('销售部')
      Department.objects.create(title="数据库产品中心")
      # 2.删除数据
      Department.objects.filter(id=1).delete() # 删掉id为1的一行
      Department.objects.all().delete() # 删除Department中的所有数据
      # 3.查询数据
      # data_list = [对象,对象,对象] QuerySet类型
      data_list = Department.objects.all()
      for obj in data_list:
      print(obj.title)
      print(data_list) # <QuerySet [<Department: Department object (1)>, <Department: Department object (2)>]>
      return HttpResponse('成功')

      .all() 和 .filter() 得到的都是QuerySet类型的数据,就是列表里面套对象

      如果只取第一行的数据

      通过Department.objects.filter(id=1).first()的方式

      1
      2
      # 4.更新数据
      Department.objects.filter(id=1).update(title="CVM产品中心")

开发阶段

1.部门管理

体验,最原始的方法来做。

Django中提供Form和ModelForm组件(方便)

  • 创建数据库表结构(models.py) 运行makemigrations和migrate 生成app02_department表

  • 引入静态文件static 创建空的templates文件夹

  • 注册路由(urls.py) 编写views.py先默认return render(req,'depart_list.html')

  • 初步布局UI,编写depart_list.html

  • 增删改查功能实现(urls –> views –> html)

    • html传值给views:

      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
      方式一:http://localhost:8000/depart/1/edit
      urls中path('/dapart/<int:nid>/edit',view.depart_edit)
      views中
      def depart_edit(req,nid):
      # 可以拿到nid
      return
      html中href='/depart/{{obj.id}}/edit'

      方式二:http://localhost:8000/depart/delete?id=1 【GET方式】
      urls中path('/dapart/delete',view.depart_delete)
      views中
      def depart_delete(req):
      req.GET.get('id') # 可以拿到id
      html中href='/depart/delete?id={{obj.id}}'

      方式三:http://localhost:8000/depart/add
      urls中path('/dapart/add',view.depart_add)
      views中
      def depart_add(req):
      req.POST.get('title') # 可以拿到title
      html中
      <form method="post">
      {% csrf_token %}
      <div class="form-group">
      <label>部门名:</label>
      <input type="text" class="form-control" name="title">
      </div>
      <button type="submit" class="btn btn-primary">提交</button>
      </form>
    • views传data给html

      1
      2
      3
      4
      views中
      def method(req):
      return render(req,html,{'data':data})
      html中{{data}}

2.模板继承

layout.html

1
2
3
4
5
6
<!--导航-->
<nav class="navbar navbar-default">
</nav>
<div>
{% block content %}{% endblock %}
</div>

extend_layout.html

1
2
3
4
5
6
7
{% extends 'layout.html' %}

{% block content %}
<h1>继承模板</h1>
{% endblock %}

同样的,还有{% block js %},{% block css %}都可以这样引入

3.用户管理

引入Django组件的方式,不采用原始的方法

在views.py中显示转化的写法:

- 日期时间数据格式转化:`obj.create_time.strftime('%Y-%m-%d')`

1
2
3
4
gender_choices = (
(1, "男"),
(2, '女')
)
- 性别显示转化`obj.get_gender_display()`
1
2
depart = models.ForeignKey(to='Department', to_field='id', on_delete=models.CASCADE)
User表中字段名为depart_id 存部门id ; Depart表中两个字段id和title
- 部门id显示转化为部门名称:`obj.depart.title`

在html中,不能写括号,所以去掉括号即可。需要注意 时间日期字段转化写法变为obj.create_time|date:'Y-m-d'

3.1 新建用户
  • 原始方法(麻烦)
    部门,性别应该为可选的,在views.py中

    1
    2
    3
    4
    5
    context = {
    'gender_choices': models.UserInfo.gender_choices,
    'depart': models.Department.objects.all()
    }
    return render(req,'user_add.html',context)

    在html中,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <label>性别:</label>
    <section class="form-control">
    {% for i in gender_choices %}
    <option value="{{ i.0 }}">{{ i.1 }}</option>
    {% endfor %}
    </section>
    <label>所在部门:</label>
    <section class="form-control">
    {% for i in depart %}
    <option value="{{ i.id }}">{{ i.title }}</option>
    {% endfor %}
    </section>

    原始方法就是像部门管理那样的写法,存在问题:

    1.用户提交数据没有校验

    2.输入错误,没有提示

    3.页面上每一个字段都需要重新写一遍

    4.关联的数据,需要手动获取

  • Django组件

    • Form组件(小简便)

      可以解决原始方法的前三个问题。

    • ModelForm组件(最简便)

3.2初识Form

在views.py中

1
2
3
4
5
6
class MyForm(Form):
user.forms.forms.CharField(widget=forms.Input)
def user_add(req):
if req.method == 'GET':
form = MyForm()
return render(req,'user_add.html',{'form':form})

在html中使用:

1
2
3
4
5
6
# html中使用
<form>
{% for field in form %}
{{ field }}
{% endfor %}
</form>
3.3初识ModelForm

在models.py中

1
2
3
4
5
6
7
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_length=10,decimal_places=2,default=0,max_digits=5)
create_time = models.DateTimeField(verbose_name='入职时间')

在views.py中

1
2
3
4
5
6
7
8
from django import forms
class MyForm(forms.ModelForm):
class Meta:
model = models.UserInfo # 继承models中的UserInfo
fields = ['name','password']
def add(req):
form = MyForm()
return render(req,'html',{'form':form})

4.ModelForm方式的新增用户

在html中使用:

1
2
3
4
5
{% for fields in form %}
<label>{{ fields.label }}</label>
{{ fields }}
<span style="color:red">{{ fields.errors.0 }}</span><br/> <!--fields.errors得到的是一个错误列表-->
{% endfor %}

页面效果:

改写样式:

在veiws.py的UserModelForm类中写:

1
2
3
4
5
6
改写初始化的样式
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
for name, field in self.fields.items():
print("field:---",field)
field.widget.attrs = {'class': 'form-control'}

print()打印出来的field如下:

改写之后的页面样式:

  • 编辑用户

    同样的方式,在views.py中加上两行

    1
    2
    data_list = models.UserInfo.objects.filter(id=nid).first()
    form = UserModelForm(instance=data_list)

所有变更数据库结构的操作,都需要重新执行makemigrations migrate

  • 靓号管理

    1
    2
    3
    4
    5
    手机号格式校验
    mobile = forms.CharField(
    label='手机号',
    validators=[RegexValidator(r'^1[3-9]\d{9}$','手机号格式错误')]
    )

models.PrettyNum.objects.filter(id=12) # 等于1
models.PrettyNum.objects.filter(id__gt=12) # 大于12
models.PrettyNum.objects.filter(id__gte=12) # 大于等于12
models.PrettyNum.objects.filter(id__lt=12) # 小于12
models.PrettyNum.objects.filter(id__lte=12) # 小于等于12

1
2
3
4
5
6
7
8
9
10
11
  
- 靓号搜索

```python
def mobile_list(req):
data_dict = {}
value = req.GET.get('q')
if value:
data_dict['mobile__contains'] = value
data_list = models.PrettyNum.objects.filter(**data_dict).order_by('-level')
return render(req,'mobile_list.html',{'data_list':data_list})

  • 靓号列表分页器

    在views.py中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    page = 1
    page_size = 10
    start = (page -1) * page_size
    end = page * page_size
    data_list = models.PrettyNum.objects.filter(**data_dict).order_by('id')[start:end]

    # 页码
    page_str_list = []
    for i in range(1,21):
    ele = '<li><a href="?page={}">{}</a></li>'.format(i,i)
    page_str_list.append(ele)
    page_string = ''.join(page_str_list)

    在mobile_list.html中

    1
    2
    3
    <ul class="pagination">
    {{ page_string }}
    </ul>

    此时浏览器中的页面为:

    需要导入mark_safe

    1
    2
    from django.utils.safestring import mark_safe
    page_string = mark_safe(''.join(page_str_list))

5.cookie和session

6.中间件

在app02/新建middleware包/auth.py

编写中间件函数

1
2
3
4
5
6
7
8
9
10
11
12
13
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class M1(MiddlewareMixin):
def process_request(self, request):
# 如果方法中没有返回值(返回None),继续向后走
# 如果有返回值 HttpResponse、render、redirect
print("M1.process_request")
return HttpResponse("无权访问")

def process_response(self, request):
print("M1.process_request")
return response

中间件是栈结构,先进后出

M1. process_request M2. process_request M2. process_response M1. process_response

7.图片验证码

pip install pillow

引入字体,随机数在白底图片上输出,即为验证码图片。

见utils/code.py和views/account.py的image_code方法

8.Ajax

浏览器向网站发送请求时:URL 和 表单的形式提交。

  • GET
  • POST

特点:页面刷新。

除此之外,也可以基于Ajax向后台发送请求(偷偷的发送请求,页面不会刷新,通过前端的JS实现)。

  • 依赖jQuery

  • 编写ajax代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $.ajax({
    url:"发送的地址",
    type:"get",
    data:{
    n1:123,
    n2:456
    },
    success:function(res){
    console.log(res);
    }
    })
  • GET请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $.ajax({
    url: '/task/ajax/',
    type: "get",
    data: {
    n1: 123,
    n2: 456
    },
    success: function (res) {
    console.log(res);
    }
    })
    1
    2
    3
    4
    5
    from django.shortcuts import render, HttpResponse

    def task_ajax(request):
    print(request.GET)
    return HttpResponse("成功了")
  • POST请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $.ajax({
    url: '/task/ajax/',
    type: "get",
    data: {
    n1: 123,
    n2: 456
    },
    success: function (res) {
    console.log(res);
    }
    })
    1
    2
    3
    4
    5
    6
    7
    8
    9
    from django.shortcuts import render, HttpResponse
    from django.views.decorators.csrf import csrf_exempt


    @csrf_exempt
    def task_ajax(request):
    print(request.GET)
    print(request.POST)
    return HttpResponse("成功了")
  • 关闭绑定事件

    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
    {% extends 'layout.html' %}

    {% block content %}
    <div class="container">
    <h1>任务管理</h1>

    <h3>示例1</h3>
    <input id="btn1" type="button" class="btn btn-primary" value="点击"/>

    </div>
    {% endblock %}

    {% block js %}
    <script type="text/javascript">
    $(function () {
    // 页面框架加载完成之后代码自动执行
    bindBtn1Event();

    })

    function bindBtn1Event() {
    $("#btn1").click(function () {
    $.ajax({
    url: '/task/ajax/',
    type: "post",
    data: {
    n1: 123,
    n2: 456
    },
    success: function (res) {
    console.log(res);
    }
    })
    })
    }

    </script>
    {% endblock %}
  • ajax请求的返回值
    一般都会返回JSON格式。

  • 获取输入框的值

    可以$('#id').val(),当输入框比较多的时候,可以将输入框都放到表单form中,通过data:$('#form3').serialize()获取到所有输入框的数据

9.图表

  • highchart,国外的
  • echarts,国内百度开源的

使用方式和前端一样

10.media

注意:就目前而言,所有的静态文件都只能放在static目录。

在django的开发过程中两个特殊的文件夹:

  • static,存放静态文件的路径,包括:css,js,项目图片
  • media,用户上传的数据

开启meida的方式:

第一步:在urls.py中

1
2
3
4
5
6
7
8
from django.urls import path, re_path
from django.views.static import serve
from django.conf import settings

urlpatterns = [
# path('admin/', admin.site.urls),
re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT},name='media')
]

第二步:在settings.py中

1
2
3
import os
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

配置开启之后,可以在浏览器中直接访问media/1.png访问到资源


资源推荐:

  • python基础

    1
    https://www.bilibili.com/video/BV1m54ylr7zE
  • 并发编程(进程线程协程)

    1
    2
    3
    4
    5
    6
    # 进程线程 资源还在
    https://www.bilibili.com/video/BV1Ev411G7i3?spm_id_from=333.999.0.0


    # 不建议小白学(协程) 资源还在
    https://www.bilibili.com/video/BV1NA411g7yf?spm_id_from=333.999.0.0
  • MySQL数据库

    1
    2
    # 2021年的 资源还在
    https://www.bilibili.com/video/BV15R4y1b7y9?spm_id_from=333.999.0.0
  • django开发知识点

    1
    2
    https://www.bilibili.com/video/BV1zE411x7LG
    https://www.bilibili.com/video/BV1JE411V7xk
  • 企业级项目开发

    1
    2
    #任务管理平台: 资源还在
    https://www.bilibili.com/video/BV1uA411b77M
  • 进阶项目

    组件化,优化代码

    1
    2
    # 资源不在了
    https://space.bilibili.com/283478842/channnel/detail?cid=91596&ctype=0
  • 前后端分离项目:django + drf框架 + vue.js

    UP主:秃头统治地球

    1
    2
    - drf框架 资源还在 restful
    https://www.bilibili.com/video/BV1ZE411j7RK
  • 微信小程序 + Django + drf框架

    1
    2
    # 资源不在了
    https://www.bilibili.com/video/BV1jC4yls7QD?spm_id_from=333.999.0.0