Django小白开发指南

文章目录

    • HTTP协议
    • socket实现一个web服务器
    • WSGI实现一个web服务器
    • WSGI实现支持多URL的web服务器
    • WSGI实现图片显示的web服务器
    • MVC && MTV
      • 1.MVC
      • 2.MTV
      • 3.总结
    • 一、创建Django项目
      • 1.创建项目
      • 2.创建app
      • 3.第一次django 请求
    • 二、模板
      • 1.配置settings.py
      • 2.模板语法
      • 3.继承模板
    • 三、models模型
      • 1.常用字段&属性
    • 四、设置数据库
      • 1.设置mysql库
      • 2.mysqldb替换mysql
      • 3.数据迁移
    • 五、URL路由匹配
      • 1.正则匹配
      • 2.路由匹配&传参
      • 3.管理多个URL文件
        • 1.去除重复url路径
        • 2.视图传参数
    • 六、View视图
      • 1.HttpRequest对象
      • 2.HttpResponse对象
      • 3.CBV-类方法
      • 4.通过HttpResponse下载文件
      • 5.分页功能
    • 七、ORM的增删改查语句
      • 1.创建语句
      • 2.查询语句
      • 3.修改语句
      • 4.删除语句
      • 5.Filter过滤条件
      • 6.多模块关联关系
      • 7.Queryset对象方法
      • 8.ORM对象操作
        • 1.单表对象操作
        • 2.外键关联操作
        • 3.外键反向关联操作
        • 4.多对多操作
        • 5.多对多反向操作
    • 八、Django会话技术
      • 1、cookie工作原理
      • 2.Cookie
      • 2、CSRF
        • 防止CSRF
    • 九、ORM-admin后台管理
      • 1.管理app下的表(模型)
      • 2.自定制admin(管理界面)
    • 返回上级目录

HTTP协议

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪“部分,以及哪部分内容首先显示(如文本先于图形)等。

socket实现一个web服务器

# -*- coding:utf-8 -*-
# created by Alex Li - 路飞学城


import socket


def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('localhost', 8000))
    sock.listen(5)

    while True:
        # 等待浏览器访问
        conn, addr = sock.accept()
        # 接收浏览器发送来的请求内容
        data = conn.recv(1024)
        print(data)

        # 给浏览器返回内容
        conn.send(b"HTTP/1.1 200 OK\r\nContent-Type:text/html; charset=utf-8\r\n\r\n")
        conn.send("

电脑前的你长的真好看!

"
.encode("utf-8")) # 关闭和浏览器创建的socket连接 conn.close() if __name__ == "__main__": main()

WSGI实现一个web服务器

  • WSGI (Web Server Gateway lnterface) 是一种规范,它定义了使用python编的web app (应用程序) 与web server (socket服务端)之间接口格式,实现web app与web server间的解耦
  • 通俗的说:当规范建立后,程序就不再重复编写web server (socket服务端),而是直接使用现成的实现WSGI的模块 (例如:wsgiref、uwsgiwerkzeug),从而让程序员更加专注与业务代码与其重复造轮子,不如直接用现成的。
from wsgiref.simple_server import make_server

def run_server(environ,start_response):
    """

    :param environ: 请求相关内容,比如浏览器类型、版本、来源地址、url等
    :param start_response: 响应相关
    :return:
    """
    start_response("200 OK",[('Content-Type','text/html;charset=utf-8')])
    return [bytes('

Hello world!

'
,encoding='utf-8')] if __name__ == '__main__': httpd = make_server('localhost',8000,run_server) httpd.serve_forever() # 死循环

WSGI实现支持多URL的web服务器

from wsgiref.simple_server import make_server

def book(environ,start_response):
    start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])
    return [bytes('

book page!

'
, encoding='utf-8')] def cloth(environ,start_response): start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')]) return [bytes('

cloth page!

'
, encoding='utf-8')] pass def url_dispacher(): urls = { '/book':book, '/cloth':cloth, } return urls def run_server(environ,start_response): """ :param environ: 请求相关内容,比如浏览器类型、版本、来源地址、url等 :param start_response: 响应相关 :return: """ request_url = environ.get("PATH_INFO") # 获取用户请求的url url_list = url_dispacher() # 拿到所有url if request_url in url_list: func_data = url_list[request_url](environ,start_response) return func_data # 真正返回数据给用户 else: start_response("404 ",[('Content-Type','text/html;charset=utf-8')]) return [bytes('

404,Page not found!

'
,encoding='utf-8')] if __name__ == '__main__': httpd = make_server('localhost',8000,run_server) httpd.serve_forever() # 死循环

WSGI实现图片显示的web服务器

from wsgiref.simple_server import make_server
import re
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

def book(environ,start_response):
    start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])

    data = """
        

欢迎来到日本人专区

上路飞学城,看尽天下小片

"""
return [bytes(data, encoding='utf-8')] def cloth(environ,start_response): start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')]) return [bytes('

cloth page!

'
, encoding='utf-8')] pass def url_dispacher(): urls = { '/book':book, '/cloth':cloth, } return urls def img_handler(request_url): """ :param request_url: static/imgs/testimg.gif :return: """ img_path = re.sub('/static','/static_data',request_url) img_abs_path = "%s%s"% (BASE_DIR,img_path) if os.path.isfile(img_abs_path): f = open(img_abs_path,"rb") data = f.read() # 读取文件内容数据 return [data,0] # 0:成功读取,1:没有读取 return [None,1] def run_server(environ,start_response): """ :param environ: 请求相关内容,比如浏览器类型、版本、来源地址、url等 :param start_response: 响应相关 :return: """ request_url = environ.get("PATH_INFO") # 获取用户请求的url url_list = url_dispacher() # 拿到所有url if request_url in url_list: func_data = url_list[request_url] (environ,start_response) # 调用相应的url函数 return func_data # 真正返回数据给用户 elif request_url.startswith("/static/"): # 表示图片 img_data,img_status = img_handler(request_url) if img_status == 0: # 读取到图片数据 start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')]) return [img_data] else: start_response("404 ",[('Content-Type','text/html;charset=utf-8')]) return [bytes('

404,Page not found!

'
,encoding='utf-8')] if __name__ == '__main__': httpd = make_server('localhost',8001,run_server) httpd.serve_forever() # 死循环

MVC && MTV

1.MVC

MVC 是一种使用 MVC (Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式

  • Model(模型)一般对应数据库操作、纪录的存取
  • View (视图)决定着如何展示数据
  • Controller (控制器) 负现处理用户交互的部分·控制器负责从视图读取数据,控制用户输入,并向模型发送数据。(urls)

2.MTV

Django是一个MTV框架,其架构模板看上去与传统的MVC架构并没有太大的区别。

Django将MVC中的视图进一步分解为 Django视图(views.py)Django模板(html文件)两个部分,分别决定“展现哪些数据”和“如何展现”,使得Django的模板可以根据需要随时替换,而不仅仅限制于内置的模板,至于MVC控制器部分,由Django框架的URLconf(urls.py)来实现。

3.总结

MVC VIEWS 负责 业务逻辑处理+数据展示
MTV Views 业务逻辑处理
Templates 数据展示

Django小白开发指南_第1张图片

Django小白开发指南_第2张图片



一、创建Django项目

  • 要在终端中使用Django创建一个项目,可以按照以下步骤进行操作:
  1. 打开终端(命令行界面)

  2. 确保你已经安装了Django。可以通过运行以下命令来检查Django是否已安装:

    django-admin --version
    

    如果没有安装Django,可以使用以下命令安装:

    pip install django
    
  3. 创建项目。在终端中使用以下命令创建一个Django项目:

    django-admin startproject project_name
    

    把 “project_name” 替换为你想要的项目名称。这将在当前目录下创建一个名为 “project_name” 的文件夹,其中包含用于构建Django项目所需的文件和目录。

    创建应用。在项目目录中使用以下命令创建一个Django应用:

    django-admin startapp app_name
    

    把 “app_name” 替换为你想要的应用名称。这将在项目目录下创建一个名为 “app_name” 的文件夹,其中包含用于构建Django应用的文件和目录。

  4. 进入项目目录。使用以下命令进入刚创建的项目目录:

    cd project_name
    

    这会将终端的当前工作目录切换到你的项目目录中。

  5. 运行开发服务器。在项目目录中,运行以下命令来启动Django的开发服务器:

    python manage.py runserver 0.0.0.0:8000
    

    这将运行开发服务器,默认在本地的8000端口上。

  6. 现在,你可以在浏览器中访问 http://localhost:8000/ 来查看新创建的Django项目的初始页面。

这样就完成了在终端中创建一个Django项目的步骤。你可以根据需要在项目中添加应用、模型、视图等。

1.创建项目

django-admin startproject my_site

项目目录结构

mysite
├── manage.py		# 管理程序的文件,启动和结束等。
└── my_site
    ├── __init__.py
    ├── settings.py		# 程序的配置文件
    ├── urls.py		# 程序的路由系统,即:url和处理其函数的对应的关系
    └── wsgi.py		# 指定框架的wsgi

2.创建app

django-admin startapp app01
├── __init__.py 包
├── admin.py 数据库后台
├── apps.py   #django 把项目和app 关联起来的一个文件
├── migrations  #数据库相关
│   └── __init__.py
├── models.py  # 数据库操作地方
├── tests.py    # 单元测试
└── views.py    # 业务逻辑代码

3.第一次django 请求

  1. 匹配路由, 路由分发器查找用户请求的url 对应关系,
    1. 找到了业务函数,就调用
    2. 找不到就报404
  2. 业务函数,执行业务逻辑
  3. 返回数据给浏览器
1。  urls.py 编写路由
2.   在views.py 写一个业务函数
    2.1 编写业务 代码
    2.2 通过HttpResponse 方法返回数据给前端

3. python manage.py runserver 0.0.0.0:8000

二、模板

1.配置settings.py

  • TEMPLATES 配置Html文件,即配置该文件的路径,以便运行程序查找
  • 配置jinja引擎,需在该项目下新建一个文件:jinja2_env.py,并添加代码:
# 安装模块:pip install jinja2

from django.templatetags.static import static
from django.urls import reverse
from jinja2 import Environment

def environment(**options):
    env = Environment(**options)
    env.globals.update({
        'static': static,
        'url': reverse,
    })
    return env
# 项目默认配置django引擎
# 若需配置jinja引擎,需添加
'BACKEND': 'django.template.backends.jinja2.Jinja2', 
'environment':'django4.jinja2_env.environment', # django4-项目名称,jinja2_env-该项目下新建的jinja2_env.py

TEMPLATES = [ # 模版,html文件
    # jinja2模板引擎 (在html文件中可以引用函数)
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [BASE_DIR/'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            # 添加environment,并指定到jinja2_env文件中的environment
            
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
    
    # django模板引擎
    {
        
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR/"templates"], # html 文件夹的位置
        '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',
            ],
        },
    },
]

2.模板语法

  • 在templates目录下的html文件
"""注释"""
{# 单行注释 #}
{% comment %}
	多行注释
{% endcomment %}
    
    
    
"""变量"""
<p>{{ name }}</p> 
<p>{{ boddy.2 }}</p> # 列表:索引号取值
<p>{{ addr.city }}</p> # 字典
    
    
    
"""if语句"""
{% if age < 18 %}
    ...
{% elif age > 60 %}
    ...
{% else %}
    ...
{% endif %}  
   
    
    
 """结合运算符"""
{% if age >= 18 and  age < 60%}
{% if age >= 18 or  age < 60%}
    
"""in"""
{% if 'movie' in boddy %}
    ...
{% endif %}
    
    
    
"""for循环语句"""
{% for b in boddy %}
       ...
{% endfor %}
"""empty用法"""
{% for b in boddy2 %}
    ...
{% empty %}   
    <p>boddy2为空或不存在</p>
{% endfor %}
    
    

"""下标"""
{% for foo in boddy %}
    <p>
        counter_{{ forloop.counter }} # 下标从1开始
        counter0_{{ forloop.counter0 }} # 下标从0开始
        revcounter_{{ forloop.revcounter }} # 下标颠倒从大到小
        revcounter0_{{ forloop.revcounter0 }} # 下标颠倒从大到小(0)
    </p>
{% endfor %}
    
    
    
"""嵌套语句"""
<table border="2" width="400">
    {% for star in stars %}
    	{% for s in star %}
            {{ s }}-
            {{ forloop.parentloop.counter }}- # 父循环的下标
            {{ forloop.counter }} # 本次循环下标
    	{% endfor %}
    {% endfor %}  
</table>
    
    
    
"""过滤器"""
<p>{{ age }}</p>
<p>{{ age|add:5 }}</p> # add:变量+5
<p>{{ age|add:-5 }}</p> # add:变量-5
    
<p>{{ name|first|upper }}</p> # 将变量name第一个字符串大写
<p>{{ name|last|lower }}</p> # 将变量name最后一个字符串小写
<p>{{ name|title }}</p> 
<p>{{ name|truncatechars:3 }}</p> # 将变量name第三个字符串开始截断

<p>{{ boddy|join:'+' }}</p> # 列表转字符串
    
<p>{{ boddy2|default:'默认值' }}</p> # 若变量boddy2无值则设置默认值
    
<p>{{ dt|date:'y-m-d' }}</p> # 转成日期格式
<p>{{ dt|date:'Y-M-d' }}</p> 
    
 
    
"""HTML转义"""
<p>{{ code|safe }}</p> # safe:将变量code字符串转义成html格式

{% autoescape on %} # 关闭自动转义
   		... # 自动转义字符串内容
{% endautoescape %}

{% autoescape off %} # 开启自动转义
   		...
{% endautoescape %}
  • 若使用 jinja模板引擎,则可以调用python的函数
<body>
    <h2>jinja2模板语言</h2>
    <hr>

    {# 可调用python函数range #}
    {% for i in range(1,10) %}
        {{ i }}
    {% endfor %}

</body>
</html>

3.继承模板

# 模板快
{% block xxx%}
	...
{% endblock %}


# 继承模板
{% extends 'xxx.html' %}



# 继承模板内容:block.super
{% block content %}
{# 默认情况下,子模板会覆盖父模板内容 #}
{# 若想继承父模板内容,需使用block.super #}
	{{ block.super }}
        
{% endblock %} # 导入其他模板文件:include {% block head %} {# 导入其他模板文件 #} {% include 'xxx.html' %}
{% endblock %}

三、models模型

  • django使用对象关系映射 (0bject Relational Mapping,简称ORM)框架去操控数据库。

models ==> ORM

模型 -> 表

类结构 -> 表结构

对象 -> 表的一条数据

类属性 -> 表的字段


1.常用字段&属性

# 常用字段
CharField:用于存储字符串,可指定最大长度。

TextField:用于存储长文本字段,没有长度限制。

IntegerField:用于存储整数。

FloatField:用于存储浮点数。

BooleanField:用于存储布尔值,TrueFalse。

DateField:用于存储日期。

DateTimeField:用于存储日期和时间。

FileField:用于存储文件,文件会被上传到指定目录。

ImageField:与FileField类似,但特定于存储图像文件。
# 常用属性
max_length:用于指定字段的最大长度。适用于CharField和TextField等字符串字段类型。

null:指定字段是否允许为空。默认为False,即不允许为空。对于关联字段(ForeignKey、OneToOneField等),默认值是True。

blank:指定字段在表单中是否可以为空。默认为False,即不能为空。对于关联字段,如果null=True,则默认值为True。

default:指定字段的默认值。

choices:用于定义字段的选项列表。适用于CharField和IntegerField等字段类型。例如:choices=[(1, ‘男’), (2, ‘女’)]

verbose_name:指定字段在后台管理界面中显示的名称。

related_name:用于指定反向关系的名称。适用于关联字段(ForeignKey、ManyToManyField等)。

unique:指定字段的值是否在数据库中唯一,默认为False。

help_text:用于在后台管理界面中显示字段的帮助文本。

auto_now:指定每次保存对象时自动更新字段的值为当前时间。适用于DateTimeField和DateField等日期时间字段类型。

auto_now_add:指定在创建对象时将字段的值设置为当前时间。适用于DateTimeField和DateField等日期时间字段类型。

editable:指定字段是否可编辑。默认为True,表示字段可以在后台管理界面中被编辑。

primary_key:指定字段是否为主键。默认为False。

db_index:指定是否在数据库中为字段创建索引。默认为False。

choices:定义了一个包含键-值对的元组,作为下拉菜单。

name / db_column:数据库中的字段名称。

upload_to:用于定义上传文件的存储路径。
  • models.py
# 注:每次写完模型或添加一个字段都需要进行一次数据迁移,将数据迁移到数据库中!

from django.db import models

class UserModel(models.Model):
    # uid 会成为主键,原来默认的id不会创建
    uid = models.AutoField(auto_created=True,primary_key=True)
    # CharField:字符串类型
    #   unique:唯一
    #   db_index:数据库索引
    name = models.CharField(max_length=20,unique=True,db_index=True)
    # IntegerField:整数类型
    #   default:默认值
    age = models.IntegerField(default=18)
    # BooleanFieldL:bool类型
    sex = models.BooleanField(default=True)
    # TextField:文本框
    #   null=True:表示可以为空
    #   blank=True:在admin管理页面可以为空
    info = models.TextField(null=True,blank=True)
    # FloatField:小数
    salary = models.FloatField(default=100000.521)
    # DecimalField:十进制小数
    #   max_digits:最大长度
    #   decimal_places:小数点是几位
    money = models.DecimalField(max_digits=4,decimal_places=2,default=10.52)

四、设置数据库

  • 对象关系映射(Object Relational Mapping),它的实质就是将关系数据(库中的业务数据用对象的形式表示出来并通过面向对象 (Object-Oriented)的方式将这些对象组织起来,实现系统业务逻辑的过程。

Django小白开发指南_第3张图片

Django小白开发指南_第4张图片

1.设置mysql库

# settings.py

# 设置mysql
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "myBlog", # 数据库名
        "USER": "root",
        "PASSWORD": "12345678",
        "HOST": "127.0.0.1",
        "PORT": "3306",
    }
}

2.mysqldb替换mysql

  • python3连接mysql的得使用pymysql,MysqlDB模块300年没更新了,但django默认调用的还是MysQLdb,因此 pymysq有个功能可以让django以为是用了MySQLdb.即在项目目录下的 init.py中加上句代码就好
  • 或者直接安装mysqlclient则无需使用该方法:pip install mysqlclient
# __init__.py(项目文件)
import pymysql


pymysql.version_info = (1, 4, 13, "final", 0) # mysqlclient版本问题
pymysql.install_as_MySQLdb()

3.数据迁移

  • 同步ORM表结构到数据库(Models.py文件每次写完就需要进行迁移)

  • 在项目中如果添加了一个字段,需手动同步到数据库,否则数据库报错没有该字段

  • 数据库同步工具:migrations

  • 1、生成迁移文件

# app01表示指定的app,若第一次执行,则可以默认不写
python manage.py makemigrations app01
  • 2、执行迁移
python manage.py migrate
# settings.py
# 同步app设置,最后写入同步的app名

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',
]

五、URL路由匹配


1.正则匹配

  • Django的路由本质上是通过正则表达式来对用户请求的url进行匹配(Django2.0版本前使用正则匹配)
# urls.py

from django.urls import path,re_path
from app02 import views


urlpatterns = [
    re_path(r'^articles/2003/$',views.article_archive1),
    # year,month被封装为字典,所以函数形参需写该键值接收
    re_path(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/$',views.article_archive2),
    # slug:匹配任意字符串
    re_path(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/(?P[\w-]+)/$',views.article_archive3),
]
# views.py

def article_archive1(request):
    return HttpResponse('静态article_2003')

def article_archive2(request,year,month):
    return HttpResponse('动态article %s-%s'%(year,month))

def article_archive3(request,arg1,arg2,slug):
    return HttpResponse('动态article %s-%s-%s'%(arg1,arg2,slug))

2.路由匹配&传参

  • Django2.0新用法path
  • 自定义方法调用 register_converter()
# urls.py

from django.urls import path
from app02 import views

# path匹配
urlpatterns = [
    path('articles/',views.new_article1),
    # str:除了'/'外都能匹配
    path('articles//',views.new_article2),
    path('articles///',views.new_article3),
    # path:'/'也能匹配
    path('articles/',views.new_article4),
]
# views.py

def new_article1(request,year):
    return HttpResponse('新用法Path %s'%year)

def new_article2(request,year,date):
    return HttpResponse('新用法Path %s %s'%(year,date))

def new_article3(request,year,month,arg):
    return HttpResponse('新用法Path %s %s %s'%(year,month,arg))

def new_article4(request,arg):
    return HttpResponse('新用法Path %s'%(arg))
  • 路由传参
# urls.py
urlpatterns = [
    path('userdetail/',userdetail,name='myuserdetail'),
]


# views.py
def userdetail(request,uid):
    user = userModel.objects.get(pk=uid) # pk:主键
    return render(request,'userdetail.html',{'user':user}) # 将user参数传入html文件的user变量


# userdetail.html
<body>
    <p>名字:{{ user.name }}</p>
    <p>年龄:{{ user.age }}</p>
</body>
  • 命名空间
    • 若路径有命名空间,则该命名空间下所有相关的路径都需要添加该命名空间
# urls.py
urlpatterns = [
	path('app01/',include(('app01.urls','app01'),namespace='app01')),
]


# app01:urls.py
urlpatterns = [
    path('userlist/',userlist,name='myuserlist'), # 
    path('userdetail/',userdetail,name='myuserdetail'),
]

# userlist.html
<body>
 	# 跳转到命名空间为app01下的myuserdetail的html( url/userdetail/id )
    <a href="{%  url 'app01:myuserdetail' user.id %}"></a> 
</body>
  • 重定向
# views.py
from django.shortcuts import redirect,reverse

# 重定向
def myredirect(request):
    # return redirect('https://www.baidu.com')
    
    # 反向解析
    # redirect(reverse('myuserdetail'),args=(2,)) ==> 'userdetail/2/'
    return redirect(reverse('myuserdetail',args=(2,))) # args传参
    return redirect(reverse('myuserdetail',kwargs={'uid':2})) # 关键字传参

	# 命名空间
    return redirect(reverse('app01:myuserdetail',args=(2,))) # args传参
    return redirect(reverse('app01:myuserdetail',kwargs={'uid':2})) # 关键字传参

3.管理多个URL文件

  • 当有多个app时,每个app可以有自己的urls.py,只需在顶级urls.py(项目下的urls)中include一下就可以
# urls.py

from django.urls import path,include

urlpatterns = [
    # 包括了app01和app02项目的urls
    path('app01/',include("app01.urls")),
    path('app02/',include("app02.urls"))
]
1.去除重复url路径
# urls.py

# 去除重复的部分url
extra_urls = [
    path('/', views.new_article2),
    path('//', views.new_article3),
    path('', views.new_article4),
]

urlpatterns = [
    # 去除url重复的路径"articles"部分,匹配结果相同
    path('articles',include(extra_urls))
]
2.视图传参数
# urls.py
urlpatterns = [
    # 写一个字典,键表示实参名,值表示传入实参值
    path('articles/',views.new_article1,{'version':'v1.0'}),
]
# views.py
# 获取参数version
def new_article1(request,year,version):
    return HttpResponse('新用法Path %s-%s'%(year,version))

六、View视图


1.HttpRequest对象

  • **request.scheme:**返回请求协议(http/https)
  • **request.path:**返回请求页的路径
  • **request.method:**返回http请求的方法(get/post/put/delete…)
  • **request.GET/request.POST:**返回http请求的参数(方法由HTML文件决定)
  • **request.content_type:**返回http请求的类型
  • **request.FILES:**返回上传的文件数据
    • 获取文件数据,需在HTML文件加上属性 enctype=“multipart/form-data” 告知后台这个表单有文件,将文件单独处理。
  • **request.META:**包含所有可用 HTTP头的字典
  • **request.get_host():**返回网站服务器地址
  • **request.get_port():**返回服务器主机端口
  • **request.get_full_path():**返回相对路径(相当于request.path)
  • **request.build_absolute_uri(‘location’):**返回url的绝对路径,若添加位置,则附加
  • **request.COOKIES:**会话技术
  • **request.session:**会话技术
  • **request.META[‘REMOTE_ADDR’]*客户端的ip地址
# views.py
from django.shortcuts import render,HttpResponse

def index(request):
    print(request.scheme) # http
    print(request.path) # /app02/index
    print(request.method) # get
    print(request.content_type) # text/plain
    print(request.GET,request.POST) # 
    print(request.GET.get('username'),request.POST.get('username')) # 获取字典数据
    print(request.FILES) # ]}>
    print(request.META['REMOTE_ADDR']) # 127.0.0.1
    print(request.META) 
    for k,v in request.META.items():
        print(k,v)
    print(request.get_host()) # localhost:8000
    print(request.get_port()) # 8000
    print(request.get_full_path()) # /app02/index
    print(request.build_absolute_uri('user=abc/pwd=123')) # http://localhost:8000/app02/user=abc/pwd=123
    print(request.COOKIES) # 浏览器cookies数据
    print(request.session)
    return render(request,'form.html')
  • 中间件(防火墙)
    • url->中间件->view,中间件相当于防火墙,需关闭相应的安全过滤,才能修改浏览器的请求方法
# 项目的settings.py

MIDDLEWARE = [ # 中间件(防火墙)
    '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',
]

2.HttpResponse对象

# 响应
from django.http import JsonResponse

# 1.返回字符串
return HttpResponse('OK')
# 2.返回模板:前后端不分离使用
return render(request,'index.html')
# 3.重定向:跳转页码	
return HttpResponseRedirect("/")
# 4.返回JSON:前后端分离使用
return JsonResponse({"data":"hello"})
  • **content_type:**用于填充HTTP的数据类型

    • 默认情况下为: “ text/html; charset = utf-8”。
  • **status:**HTTP的状态码

# views.py

from django.shortcuts import HttpResponse

def index(request):
	html = """
    
"""
res = HttpResponse(html,content_type="text/plain",status=404)
  • **HttpResponseRedirect:**状态码:302,临时重定向一个新的位置
  • **HttpResponsePermanentRedirecttt:**状态码:301,永久定向到一个新的位置
from django.shortcuts import HttpResponseRedirect,HttpResponsePermanentRedirecttt

def test(request):
    return HttpResponseRedirect("/") # 临时定向到首页
	return HttpResponsePermanentRedirecttt("/") # 永久定向到首页

3.CBV-类方法

  • CBV(Class-Based-View),CBV就是在视图里使用处理请求。
  • 即可以让我们用类写View。这样做的优点主要下面两种:
    • 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
    • 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
# views.py
from django.views import View

# 该类会自动判断调用方法
class TestView(View):
    def get(self,request):
        return HttpResponse("Class View get request")
    def post(self,request):
        return HttpResponse("Class View post request")
    pass
  • url处理是需要一个可调用的函数,class-based-view提供了一个 as_view() 静态方法
# urls.py

urlpatterns = [
    path('class_view',views.TestView.as_view()) # 对于类需调用as_view方法
]
  • 类继承
# views.py 

# 类继承
class TestView2(TestView):
    time = 10

4.通过HttpResponse下载文件

  • MIME参考手册:https://www.w3school.com.cn/media/media_mimeref.asp
  • 对于文件类型,需参考MIME手册,然后修改 content_type
# views.py

def download_file(request):
    f = open('static_data/data.xls','rb')
    res = HttpResponse(f.read(),content_type='application/vnd.ms-excel') # 文件类型为xls
    res['Content-Disposition'] = 'attachment; filename="my_data.xls"' # 对下载的文件进行处理
    return res

5.分页功能

# views.py
from django.core.paginator import Paginator

def paginate(request,page=1):
    per_page = 10
    all_data = PersonModel.objects.all()
    # 分页器
    paginator = Paginator(all_data,per_page)

    persons = paginator.page(page) # 获取第page页的数据
    pages = range(1,per_page+1) # 页面范围,供遍历
    data = {'persons':persons,'pages':pages}
    
    return render(request,'paginate.html',data)
# urls.py
urlspattern = [
    path('paginate//',paginate,name='paginate'),
]

七、ORM的增删改查语句


1.创建语句

  • 方法1:在终端执行以下命令,进入django的数据库环境
python manage.py shell
  • 创建数据
from app01 import models

# 方法1,object.create创建对象,创建完后数据将自动提交保存到数据库
models.Account.object.create(
	username = '小枫',
    email='[email protected]',
    password='12345678',
)
# 方法2,先创建数据,再通过save方法提交数据
new_obj = models.Account(
	username = '小枫',
    email='[email protected]',
    password='12345678',
)
new_obj.save()
  • 外键关联 models.ForeignKey(“Account”)
# 方法1,直接在对象里关联 account_id = id值
models.Article.objects.create(
    title = '如何学Python',
    content = '日复一日,坚持学习',
    # 关联的键值必须加上_id
    account_id = 1,
    pub_date = '2023-7-31',
)
# 方法2,先各自创建表,再进行关联 a1.account = m1对象
# 先创建用户表
m1 = models.Account(
	username='王五',
	email='[email protected]',
	password='213123',
)
m1.save()
# 再创建文章表
a1 = models.Article(
    title='什么是chatggpt',
    content='人工智能chatgpt',
    pub_date='2022-10-12',
)
# 关联表
a1.account = m1
a1.save()
  • 多对多关联 models.ManyToManyField (“Tag”)
a1 = models.Article(
    title='什么是chatggpt',
    content='人工智能chatgpt',
    account_id = 1,
    pub_date='2022-10-12',
)
# tags.set,赋值tags对象id为1,3,5
a1.tags.set([1,3,5])
# tags.add,在原记录上加新值tags对象id为1,2,3
a1.tags.add(1,2,3)
articles = models.Article.objects.filter(content__contains='技术')  # 过滤出含有关键词的文章
tag = models.Tag.objects.get(id=2)  # 获取要关联的标签对象

for article in articles:
    article.tags.add(tag)  # 将标签关联到每篇过滤后的文章中

2.查询语句

# __gt:大于
# 查询id大于5的Account对象
models.Account.objects.filter('id__gt=5')
# startswith:以什么开头
# 查询以密码为a开头的Account对象                              
models.Account.objects.filter('password__startswith='a')
# , : 和                              
# 查询id大于5的Account对象并且psw是123
models.Account.objects.filter('id__gt=5,password='123')                          

3.修改语句

# 批量修改
models.Account.objects.filter(id__gt=1).update(password='1234556')

# 单条修改
a = models.Account.objects.get(id=1)
a.username = 'job'
a.save()

4.删除语句

  • 相关联的表数据也会被删除
# 批量删除
models.Account.objects.filter(id__gt=1).delete()

# 单条删除
a = models.Account.objects.get(id=1)
a.delete()

5.Filter过滤条件

  • 一般在前面加上is表示对字母大小写不敏感,可返回多个对象

gt:大于

it:小于

ite:小于等于

gte:大于等于

statswith:以什么开头

endswith:以什么结尾

contains:包含

models.Account.objects.filter(password__contains="1")

in:判断是否存在并返回

models.Account.objects.filter(id__in=(2,3))

range:区间过度,可对数字、日期进行过滤

import datetime
start_date = datetime.date(2022,12,15)
end_date = datetime.date(2023,8,1)
models.Account.objects.filter(register_date__range=(start_date,end_date))

date:查具体哪一天内容

models.Account.objects.filter(register_date__date="2023-7-31")
models.Account.objects.filter(register_date__date__gt="2023-7-31") # 配合gt使用

year、month、day:查哪一年、哪一月份、哪一天

models.Account.objects.filter(register_date__year="2023")
models.Account.objects.filter(register_date__month="12")
models.Account.objects.filter(register_date__day="15")

week:查询一年的第几周(52)

week_day:查星期几,从1(星期日)到7(星期六)

models.Account.objects.filter(register_date__week_day=2) # 星期一

hour、mintue、second:查每天的哪小时、哪分钟、哪一秒

models.Account.objects.filter(register_date__hour=8)
models.Account.objects.filter(register_date__mintue=15)
models.Account.objects.filter(register_date__second=15)

regex:正则表达式

models.Account.objects.filter(password__regex=r"^(1)")
  • 方法2:在views.py 里写函数,对模型对象进行操作
    • 同时需配置路由url
from django.shortcuts import render,HttpResponse
from django.db.models import Max,Min,Sum,Avg,Count
from app01.models import *

# Create your views here.
def add_person(request):
    # 方式1
    # p = PersonModel()
    # try:
    #     p.name='xiaoM'
    #     p.age=21
    #     p.save()
    # except Exception as e:
    #     return HttpResponse('add fail')
    # return HttpResponse('add success!')

    # 方式2
    # try:
    #     p = PersonModel(name='xiaoK',age=22)
    #     p.save()
    # except Exception as e:
    #     return HttpResponse('add fail')
    # return HttpResponse('add success!')

    # 方式3
    try:
       PersonModel.objects.create(name='ku',age=23)
    except Exception as e:
        return HttpResponse('add fail')
    return HttpResponse('add success!')

def del_person(request):
    try:
        PersonModel.objects.filter(age__gt=20).delete()
    except Exception as e:
        return HttpResponse('delete fail')
    return HttpResponse('delete success!')

def update_person(request):
    try:
        # p = PersonModel.objects.filter(age__gt=23).update(age=100)
        p = PersonModel.objects.get(id=5)
        p.age = 18
        p.save()
    except Exception as e:
        print(e)
        return HttpResponse('update fail')
    return HttpResponse('update success!')


def get_person(request):
    try:
        # p = PersonModel.objects.get(id=5)
        # print(p.name,p.age)

        p = PersonModel.objects.all()
        for o in p:
            print(o.name,o.age)
    except Exception as e:
        print(e)
        return HttpResponse('get fail')
    return HttpResponse('get success!')


# 聚合函数
def aggregate(request):
    p = PersonModel.objects.aggregate(Max('age'))
    p = PersonModel.objects.aggregate(Min('age'))
    p = PersonModel.objects.aggregate(Sum('age'))
    p = PersonModel.objects.aggregate(Count('age'))
    p = PersonModel.objects.aggregate(Avg('age'))
    print(p)
    # 排序
    p = PersonModel.objects.all().order_by('age') # 升序
    p = PersonModel.objects.all().order_by('age','id')
    p = PersonModel.objects.all().order_by('-age')  # 降序
    print(p)
    return HttpResponse('Success!')

6.多模块关联关系

  • 1.关联分类

    • ForeignKey:一对多,将字段定义在多的端中
    • ManyToManyField:多对多,将字段定义在两端的任意一端中
    • OneToOneField:一对一,将字段定义在任意一端中
  • 2.外键关联

    • ForeignKey:一对多关系
    • OneToOneField:一对一关系
from django.db import models

# 创建模型A
class ModelA(models.Model):
    name = models.CharField(max_length=100)

# 创建模型B
class ModelB(models.Model):
    name = models.CharField(max_length=100)
    model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)  # 建立一对多关系	
    model_a = models.OneToOneField(ModelA, on_delete=models.CASCADE)  # 建立一对一关联
  • on_delete参数
# 注意:修改on_delete参数之后需要重新同步数据库
user_type = models.ForeignKey(UserType,on_delete=models.CASCADE)

models.CASCADE默认值(Django1.11) # 表示级联删除,即删除UserType时胡关联的User也会被删除
models.PROTECT # 保护模式,阻止级联删除。(若无外键关联可删除)
models.SET_NULL # 置空模式,设为null,nuLL=True参数必须具备
modeLs.SET_DEFAULT # 置默认值设为默认值,default参数必须具备
models.SET() # 删除的时候重新动态指向一个安体访问对应元素,可传两数
modeLs.DO_NOTHING什么也不做。(Django1.11) # 有外键约束
  • 外键查询
# 正向查询:直接调用关联的键查询
print(user.user_type.name)
print(user.user_type.id)
# 反向查询
#	内部自动生成 user_set 属性供反向查询
print(user_type.user_set.all()) # 返回QuerySet
print(user_type.user_set.filter(id=1))

# reLated_name:关联名称,设置反向查询的名称,原本使用user_set改为users
user_type = models.ForeignKey(UserType,,reLated_name=users)
print(user_type.users.all())
  • 3.多对多关联
from django.db import models

# 创建模型A
class ModelA(models.Model):
    name = models.CharField(max_length=100)
    model_b = models.ForeignKey('ModelB', on_delete=models.CASCADE)

# 创建模型B
class ModelB(models.Model):
    name = models.CharField(max_length=100)
    model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
# 使用
m1 = ModelA.object.get(id=1)
m1.model_b.all()

m2 = ModelB.object.get(id=1)
m2.model_a.all()

7.Queryset对象方法

all():返回所有数据

  • 可在models.py 文件中的类加上__ str __方法,在该方法中写上初始化的字符串,调用all方法时将自动调用该初始化字符串
# models.py
class Account(models.Model):
    """账户表"""

    username = models.CharField(max_length=64,unique=True)
    email = models.EmailField(unique=True)
    password = models.CharField(max_length=255)
    register_date = models.DateTimeField(auto_now_add=True)
    signature = models.CharField("签名",max_length=255,null=True)
    # str方法在调用all方法时自动调用
    def __str__(self):
        return self.username
    
# python manage.py shell
modles.Account.objects.all() # 自动返回该类的初始化str方法

exclude():使用方法与filter()相同,结果与filter相反;排除符合条件的数据

models.Account.objects.exclude(register_date__date='2023-6-15')

values():将数据都封装成一个字典

a = models.Account.objects.all()
a.values()
# 通过键直接取该相应的值(字典)
a.values('username')

order_by():多字段排序

# 通过id进行顺序排序
a.values('id','register_date').order_by('id')
# 通过id进行逆序排序
a.values('id','register_date').order_by('id')

# 通过register_date进行顺序,若相同则根据id进行排序
a.values('id','register_date').order_by('register_date','id')

reverse():反转顺序,使用前必须先进行排序

a.values('id','register_date').reverse()
a.order_by('id').reverse()

get():精确查询一条数据(必须存在的数据),返回一个对象

a = models.Account.objects.get(id=1)
a = models.Account.objects.get(id__id=1) # 必须只存在一条大于1的数据
# 直接调用
a.username

8.ORM对象操作


1.单表对象操作
o = models.Artilce.objects.all()[0]
o.title
2.外键关联操作
o = models.Artilce.objects.all()[0]
o.account.username
3.外键反向关联操作
  • article_set是动态生成的,被外键关联的类,会自动根据关联的类名加上 _set,通过该属性可以反向查找
a = models.Account.objects.get(name='job')
a.article_set.all()
a.article_set.select_related() # 使用效果与all相同
4.多对多操作
a = models.Article.objects.get(id=5)
a.tags.all()
a.tags.select_related() # 使用效果与all相同
5.多对多反向操作
t = models.Tag.objects.get(name='科技')
t.article_set.all()
a.article_set.select_related()

八、Django会话技术

1、cookie工作原理

Django小白开发指南_第5张图片

2.Cookie

  • 使用response的方法
import datetime

from django.shortcuts import render,HttpResponse,redirect,reverse
from app01.models import *

# 获取cookie
def index(request):
    # cookie
    # 获取该页面的cookie为userid的值,若无则设为0
    userid = request.COOKIES.get('userid',0)
    # 获取登录的用户
    user = userModel.objects.filter(id=userid).first()
    return render(request,'index.html',{'user':user})


# 设置cookie
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    elif request.method == 'POST':
        # 1、接收前端提交过来的数据
        uname = request.POST.get('uname') # 参数为html的name属性值
        passwd = request.POST.get('passwd')

        # 2、登录验证
        users = userModel.objects.filter(username=uname,password=passwd)
        if users.exists():
            # 获取当前登录的用户对象
            user = users.first()
            response = redirect(reverse('index'))

            # 3、设置cookie
            response.set_cookie('userid',user.id) # 创建cookie
            # 设置过期时间
            # response.set_cookie('userid',user.id,max_age=7*24*3600) # max_age:秒
            # response.set_cookie('userid',user.id,
            #                     expires=datetime.datetime(2023,9,5))
            response.set_cookie('userid', user.id, # timedelta:时间差
                                 expires=datetime.datetime.now()+datetime.timedelta(days=10))
            # 跳转到登录页面
            return response

    return HttpResponse("当前账户不存在或账户密码有误,请重新输入或注册!")


# 删除cookie
def logout(request):
    response = redirect(reverse('index'))
    # 注销、删除cookie
    response.delete_cookie('userid')
    request.session.delete(session_key)

    return response
  • session
    • 使用request的方法
import datetime
from django.shortcuts import render,HttpResponse,redirect,reverse
from app01.models import *

# 获取session
def index(request):
    # session
    userid = request.session.get('userid',0)
    # 获取登录的用户
    user = userModel.objects.filter(id=userid).first()
    return render(request,'index.html',{'user':user})


# 设置session
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    elif request.method == 'POST':
        # 1、接收前端提交过来的数据
        uname = request.POST.get('uname') # 参数为html的name属性值
        passwd = request.POST.get('passwd')

        # 2、登录验证
        users = userModel.objects.filter(username=uname,password=passwd)
        if users.exists():
            # 获取当前登录的用户对象
            user = users.first()
            response = redirect(reverse('index'))

            # 3.设置session
            request.session['userid'] = user.id
            request.session.set_expiry(7*24*3600) # 设置过期时间

            return response

    return HttpResponse("当前账户不存在或账户密码有误,请重新输入或注册!")



# 删除session
def logout(request):
    response = redirect(reverse('index'))
    # 注销、删除session
    session_key = request.session.session_key
    request.session.delete(session_key)

    return response

2、CSRF

  • CSRF全拼为Cross Site Request Forgery,跨站请求伪造
  • CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求
    • 包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…
  • 造成的问题:个人隐私泄露以及财产安全

Django小白开发指南_第6张图片

防止CSRF
  • Django下的CSRF预防机制
    django第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个token 放在 cookie里。然后每次 POST请求都会带上这个 token,这样就能避免被CSRF攻击。
  • 在Post请求时,表单中添加 {% csrf_token %}
{# 禁止csrf安全验证 #} {% csrf_token %} ...

九、ORM-admin后台管理

  • 创建一个admin,用于登录 ‘localhost:8000/admin’
# 在项目的目录下执行
python manage.py createsuperuser

1.管理app下的表(模型)

  • 注册对应的模型,在该app项目的admin.py上写
# app01/  admin.py
from app01 import models

admin.site.register(models.Account)
admin.site.register(models.Article)
admin.site.register(models.Tag)

Django小白开发指南_第7张图片

2.自定制admin(管理界面)

# app01 --admin.py
# admin自定制
class AccountAdmin(admin.ModelAdmin):
    list_display = ('username','email','register_date','signature') # 显示列表
    search_fields = ('username','email') # 搜索内容
    list_filter = ('register_date',) # 过滤
    list_per_page = 5 #分页,每页几条数据
    list_editable = ['signature',] # 可在列表修改的属性

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title','pub_date','account')
    fields = ('title','content',('pub_date','tags'))# 可自定义显示范围
    exclude = ('account',) # 与fields相反,不显示某字段
    date_hierarchy = 'pub_date' # 按日期分类显示
    fieldsets = (
        ('文章内容',
         {'fields':('title','content'),
          'classes':('wide','extrapretty'), # 样式
         }
        ),
        ('发布相关',
         {'fields':('account','pub_date'),
          'classes':('collapse',), # 可折叠
         }
        )
    )
    # 只能针对多对多
    # filter_vertical = ('tags',)
    filter_horizontal = ('tags',)
    radio_fields = {'account':admin.HORIZONTAL} # 由下拉框变单选框
    autocomplete_fields = ['account',] # 外键自动补全,可用于查询数据
    
admin.site.register(models.Account,AccountAdmin) #模型与管理类相关联
admin.site.register(models.Article,ArticleAdmin)
admin.site.register(models.Tag)
  • 在admin管理后台,如果添加的字段可为空时,需在models.py 在该字段添加属性 blank=True
    • 一般null和blank同时使用(null作用于数据库)
class Account(models.Model):
    """账户表"""
    signature = models.CharField("签名",max_length=255,null=True,blank=True) # blank与admin配合使用
  • 将多对多关联显示到列表
    • 在 models.py 上自定义方法,在admin.py引入(若不自定义方法无法显示)
# admin.py
class Article(models.Model):
    def get_tags(self):
        return ', '.join([i.name for i in self.tags.all()])
    

# admin.py
class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title','pub_date','account','get_tags')
  • 添加HTML样式

    • 在 models.py 上写该属性的html样式,在admin.py上显示
    • 注意:添加新字段需要手动将数据同步到数据库,否则数据库没有该数据报错
      • python manage.py makemigrations app01
      • python mange.py migrate
    # models.py
    from django.utils.html import format_html
    
    class Tag(models.Model):
        color_code = models.CharField(max_length=6)
        def colored_name(self):
            return format_html(
                '{}',
                self.color_code,
                self.name,
            )
            
    # admin.py
    class TagAdmin(admin.ModelAdmin):
        list_display = ['name','colored_name']
    
    
  • 自定义表单名称

    • 在 models.py 各表类中添加类 Meta,添加属性 verbose_name_plural
      • 在各表字段自定义名称,直接在字段添加属性 verbose_name
# models.py

class Account(models.Model):
    class Meta:
        # verbose_name = '账户' # 存在复数形式
        verbose_name_plural = '账户'
        
class Article(models.Model):
    tags = models.ManyToManyField("Tag",verbose_name='标签')
    class Meta:
        verbose_name_plural = '文章'
        
class Tag(models.Model):       
        class Meta:
        verbose_name_plural = '标签'

返回上级目录

import sys
sys.path.append('..') # 返回上级目录

你可能感兴趣的:(django,servlet,python,个人开发,开发语言,后端)