Django框架

Django框架

Django是Python Web应用框架, 基于Python的WSGI(Web Service Gateway Interface)Web服务网关接口, Django从3.0开始运行 ASGI (异步服务网关接口)。

Django三大版本: Django 1.x、Django 2.x, Django 3.x(性能最优, 比较FastAPI/Tornado)。

一、 Django入门

1.1 基本概念

1.2 创建环境与app项目

安装依赖包

pip install django==2.2 -i https://mirrors.aliyun.com/pypi/simple

【注意】如果Python版本(3.7.4+)很高时,SQLite3版本同样很高,则django版本建议使用django==2.1.5+;因为,admin.site 站点管理时,会报auth_user_old表不存在的错误。

进入"终端" CMD命令, 通过 django-admin 命令创建django项目。

django-admin startproject helloDjango

通过django-admin命令创建app应用

django-admin startapp  mainapp

在一个Django项目中,存在很多的app应用(模块), 创建好的app需要注册到主工程中(settings.py)

其它命令,可以通过django-admin help 命令查看:

[django]
    check
    compilemessages
    createcachetable
    dbshell
    diffsettings
    dumpdata
    flush
    inspectdb
    loaddata
    makemessages
    makemigrations
    migrate
    runserver
    sendtestemail
    shell
    showmigrations
    sqlflush
    sqlmigrate
    sqlsequencereset
    squashmigrations
    startapp
    startproject
    test
    testserver

查看某一命令的使用: django-admin help runserver

查看相关的命令: python manage.py help

Available subcommands:

[auth]
    changepassword
    createsuperuser

[contenttypes]
    remove_stale_contenttypes

[django]
    check
    compilemessages
    createcachetable
    dbshell
    diffsettings
    dumpdata
    flush
    inspectdb
    loaddata
    makemessages
    makemigrations
    migrate
    sendtestemail
    shell
    showmigrations
    sqlflush
    sqlmigrate
    sqlsequencereset
    squashmigrations
    startapp
    startproject
    test
    testserver

[sessions]
    clearsessions

[staticfiles]
    collectstatic
    findstatic
    runserver

1.3 django项目结构

项目结构如下:

helloDjango
		|--- helloDjango   主工程目录
				 		|----  settings.py   # 设置文件, 数据库连接、app注册、中间件及模板配置
				 		|----  urls.py       # 总路由
				 		|----  wsgi.py       # Django实现wsgi的脚本
            |----  __init__.py
		|--- mainapp      应用模块(主)
						|----  __init__.py
						|---- admin.py      # 后台管理配置脚本
						|---- models.py     # 数据模型类声明所在脚本
						|---- views.py      # 声明当前应用的视图处理函数或类
						|---- urls.py       # 自已增加的当前应用模块的子路由
						|---- tests.py      # 当前应用模块的单元测试类
						|---- apps.py       # 声明当前应用的基本信息
		|--- manage.py     WEB应用的启动脚本, 项目工程的入口

启动项目的命令:python manage.py runserver

1.4 Django请求流程

1. 到urls分发器 (主路由urls.py -> 子路由 urls.py)
2. urls分发器根据路由规则(正则)分发到views
3. views去调用Model,交互数据
4. views将数据渲染(解析模板标签)到模板中,获取渲染之后HTML文本信息
5. 模板页面呈现给用户(封装HttpResponse对象)
http://127.0.0.1:8000/m/add/?csrfmiddlewaretoken=GQg9ySsT2KKOg2mzFosONCE3WqxaEnqK7UFkwbsGmwo9QLYbajI9PRfAgfHi0pgi&sn=111&name=%E7%8B%84%E5%A4%A7%E5%93%A5

1.5 请求和响应

请求对象: django.http.HttpRequest
响应对象: django.http.HttpResponse | JsonResponse
快捷函数: django.shortcuts.render 渲染 | redirect 重定向  ,  快速生成响应的对象

Request结构:

Request结构主要由以下部分组成:
    URL字段
    Header字段
    Body字段  该字段是一个io.ReadCloser接口
    Form字段、PostForm字段和MultipartForm字段

请求对象的属性:

request.method 请求方法,请求方法有:GET、POST、PUT/PATCH、DELETE、OPTIONS
request.GET  QueryDict 字典类型, get请求的查询参数
request.POST QueryDict 字典类型, post请求的表单form参数
request.META QueryDict 字典类型, 存放客户端环境相关参数, 如REMOTE_ADDR 客户端的IP地址

render函数:

render(request, '模板html文件', {}) 渲染模板,第三个参数是dict类型,可以在渲染模板时,替换{
  { }}或{% %}表达式。

二、 数据库连接与ORM模型

ORM(Object Relationship Mapping 对象关系映射): 将类和表进行映射, 针对类的实例操作时,即对表的行数据进行操作。在Python中,使用元类和相关的自省函数(hasattr、getattr、setattr、isinstance)实现的。

2.1 数据库连接配置

默认是sqlite3数据库, 在使用ORM模型之前,需要先生成迁移文件,再执行迁移命令,在数据库中生成这些模型对应的表。

  • 先生成迁移文件
python manage.py  makemigrations
  • 开始迁移(生成表、 修改表、删除表)
python manage.py migrate

注意: 一旦生成了迁移文件并且迁移成功之后,不要删除迁移文件。

sqlite3数据库的文件访问方式可以通过python的内置模块sqlite3(微型关系数据库, 不强调数据类型),还可以是pycharm的数据面板打开。

以下是python的sqlite3模块打开sqlite.db数据库文件的方式:

>>> import sqlite3
>>> db = sqlite3.connect('/Users/apple/PycharmProjects/xpy201/hidjango/db.sqlite3')
>>> cursor = db.cursor()
>>> sql = "select name from sqlite_master where type='table'"
>>> cursor.execute(sql)
<sqlite3.Cursor object at 0x104c80420>
>>> cursor.fetchall()
[('django_migrations',), ('sqlite_sequence',), ('auth_group_permissions',), ('auth_user_groups',), ('auth_user_user_permissions',), ('django_admin_log',), ('django_content_type',), ('auth_permission',), ('auth_user',), ('auth_group',), ('django_session',)]

2.2 初步使用ORM模型

在app模块中的models.py 定义一个用户(客户)模型

from django.db import models

class UserEntity(models.Model):
     # 默认情况下会自动创建id主键
     name = models.CharField(max_length=20)
     age = models.IntegerField(default=0)
     phone = models.CharField(max_lengt=11)
     
     class Meta:
     		 # 指定当前模型类映射成哪一个表
     		 db_table = 'app_user'

模型创建完成后,先后执行生成迁移文件和迁移。

2.3 CURD

查询

UserEntity.objects.all()  # 查询所有, list
UserEngtity.objects.get(pk=id)  # 根据主键值查询一个实体对象

X.objects 对象在X模型类定义时,由它的父类的元类动态添加的cls.add_to_class('objects', manager)。此对象主要用于对模型的查询操作(基于QuerySet类构建)。

增加

u = UserEntity()
u.name = 'disen'
u.age = 20
u.phone = '177'

# 保存模型对象
u.save()

删除

u = UserEntity.objects.get(1)
u.delete()  # 删除

更新

u = UserEntity.objects.get(3)
u.name = '李成'
u.save()

django作业: 完成以上的orm模型练习。

from django.db import models

# Create your models here.
# 声明学生模型类

class Student(models.Model):
    # 如果声明的字段不存在主键primary key ,默认增加id 主键字段
    sn = models.IntegerField(primary_key=True, db_column='sn')
    name = models.CharField(max_length=20, null=False)
    age = models.CharField(max_length=30)
    sex = models.CharField(max_length=2, default='男')

    def __str__(self):
        return f'{self.sn}, {self.name}, {self.age}, {self.sex}'

    class Meta:
        db_table = 'tb_student'
        # ordering = ('-age', )

Python Console中的命令:

>>> from mainapp.models import Student
>>> Student.objects.all()
<QuerySet [<Student: 1, disen, 1991-10-12,>, <Student: 2, jack, 1992-10-12,>]>
>>> Student.objects.get(pk=2)
<Student: 2, jack, 1992-10-12,>
>>> s2 = Student.objects.get(pk=2)
>>> s2
<Student: 2, jack, 1992-10-12,>
>>> s2.delete()
(1, {
   'mainapp.Student': 1})
>>> Student.objects.all()
<QuerySet [<Student: 1, disen, 1991-10-12,>]>
>>> s1 = Student.objects.get(pk=1)
>>>s1.name = '李阳'
>>>s1.save()
>>>s2.save()

除了Python Console之外,也可以在Terminal中通过Python交互环境进入,但需要将项目的path目录复制,如项目的位置为/Users/apple/PycharmProjects/xpy201/hidjango,则如下操作:

(xpy201) hidjango # python
>>> import os, sys
>>> import django
>>> sys.path.insert(0, '/Users/apple/PycharmProjects/xpy201/hidjango')
>>> os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hidjango.settings')
'hidjango.settings'
>>> django.setup()
>>> from mainapp.models import Student
>>> Student.objects.all()
<QuerySet [<Student: 1, 李阳, 1991-10-12,>, <Student: 2, jack, 1992-10-12,>]>

模型类的objects是Model的元类动态添加的: cls.add_to_class('objects', manager)base.py 360行,它的类型是django.db.models.Manager类(QuerySet查询结果集类)。

2.4 ORM应用于View

ORM定义的模型类,在urls路由对应的处理view函数中使用。根据客户端请求,要么查询数据,要么修改或保存数据,根据相关的业务数据要求,通过ORM实现数据的查询、修改、存储和删除。

2.4.1 表单数据保存

将POST请求的表单数据进行存储。首先,在view函数所在的脚本中,导入模型类,如Student

from .models import Student

# path: /m/add/
def add_stu(request: HttpRequest):
    if request.method == 'POST':
        form = request.POST
        
        s = Student()
        s.sn = form.get('sn', None)
        s.name = form.get('name', None)
        s.age = form.get('age', None)
        s.sex = form.get('sex', '男')
        
        s.save()
        return redirect('/m/list/')
      
     return render(request, 'add_stu.html', locals())

Window 操作系统如果出现debug下的gbk编码问题,则修改django/views/debug.py文件第331和338两行的文件编码为UTF-8,如:

 def get_traceback_html(self):
        """Return HTML version of debug 500 HTTP error page."""
        with Path(CURRENT_DIR, 'templates', 'technical_500.html').open(encoding='utf-8') as fh:
            t = DEBUG_ENGINE.from_string(fh.read())
        c = Context(self.get_traceback_data(), use_l10n=False)
        return t.render(c)

    def get_traceback_text(self):
        """Return plain text version of debug 500 HTTP error page."""
        with Path(CURRENT_DIR, 'templates', 'technical_500.txt').open(encoding='utf-8') as fh:
            t = DEBUG_ENGINE.from_string(fh.read())
        c = Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
        return t.render(c)

修改完成后,确认保存。或者降低django的版本为pip install django==2.0.1

2.4.2 查询数据渲染

在新的view函数中,查询所有的数据:

# 处理客户端发送url为 /m/list/ 的请求
def list_stu(request):
    datas = Student.objects.all()
    return render(request, 'list_stu.html', 
                  context={
   'datas': datas, 'title': '列出所有学生'})

将数据渲染到html中,html文件内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>学生列表</title>
</head>
<body>
<h1>{
  { title }}</h1>
<table border="1" cellspacing="0" cellpadding="5" width="80%">
    <thead>
        <th>学号</th>
        <th>姓名</th>
        <th>生日</th>
        <th>性别</th>
        <th width="200px">操作</th>
    </thead>
    {% for stu in datas %}
       <tr>
           <td>{
  { stu.sn }}</td>
           <td>{
  { stu.name }}</td>
           <td>{
  { stu.age }}</td>
           <td>{
  { stu.sex }}</td>
           <td>
               <button onclick="">删除</button>
               <button onclick="">编辑</button>
           </td>
       </tr>
    {% endfor %}

</table>

</body>
</html>
2.4.3 API接口数据

API接口: 前端页面的数据接口(URL),通过ajax(XMLHttpRequest)或 fetch()异步请求的方式获取API接口数据。API接口的数据,一般使用是json格式的文本字符串,在View函数中,对响应的json数据通过JsonResponse进行封装。

API接口说明文档:

base_url = ‘http://localhost:8000’

  1. 删除学生接口
  • url : /m/del/

  • 请求方法: GET

  • 请求参数(查询参数):

    sn 表示学号
    
  • 响应的数据

    • 正确(成功)

      {
             "data": "haha", "code": 0}
      
    • 失败

      {
             "code": 1, "msg": "必须指定sn查询参数"}
      

测试接口的前端js代码:

...
<button onclick="del_stu('{
    { stu.sn }}')">删除</button>
...

<script>

    function del_stu(sn) {
    
        if(confirm('确认是否删除学号为: '+sn + " 的学生?")){
    
            // alert('正在删除学号为: '+sn)
            // url = 'http://locahost:8000/m/del/?sn=123'
            url = location.origin+'/m/del/?sn='+sn
            fetch(url).then(resp=>resp.json()).then(data=>{
    
                if (data.code == 0){
    
                    // 刷新当前页面的数据
                    open('/m/list/', target='_self')
                }else{
    
                    alert('操作失败')
                }
            })
        }
    }
</script>

view函数的写法:

def del_stu(request):
    # 获取查询参数中的sn
    sn = request.GET.get('sn', None)
    if not sn:
        return JsonResponse({
   'code': 1, 'msg': '必须指定sn查询参数'})

    s = Student.objects.get(pk=sn)  # pk表示主键的列名
    s.delete()
    return JsonResponse({
   'data': 'OK', 'code': 0})

扩展:增加列表页面的搜索功能

views.py脚本的内容:

def list_stu(request):
    wd = None
    if request.method == 'POST':
        # wd 可能是学号,可能是姓名
        # django.db.models.Q 可以多条件查询
        wd = request.POST.get('wd', '')

    if wd:  # '' 为 False, None为False
       if wd.isdigit():  # 判断搜索的内容是否为数字 
           datas = Student.objects.filter(sn=wd).all()
       else:
           datas = Student.objects.filter(name__contains=wd).all()
    else:
        datas = Student.objects.all()

    return render(request, 'list_stu.html', 
                  context={
   'datas': datas, 'title': '列出所有学生'})

list_stu.html模板内容:

<h1>{
  { title }}</h1>

<form method="post">
    {% csrf_token %}
    <div style="text-align: center">
        <input type="text" placeholder="输入学号或姓名" name="wd" size="30"> <button>搜索</button>
    </div>
</form>

...

作业: 完成修改功能。

2.4.4 跨域请求配置
2.4.4.1 自定义中间件方式

在项目创建middles.py脚本,并定义CorsMiddleware类

ALLOWED_ORIGINS = '*'
ALLOWED_METHODS = ['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE']
# ALLOWED_METHODS = ['POST', 'GET']
ALLOWED_HEADERS = ['Content-Type', '*']
ALLOWED_CREDENTIALS = 'true'

class CorsMiddleware(object):
    """
    This middleware allows cross-domain XHR using the html4/5 post data API.               Access-Control-Allow-Origin: http://foo.example
    Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE
    """
    def process_request(self, request):
        if 'HTTP_ACCESS_CONTROL_REQUEST_METHOD' in request.META:
            response = http.HttpResponse()
            response['Access-Control-Allow-Origin']  = ALLOWED_ORIGINS
            response['Access-Control-Allow-Methods'] = ",".join(ALLOWED_METHODS )
            response['Access-Control-Allow-Headers'] = ",".join(ALLOWED_HEADERS )
            response['Access-Control-Allow-Credentials'] =  ALLOWED_CREDENTIALS
            return response                                                             
        return None                                                     
    def process_response(self, request, response):
        response['Access-Control-Allow-Origin']  = ALLOWED_ORIGINS
        response['Access-Control-Allow-Methods'] = ",".join( ALLOWED_METHODS )
        response['Access-Control-Allow-Headers'] = ",".join( ALLOWED_HEADERS )
        response['Access-Control-Allow-Credentials'] = ALLOWED_CREDENTIALS
                                                               
        return response

并配置到settings文件的中间件的位置:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'middles.CorsMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]
2.4.4.2 第三方应用方式

安装第三方的跨域请求的应用:django-cors-headers。

参考文档: https://pypi.org/project/django-cors-headers/

pip install django-cors-headers

安装成功之后, 添加已安装应用列表:

INSTALLED_APPS = [
    ...
    'corsheaders',
    ...
]

再配置中间件:

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

最后配置应用中使用的常量:

CORS_ORIGIN_ALLOW_ALL = True  # 默认为False
CORS_ALLOW_CREDENTIALS = True
# CORS_ORIGIN_WHITELIST = []  # 白名单, 只有CORS_ORIGIN_ALLOW_ALL为False时有效
CORS_ALLOW_METHODS = [
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
]
CORS_ALLOW_HEADERS = [
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
]

三、 综合案例-水果管理

3.1 模型设计

# Fruit 水果模型类(name 名称, price 价格, source 产地, content 描述, cate_type_id 类型ID)
# FruitImage 水果图片模型类(fruit_id 水果ID, url 图片的路径, width 宽度, height 高度, name 标题)
# CateType 水果分类(name 名称, order_num 排序号)

# Store 水果商店(name 商店名称,boss_name 店主姓名, phone 联系电话, address 详细地址, city 城市, lat 纬度, lon 经度)  
# 经纬度: 百度地图拾取坐标系统

# StoreDetail 水果商店的详情, 广告、精选水果、特价水果

模型和模型之间的关系:

  • 一对一的关系 models.OneToOneField()
  • 一对多的关系 models.Foreignkey()
  • 多对一的关系
  • 多对多的关系 models.ManyToManyField()

【注】在一端模类中,访问多端模型的对象时: obj.小写的多端类名_set, 或在多端类中定义关系时指定了反向引用的属性名。

from django.db import models

# Create your models here.
class FruitCategory(models.Model):
    # 如果不存在主键字段时,默认新增一个id字段
    name = models.CharField(verbose_name='分类名', max_length=20, unique=True)
    num = models.IntegerField(verbose_name='序号')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = verbose_name = '水果分类'
        db_table = 'tb_category'
        ordering = ('-num', )


class Fruit(models.Model):
    name = models.CharField(max_length=20, verbose_name='水果名')
    price = models.FloatField(verbose_name='价格', default=0)
    source = models.CharField(verbose_name='源产地', max_length=50)

    # blank和verbose_name两个参数描述字段在admin站点显示的信息和必填验证
    content = models.TextField(verbose_name='描述', null=True, blank=True)


    # ForeignKey属性的字段(关联主表模型类的实例),自动增加`字段名_id` 外键字段
    # related_name 指定关联父模型引用当前类的名称,默认为`小写的当前类名_set`
    category = models.ForeignKey('FruitCategory',
                                 on_delete=models.SET_NULL,  # 级联删除时设置为NUll
                                 null=True, blank=True, verbose_name='所属分类',
                                 related_name='fruits')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'tb_fruit'
        verbose_name_plural = verbose_name = '水果信息'
        ordering = ('source', 'price')
c1 = FruitCategory(name='水果',num=100)
c1.save()

f1 = Fruit(name='红富士', price=4.5, source='延安', category_id=c1.id)
f1.save()

f2 = Fruit(name='小青', price=3.5, source='山东', category_id=c1.id)
f2.save()

f1.category.name  # 查看水果的分类名称

c1.fruits.all()  # 查看当前分类下的所有水果

设计水果图片模型类:

class FruitImage(models.Model):
    fruit = models.OneToOneField('Fruit',
                                 on_delete=models.CASCADE,
                                 verbose_name='水果')

    title = models.CharField(max_length=50, verbose_name='标题')

    # 使用ImageField时,需要安装pillow库
    # 配置静态资源 static和媒体资源 media
    # upload_to 是相对于settings.MEDIA_ROOT 路径
    img = models.ImageField(upload_to='fruits',
                            width_field='width',
                            height_field='height')

    width = models.IntegerField(verbose_name='宽度')
    height = models.IntegerField(verbose_name='高度')

安装pillow库:

pip install pillow

设置静态资源的目录:

# 静态资源文件: css/js/image/fonts等
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

# 媒体资源文件
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/media')

将媒体资源访问的URL添加到主路由中:

from fruitpro import settings
from django.conf.urls.static 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值