美文网首页
Python - Django快速入门

Python - Django快速入门

作者: huangsv | 来源:发表于2019-07-30 21:55 被阅读0次

一、Django创建

1、安装虚拟环境

pip install virtualenv

2、创建虚拟环境

cd "工作路径"
virtualenv venv
vene\Scripts\activate  # 目前windows在cmd中才有效

3、安装Django框架

pip install django==2.2.3  # 2.2.3目前是最新版

4、创建django项目——demo

django-admin startproject demo
'''
Django项目文件说明:
    demo/__init__.py:说明demo是一个python包
    demo/settings.py:项目的配置文件
    demo/urls.py:项目进行url路由的配置
    demo/wsgi.py:web服务器和Django交互的入口
    manage.py:项目的管理文件
'''

5、创建django应用——appdemo

cd demo
python manage.py startapp appdemo
# 一个项目由多个**应用**组成的,每一个应用完成一个特定的功能
'''
应用文件说明:
migrations:文件夹
    __init__.py:说明目录是一个python模块
    admin.py:网站的后台管理相关的文件
    model.py:模型 写和数据库相关的内容
    tests.py:写测试代码的文件
    views.py:视图 接收请求,进行处理,与M和V进行交互,返回应答
            定义函数,视图函数
'''

6、注册应用

在项目文件 demo/demo/settings.py 下的 INSTALLED_APPS 列表中注册

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 注册应用
    'appdemo',
]

7、配置数据库(可选,如不配置将使用默认数据库sqlite3)

# 1)在项目文件 demo/demo/settings.py  下的 DATABASES 字典中修改
# 连接的数据库需要存在,Django里不需要自动创建数据库

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'Django_mysql',  # 连接数据库名字
        'USER': 'root',  # 连接mysql的用户名
        'PASSWORD': 'root',  # 连接mysql的密码
        'HOST': 'localhost',  # 指定mysql数据库所在的电脑ip
        'PORT': '3306',  # mysql的端口号
    }
}

# 2)在项目文件 demo/demo/__init__.py 中导入mysql,需要提前安装pymysql模块
import pymysql
pymysql.install_as_MySQLdb()

二、M模型

1、编写模型类(操作数据库)

# 应用文件 appdemo/models.py 中编写,以下是创建两个数据表

from django.db import models

# Create your models here.
# 类需要继承 models.Model 类 才是模型类
class BookClass(models.Model):
    '''图书分类模型'''
    # Django自动创建的 id 为分类序号,这里用创建
    # id = models.CharField(primary_key=True, max_length=2)
    # 类名
    cname = models.CharField(max_length=10)
    
    def __str__(self):
        # 重写方法
        return self.cname


class BookInfo(models.Model):
    '''图书信息模型类'''
    # 图书名称
    bname = models.CharField(max_length=50)
    # 作者
    bauthor = models.CharField(max_length=30)
    # 出版日期,DateField说明是一个日期类型
    bpub_date = models.DateField()
    # 类型,设置为外键,on_delete=models.CASCADE
    bclass = models.ForeignKey('BookClass', on_delete=models.CASCADE)
    
    def __str__(self):
        # 重写str方法
        return self.bname


'''模型类型:
AutoField : 一个ID自增的字段,但创建表过程Dajngo会自动添加一个自增的主键字段
BinaryField : 一个保存二进制数据的字段
BooleanField : 一个布尔值的字段,应该指明默认值,管理后台中默认呈现为CheckBox形式
NullBooleanField : 可以为None值的布尔值字段
CharField : 字符串字段,max_length 指明最大长度,必须指明参数max_length值,管理后台中默认呈现为TextInput形式
TextField : 文本域字段,对于大量文本应该使用TextField。管理后台中默认呈现Textarea形式
DateField : 日期字段([auto_now=False, auto_now_add=False]),auto_now每次保存对象时,自动设置该字段为当前时间,auto_now_add表示当对象第一次被创建时自动设置当前时间,代表Python中datetime.date的实例。管理后台中默认呈现TextInput形式
TimeField:时间字段,参数同上
DateTimeField : 时间字段,代表Python中fatetime.datetime实例。管理后台默认呈现TextInput形式
EmailField : 邮件字段,是CharField的实现,用于检查该字段值是否符合邮件地址格式
FileField : 文件上传字段,管理后台默认呈现ClearableFileInput形式
ImageField : 图片上传字段,是FileField的实现。管理后台默认呈现ClearableFileInput形式
IntegerField : 整数值字段,在管理后台默认呈现NumberInput或者TextInput形式
DecimalField(max_digits=None, decimal_pcaces=None) : 十进制浮点数。max_digits表示总位,decimal_pcaces表示小数位数
FloatField : 浮点数值字段,参数同上,在管理后台默认呈现NumberInput或者TextInput形式
SlugField : 保存字母数和下划线和连接符,用于生成url的短标签
UUIDField : 保存一般统一标识符的字段,代表Python中UUID的实例,建议默认值:default
ForeignKey : 外键,需指定参数(当该模型实例删除的时候,是否删除关联模型) on_delete=models.CASCADE
ManyToManyField : 多对多关系字段,与ForeignKey类似
OneToOneField : 一对一关系字段,常用于扩展其他类型

选项:
default : 设置默认值
primary_key : 若为True,设置为主键,一般作为AutoField的选项使用,默认False
unique : 如果为True,则在表中必须有唯一值,默认False
db_index : 若为True,则在表中会为此字段创建索引,默认False
db_column : 字段的名称,如果未指定,则使用属性的名称
null : 若为True,表示允许为空,默认False
blank : 若为True,则该字段 允许为空白,默认False
null是数据库范畴的概念;blank是后台管理页面表单验证范畴的。
'''

2、执行数据迁移

# 生成迁移文件
python manage.py makemigrations
# 迁移文件是根据模型类生成的

# C:\Users\huangsv\AppData\Local\Programs\Python\Python37\Lib\site-packages\django\db\backends\mysql\base.py  注释第36行两行

# C:\Users\huangsv\AppData\Local\Programs\Python\Python37\lib\site-packages\django\db\backends\mysql\operations.py 找到 146 行,把 decode 改成 encode 即可

# 上面两个问题在 Django2.2.3 版中发现,实测 Django2.0 版没有此问题

# 执行迁移生成表
python manage.py migrate
# 操作数据
# 在python manage.py shell中
'''

插入
# 导入模型类
In [2]: from appdemo.models import BookInfo
# 创建对象
In [2]: b = BookInfo()
# 添加bname字段
In [3]: b.bname = '天龙八部'
# 导入python时间模块
In [4]: from datetime import date
# 添加bpub_date字段
In [5]: b.bpub_date = date(1990,1,1)
# 保存
In [6]: b.save() 

查询
# 使用类.objects.get(条件)创建对象
In [10]: b2 = BookInfo.objects.get(id=1)
# 对象.字段 查询
In [11]: b2.bname
Out[11]: '天龙八部'
# 查询 id字段
In [12]: b2.id
Out[12]: 1
# 查询出版日期
In [13]: b2.bpub_date
Out[13]: datetime.date(1990, 1, 1)

修改
# 修改出版日期,直接赋值更新即可
In [14]: b2.bpub_date=date(1990,10,10)
# 更新一次需要保存一次
In [15]: b2.save()

In [16]: b2.bname
Out[16]: '天龙八部'
# 查询出版日期,显示的更新后的日期
In [17]: b2.bpub_date
Out[17]: datetime.date(1990, 10, 10)

删除
In [18]: b2.delete()
'''

'''查询函数:通过 模型类.objects 属性可以调用如下函数,实现对模型类对弈的数据表的查询

get("条件表达式") : 返回表中满足条件的一条且只能有一条的数据;返回的是一个模型类对象;查询到多条数据,则抛出异常MultipleObjectsReturned,查询不到数据,则抛出异常DoesNotExist
all() : 返回模型类对应表格中的所有数据;返回值是QuerySet类型;查询集
filter("条件表达式") : 返回满足条件的数据;返回值是QuerySet类型;参数写查询条件
exclude("条件表达式") : 返回不满足条件的数据;返回值是QuerySet类型;参数写查询条件
order_by() : 对查询结果进行排序;返回值是QuerySet类型;参数中写根据哪些字段进行排序

条件格式:示例:(字段__contains=  )
查询包含 : contains  相当于sql中的like
查询以 结尾的endswith 开头startswith
查询不为空 isnull = False/True
查询范围 in = []
比较查询(id__gt=3) gt(greate than)大于 lt(less than)小于 gte(equal) 大于等于 lte(equal)小于等于
 示例 查询1980年发表的图书((bpub_date__year=1980)) 
     查询1980年1月1日后发表的图书((bpun_date__gt=date(1980,1,1)) )
排序查询 .objects.all().order_by('id') 升序
        .objects.all().order_by('-id') 降序
        排序可以省略all()
例子:
Q 对象 : 用于查询时条件之间的逻辑关系
from django.db.models import Q

1.查询id大于3且阅读量大于30的图书的信息
filter(id__gt=3, bread__gt=30)
filter(Q(id__gt=3)&Q(bread__gt=30))

2.查询id大于3或者阅读量大于30的图书信息
filter(Q(id__gt=3)|Q(bread__gt=30))

3.查询id不等于3图书的信息
filter(~Q(id=3))

F对象 : 用于类属性之间的比较
from django.db.models import F
1.查询图书阅读量大于评论图书信息
filter(bread__gt=F('bcomment'))

2.查询图书阅读量大于2倍评论量图书信息
filter(bread__gt=F('bcomment')*2)

聚合函数 : 对查询结果进行聚合操作,调用函数来使用聚合:aggregate,返回值是一个字典
from django.db.models import Sum, Count, Max, Min, Avg
1.查询所有图书的数目
all().aggregate(Count('id'))  # 不能写 *
    返回字典 {'id__count': 5}

2.查询所有图书阅读量的总和
.appregate(Sum('bread'))
    {'bread__sum': 126}
    
count函数 返回一个数目
.all().count()     .count()
1.统计id大于3的所有图书的数目
.filter(id__gt=3).count()

查询集
all, filter, exclude, order_by 调用这些函数会产生一个查询集,QuerySet类对象可以继续调用上的所有函数。

查询集特性:
1)惰性查询:只有在实际使用查询集中的数据的时候都会发生对数据库的真正查询
2)缓存:当使用的是一个查询集时,第一次的时候会发生实数据库的查询,然后把结果缓存起来,之后再使用这个查询集时,使用的缓存中的结果

限制查询集:
可以对一个查询集进行取下标或者切片操作来限制查询集的结果。
对一个查询集进行切片操作会产生一个新的查询集,下标不允许为负数。

取出查询集的第一条数据的两种方式:
b = BookInfo.objects.all()
b[0] 如果b[0]不存在,会抛出IndexError异常
b[0:1].get() 如果b[0:1].get()不存在,会抛出DoesNotExist异常
b.exists 判断一个查询集中是否有数据,True False
'''

'''
关联查询(一对多)
在一对多关系中,一对应的类我们把它叫做一类,多对应的那个类我们把它叫做多类,我们把多类中定义的建立关联的类属性叫做关联属性
例子:查询图书id为1的;图书关联的所有英雄的信息。
一类查多类
b = BookInfo.objects.get(id=1)
b.heroinfo_set.all()
通过模型类查询:
HeroInfo.objects.filter(hbook__id=1)

例子:查询id为1的英雄关联的图书信息
多类查一类
h = HeroInfo.object.get(id=1)
h.hbook
通过模型类查询:
BookInfo.objects.filter(heroinfo__id=1)

通过模型类实现关联查询:
例子:
1.查询图书信息,要求图书关联的英雄的描述包含‘八’
BookInfo.objects.filter(heroinfo__hcomment__contains='八')

2.查询图书信息,要求图书中的英雄的id大于3
BookInfo.objects.filter(heroinfo__id__gt=3)

3.查询书名为“天龙八部”的所有英雄
HeroInfo.objects.filter(hbook__btitle='天龙八部')
'''

3、设置本地化

# 在项目文件 demo/demo/settings.py

# 设置语言 为 中文
LANGUAGE_CODE = 'zh-hans'
# 时区 上海
TIME_ZONE = 'Asia/Shanghai'
# 显示国际时间 
USE_TZ = True

4、注册模型类并自定义管理页面

# 应用文件admin.py下

from django.contrib import admin
# 1 导入模型类
from .models import BookInfo, BookClass
# Register your models here.
# 自定义模型管理类
class BookInfoAdmin(admin.ModelAdmin):
    '''图书信息类'''
    list_per_page = 10  # 指定每页显示10条信息
    list_display = ['id', 'bname', 'bauthor', 'bpub_date']
    # list_display可以写模型类中定义的方法
    # list_display = ['title']
    actions_on_bottom = True  # 让管理页面的下方也有一个执行框 
    actions_on_top = False  # 隐藏管理页面的上方的执行框 
    list_filter = ['bname']  # 列表面右侧以bname出现过滤栏
    search_filter = ['bname']  # 列表上方以bname出现搜索框
    
    # 编辑页 显示字段顺序
    fields = []
    # 编辑页 分组字段显示
    fieldsets = (
        ('组名',{'fields':[]})
    )  
    # fields与fieldsets只能用一个
    
class BookClassAdmin(admin.ModelAdmin):
    '''图书分类类'''
    list_display = ['id', 'cname']
    # 编辑页 关联对象 显示该类的多类属性
    inlines = [ BookClassStackedlnline ]

# 2 注册模型类,并加入自定义管理页面
admin.site.register(BookInfo, BookInfoAdmin)
admin.site.register(BookClass, BookClassAdmin)

# 编辑页 关联对象
'''在一对多的关系中,可以在一端的编辑页中编辑多端的对象,嵌入多端对象的方式包括表格,块
类型lnlineModelAdmin:表示在模型的编辑页面嵌入关联模型的编辑
子类Tabularlnline:以表格形式嵌入
子类Stackedlnline:以块形式嵌入
'''
# 打开admin.py 创建BookClassStackedlnline类
class BookClassStackedlnline(admin.Stackedlnline):
    model = BookInfo  # 关联子对象
    extra = 2  # 额外增加2个子编辑对象
# 表格形式嵌入同上,继承类不同




#---------------------------------------------
# 模型类
class ......(models.Model):
    # 可以在这里对管理后台指定名称
    bname = models.CharField(verbose_name = '书名', max_length=50)
    ......
    def title(self):
        return selft.atitle
    # 可以点击后台管理页面的title列进行atitle排序
    title.admin_order_field = 'atitle'
    title.short_description = '可以让title显示的中文'
    
    # 关联对象
    def parent(self):
        if selft.aParent is None:
            return ''
        return selft.aParent.atitle
    parent.short_description = '父级'

这时在后台就有模型的信息,这个返回值是把一个对象转换成一个字符串,可以在models.py中重写魔法方法str(self),在编写models时已经重写了

这里可以跳到第 五 步运行了

*M 模型扩展

objects

可以在应用文件下的 models.py 定义一个继承 objects 类的类,重写all(),封装save()等方法

# 例子:图书管理器类 objects
# 继承 models.Manager 类
class BookInfoManager(models.Manager):
    '''图书模型管理器'''
    # 重写 all() 方法
    def all(self):
        # 1、调用父类的all(),获取所有的数据
        books = super().all()
        # 2、根据需求进行过滤
        books = books.filter(isDelete=False)
        # 3、返回books
        return books
    
    # 封装函数,操作模型类对应的数据表(增删改查)
    # BookInfo.objects.insert_book('', '')
    # 在models.Manager中有一个create方法可以用,BookInfo.objects.create(bname='', bpub_date='')
    def insert_book(self, bname,bpub_date):
        # 1、创建一个图书对象
        # book = BookInfo()
        # 获取selft所在的模型类
        model_class = self.model
        book = model_class()
        book.bname = bname
        book.bpub_date = bpub_date
        # 2、保存进行数据库
        book.save()
        # 3、返回book
        return book

    
def BookInfo(models.Model):
    # ...... 其他代码省略
    
    # 自定义一个BookInfoManager类的对象
    objects = BookInfoManager()

元选项

指定模型类生成表的表名

# 在模型类中定义 Meta 元类
# 例子:
def BookInfo(models.Model):
    # ...... 其他代码省略
    
    # 自定义一个BookInfoManager类的对象
    objects = BookInfoManager()
    
    # 元类 Meta
    class Meta:
        # 指定模型类的对应的表名
        db_table = 'new table name'

三、V视图

1、在应用下的views.py中定义函数

from django.shortcuts import render
# 导入httprequest类
from django.http import HttpResponse

def index(request):
    # 进行处理,和M与T交互
    return HttpResponse("这是我的第一个django项目")

2、在项目文件中的urls.py中声明路径

from django.contrib import admin
# 需要导入 include
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),  # 配置项目
    path('', include('appdemo.urls')),  # 包含应用中的urls文件
]

3、在应用文件中新建urls.py

from django.urls import path
# 导入同级路径下的views
from . import views

# 通过url函数设置url路由配置项
urlpatterns = [
    path('index/', views.index), # 建立/index和视图index之间的关系
]

* V视图扩展

cookie, session, ajax

from django.shortcuts import render, redirect  # 重定向
# from django.http import HttpResponse, jsonResponse, HttpResponseRedirect  # 重定向
from django.http import HttpResponse,JsonResponse
from datetime import datetime,timedelta

# Create your views here.
from .models import BookClass

def first(request):
    # 重定向
    # return HttpResponseRedirect('/index')
    return redirect('/index')

def index(request):
    '''/index,显示分类'''
    # 通过 M模型 查找图书的信息,导入图书模型
    bc = BookClass.objects.all()
    # 使用模板
    temp = 'index.html'
    # 定义模板上下文:向模板文件传递数据
    temp_dict = {
        'bc':bc,
    }
    return render(request, temp, temp_dict)


def detail(request, cid):
    '''查询图书分类关联信息'''
    try:
        # 1.根据bclass_id查询图书信息
        book = BookClass.objects.get(id=cid)
    except:
        return render(request, '404.html')
    # 2.查看和book关联的图书信息
    bn = book.bookinfo_set.all()
    temp = 'detail.html'
    temp_dict = {
        'book':book,
        'bn':bn,
    }
    return render(request, temp, temp_dict)

def login(request):
    '''登录页面'''
    # 判断用户是否登录
    if request.session.has_key('islogin'):
        # 用户已登录
        return redirect('/index')
    else:
        # 用户未登录
        # 获取cookie user
        if 'user' in request.COOKIES:
            # 获取记住的用户名
            user = request.COOKIES['user']
            # pw = request.COOKIES['pw']
        else:
            user = ''
            # pw = ''
        return render(request, 'login.html', {'user': user})

def login_check(request):
    '''登录校验视图'''
    # request.POST 保存的post方式提交的参数
    # request.GET 保存的get方式提交的参数
    # 1.获取提交的用户名和密码
    user = request.POST.get('user')
    pw = request.POST.get('pw')
    remember = request.POST.get('remember')  # None on 值

    # 2.进行登录的校验
    # 实际开发根据用户名和密码查找数据库
    if user == 'admin' and pw == '1234':
        response = redirect('/index')
        if remember == 'on':
            response.set_cookie('user', user, max_age=7*24*3600)
            # response.set_cookie('pw', user, max_age=7*24*3600)

            # 记住用户登录状态
            # 只有session中有islogin,就认为用户已登录
            request.session['islogin'] = True
        # 3.返回应答
        return response
    else:
        return redirect('/login')


def ajax_test(request):
    '''显示ajax页面'''
    return render(request, 'ajax_test.html')

def ajax_handle(request):
    '''ajax请求'''
    # 返回的json数据 {'res': 1}
    return JsonResponse({'res': 1})


def set_cookie(request):
    '''设置一个cookie信息'''
    response = HttpResponse('设置cookie')
    # 设置一个cookie信息,名字num,值1
    response.set_cookie('cookie', 1, max_age=14*24*3600)  # max_age=14*24*3600 单位 s
    response.set_cookie('cookie2', 1, expires=datetime.now()+timedelta(days=14))  # expires 单位 日期
    # 返回response
    return response

'''
cookie特点:
    1.以键值对方式进行存储,存储在浏览器上
    2.通过浏览器访问一个网站时,会将浏览器存储的跟网站相关的所有cookie信息发送给该网站的服务器。request.COOKIES
    3.cookie是基于域名安全的
    4.cookie是有过期时间的,如果不指定,默认关闭浏览器之后cookie就会过期
     response.set_cookie('num', 1, max_age='秒', expires=)  # 后面两个参数都可以设置过期时间 
设置cookie,需要一个HttpResponse类的对象或者是它子类的对象
HttpResponseRedirect, JsonResponse
'''

def get_cookie(request):
    '''获取cookie的信息'''
    cook = request.COOKIES['cookie']
    return HttpResponse(cook)

def set_session(request):
    '''设置session'''
    request.session['user'] = 'user'
    request.session['age'] = 18
    return HttpResponse('设置session')

def get_session(request):
    '''获取session'''
    user = request.session['user']
    age = request.session['age']
    return HttpResponse(user+':'+str(age))

'''
session存储在服务器端
session的特点:
    1.session的以键值对进行存储
    2.session依赖于cookie。唯一的标识码保存在sessionid cookie中
    3.session也是有过期时间,如果不指定,默认两周就会过期
对象及方法
通过HttpRequest对象的session属性进行会话的读写操作
1.以键值对的格式写session
request.session['键'] = 值
2.根据键值读取值
request.session.get('键', 默认值)
3.清除所有的session,存储中删除值部分
request.session.clera()
4.清除session数据,在存储中删除session的整条数据
request.session.flush()
5.删除session中的指定键及值,在存储中只删除某个键及对应的值
del request.session['键']
6.设置会话的超时时间,如果没有指定过期时间则两个星期后过期
request.session.set_expiry(value)
    如果value是一个整数,会话session_id cookie将在value秒没有活动后过期
    如果value为0,那么用户会话的session_id cookie将在用户的浏览器关闭时过期
    如果value为None,那么会话的session_id cookie两周之后过期
'''

'''
cookie : 记住用户名,安全性要求不高
session : 涉及到安全性要求比较高的数据,银行卡,密码
'''

登录判断路由

# 登录判断装饰器
def login_required(func):
    def wrapper(request, *args, **kwargs):
        # 判断用户是否登录
        if request.session.has_key('islogin'):
            # 用户已登录
            return func(request,  *args, **kwargs)
        else:
            # 用户未登录跳转登录页面
            return redirect('/login')
        return wrapper

反向解析

项目文件的urls.py,配置namespace

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    # 配置namespace时需要在inclued中传入app_name
    path('', include(('apptest.urls', 'apptest'), namespace='apptest'))
]

应用文件urls.py,path配置name属性

path('index/', views.index, name='index'),

HTML文件中

<!--{% url 'namespace:name' %}-->
<a href="{% url 'apptest:index' %}">跳转首页</a>

视图中重定向使用反向解析

# 反向解析跳转首页,导入reverse模块
from django.urls import reverse
def test_redirect(request):
    url = reverse('apptest:index')
    return redirect(url)

中间件

获取浏览器的ip地址

ip_list
user_ip = request.META['REMOTE_ADDR']
# 通常用来禁止ip
if user_ip in ip_list:
    return HttpResponse('禁止访问')

# 可以使用装饰器来禁止ip访问所有页面
EXCLUDE_IPS = []
def blocked_ips(view_func):
    def wrapper(request, *view_args, **view_kwargs):
        # 获取浏览端的ip地址
        user_ip = request.META['REMOTE_ADDR']
        if user_ip in EXCLUDE_IPS:
            return HttpResponse('禁止访问')
        else:
            return view_func(request, *view_args, **view_kwargs)
    return wrapper

使用中间件:中间件会在使用V视图时执行
1)应用文件下新建 middleware.py 文件
2)定义中间件类

from django.http import HttpResponse

class BlockedIPSMiddleware(object):
    '''中间件类'''
    EXCLUDE_IPS = []
    # process_view预留函数名,不能变
    def process_view(request, view_func, *view_args, **view_kwargs):
        '''视图函数调用之前会调用这个函数'''
        # 禁止一系列ip
        def blocked_ips(view_func):
            def wrapper(request, *view_args, **view_kwargs):
                # 获取浏览端的ip地址
                user_ip = request.META['REMOTE_ADDR']
                if user_ip in BlockedIPSMiddleware.EXCLUDE_IPS:
                    return HttpResponse('禁止访问')
    '''预留函数:
    __init__ : 服务器响应第一个请求的时候调用一次
    process_request : 是在产生request对象,进行url匹配之前调用
    process_view : 是url匹配之后,调用视图函数之前
    process_response : 视图函数调用之后,内容返回给浏览器之前
    process_exception : 视图函数出现异常,会调用这个函数
    如果注册的多个中间伯类中包含process_excetion函数的时候,调用的顺序跟注册的顺序是相反的
    '''
    
class TestMiddleware(object):
    '''中间件类'''
    def __init__(self):
        '''服务器重启之后,接收第一个请求时调用'''
        print('----init----')

    def process_request(self, request):
        '''产生request对象之后,url匹配之前调用'''
        print('----process_request')
        # return HttpResponse('在这调用返回结果会直接执行process_response')

    def process_view(self, view_func, *view_args, **view_kwargs):
        '''在url匹配之后,调用视图函数之前'''
        print('-----process_view----')
        return HttpResponse('在这调用返回结果会直接执行process_response,不会执行view')

    def process_response(self, request, response):
        '''视图函数调用之后,内容返回浏览器之前'''
        print('-----process_response----')
        return response

    def process_exception(self, request, exception):
        '''视图函数发生异常时调用'''
        print('----process_exception----')

3)注册中间件

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',
    'apptest.middleware.BlockedIPSMiddleware',  # 注册中间件类
]

四、T模板

1、新建一个文件夹 templates (有则不用创建),并且在项目文件中setting.py中的TEMPLATES列表中指明路径

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 直接修改这里,连接路径
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        '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、使用模板文件,在应用下的views.py中再次定义函数

'''
-1 加载模板文件:去模板目录下获取html文件的内容,得到一个模板对象
-2 定义模板上下文:向模板文件传递数据
-3 模板渲染:得到一个标准的html内容
'''
from .models import BookClass

def show_books(request):
    '''显示图书'''
    # 通过 M模型 查找图书的信息 导入图书模型类
    books = BookInfo.objects.all()
    # 使用模板,加载模板文件,这里需要在templates下创建一个books.html的HTML文件
    temp = 'books.html'
    # 定义模板上下文:向模板文件传递数据
    temp_dict = {
        'books': books,
    }
    # 第三个参数是传递给html的参数,字典形式;模板渲染:得到一个标准的html内容
    return render(request, temp, temp_dict)

3、编写templates下的books.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>金庸小说全集</title>
</head>
<body>
<h1>小说信息列表:</h1>
<ol>
    {% for book in books %}
    <li><a href="/books/{{ book.id }}">{{ book.btitle }}</a></li>
    {% endfor %}
</ol>
</body>
</html>

<!--
模板变量:通过字典传递
定义content: {{ content }}
列表变量list: {{ list }} 
for循环:
<ul>
    {% for i in list %}
    <li>{{ i }}</li>
    {% endfor %}
</ul>
-->

4、再次在应用文件中的urls.py添加路由

# from django.conf.urls import url
from django.urls import path, re_path  # 如果需要正则需要导入re_path
from . import views
# 通过url函数设置url路由配置项

urlpatterns = [
    path('index/', views.index), # 建立/index和视图index之间的关系
    path('books/', views.show_books),
]

*静态文件

CSS,JS, images 都是静态文件
项目中新建文件夹 static
并在setting.py中配置

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

后台上传图片

1.新建 static -> media 文件夹
2.配置上传文件保存目录

# settings.py 
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/media')

# 模型类
class PicTest(models.Model):
    '''上传图片'''
    # upload_to= path  相对于media目录来说的路径
    gpic = models.ImageField(upload_to='PicTest')

自定义图片上传

# 视图
# /show_upload
def show_upload(request):
    '''显示上传图片页面'''
    return render(request, 'index.html')

def upload_hanle(request):
    '''上传图片处理'''
    # 上传文件不大于2.5MB,存储在内存中;大于2.5MB,文件内容写到一个临时文件中
    # 1.获取上传图片文件的处理对象
    img = request.FILES.get('pic')
    # img.chunks()  # 返回值是一个生成器
    # 2.创建一个文件
    save_path = '%s/PicTest/%s' % (settings.MEDIA_ROOT, img.name)
    # 3.获取上传文件的内容并写到创建的文件中
    with open(save_path, 'wb') as f:
        # 3.获取上传文件的内容并写到创建的文件夹中
        for content in img.chunks():
            f.write(content)

    # 4.在数据库中保存上传记录
    PicTest.objects.create(gpic='PicTest/%s' % img.name)
    # 5.返回
    return HttpResponse('上传成功!')
<!--HTML页面代码-->
<form method="post" enctype="multipart/form-data" action="/upload_hanle/">
    {% csrf_token %}
    <input type="file" name="pic"><br>
    <input type="submit" value="上传">
</form>

配置urls路由

*T模板扩展

模板文件是使用

通常是在视图函数中使用模板产生html内容返回给客户端

a) 加载模板文件 loader.get_template
获取模板文件的内容,产生一个模板对象

b)定义模板上下文 RequeseCountext
给模板文件传递数据

c)模板渲染产生html页面内容 reder
用传递的数据替换相应的变量,产生一个替换到的表中html标准内容

模板变量

{% for i in list %}
    {{ forloop.counter }}{{ i }}
{% endofr %}
<!--forloop.counter可以显示遍历的第几次-->

{% if 条件 %}
{% elif 条件 %}
{% else %}
{% endif %}
关系比较操作符:> < >= <= == !=
逻辑运算:not and or 

过滤器
模板变量 | 过滤器: 参数
例子: bpub_date | date: 'Y年-m月-d日'
    length : 求长度,字符串,列表
    default: '默认值' : 设置模板变量的默认值
            模板硬编码:
            {{ test | default:'<h1>Hello</h1>'}}
-------------------------------------------------
重点过滤器:
在模板上下文的html标记默认是会被转义的
关闭模板上下文字符串的转义:可以使用 {{ 模板变量 | safe }}
也可以使用(打开/关闭)转义:
{% autoescape off/on %}
    {{ 模板变量 }}
{% endautoescape %}
模板硬编码中的字符串默认不会经过转义,如果需要转义,那需要手动进行转义
{{ test | default:'&lt;h1&gt;Hello&lt;/h1&gt;'}}
--------------------------------------------------
自定过滤器
新建目录 tepmolatetags
py文件:
from django.template import Library
# 创建一个Library类的对象
register = Library()
# 自定义的过滤器,至少有一个参数,最多两个
@register.filter
def mod(num):
    '''判断num是否为偶数'''
    return num%2 == 0

html文件中
{% laod filters %}
<head>......
            

模板继承

<!--父模板base.html-->
<!DOCTYPE html>
<html lang="en">
<link>
    <meta charset="UTF-8">
    <title>{% block title %}父模板{% endblock title %}</title>
    <link rel="stylesheet" href="/static/css/base.css">
</head>
<body>
<h1>导航栏</h1>
{% block temp %}
    <p>父模板的内容</p>
{% endblock temp %}
</body>
</html><!--父模板-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父模板</title>
</head>
<body>
<h1>导航栏</h1>
</body>
</html>
<!--子模板-->
{% extends 'base.html' %}
{% block title %}子模板{% endblock title %}
{% block temp %}
    {{ block.super }}<br>
    子模板的内容
{% endblock temp %}

继承的子模板不能有其他内容,且只能继承一个父模板一次,CSS也能被继承
把所有页面相同的内容放到父模板文件中,不需要放在块中。有些位置页面内容不同,需要在父模板中预留块

验证码

pip install pillow
from PIL import Image, ImageDraw, ImageFont
from django.utils.six import BytesIO

def verify_code(request):
    # 引入随机函数
    import random
    # 定义变量,用于画面的背景色,宽,高 RGB
    bgcolor = (random.randrange(20, 100), random.randrange(20, 100), 255)
    width = 80
    height = 25
    # 创建画面对象
    im = Image.new('RGB', (width, height), bgcolor)
    # 创建画笔对象
    draw = ImageDraw.Draw(im)
    # 调整画笔的point()函数绘制点
    for i in range(0, 100):
        xy = (random.randrange(0, width), random.randrange(0, height))
        fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
        draw.point(xy, fill=fill)
    # 定义验证码的备选值
    str1 = 'qwertyuiopasdfghjklzxcvbnm'
    str1 += 'QWERTYUIOPLKJHGFDSAZXCVBNM1234567890'
    # 随机选取4个值作为验证码
    rand_str = ''
    for i in range(0, 4):
        rand_str += str1[random.randrange(0, len(str1))]
    # 构造字体对象
    font = ImageFont.truetype('msyh.ttc', 18)
    # 构造字体颜色
    fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
    # 绘制4个字
    draw.text((5, 1), rand_str[0], font=font, fill=fontcolor)
    draw.text((20, 1), rand_str[1], font=font, fill=fontcolor)
    draw.text((40, 1), rand_str[2], font=font, fill=fontcolor)
    draw.text((60, 1), rand_str[3], font=font, fill=fontcolor)
    # 释放画笔
    del draw
    # 存入session,用于做进一步验证
    request.session['verifycode'] = rand_str
    # 内存文件操作
    buf = BytesIO()
    # 将图片保存在内存中,文件类型为png
    im.save(buf, 'png')
    # 将内存中的图片数据返回给客户端,MIME类型为图片png
    return HttpResponse(buf.getvalue(), 'image/png')
<!--在HTML中使用验证码-->
<input style="width: 50px" type="text" name="yzm">&nbsp;<img src="/verify_code">
# 获取用户输入的验证码
yzm = request.POST.get('yzm')
# session中保存的验证码
vcode = request.session.get('verifycode')
# 进行验证码校验
if str(yzm).lower() != str(vcode).lower():pass

五、运行Django

1、创建管理员

python manage.py createsuperuser
# 会提示输入一些信息,作为后台管理界面的登录

2、运行程序

python manage.py runserver [ip:port]
# []为可选项,指定ip与端口,如默认则是 127.0.0.1:8000

**浏览器url:127.0.0.1:8000/admin 即可使用管理后台 **

六、网页分页

# V视图 view.py
from django.core.paginator import Paginator
def show_img(request, p):
    '''分布显示图片'''
    # 1.查询出所有
    img = PicTest.objects.all()
    # 2.分布,每页显示10条
    paginator = Paginator(img, 1)
    # print(paginator.num_pages)
    # print(paginator.page_range)

    # 3.获取第 p 页的内容
    if p == '':
        p = 1
    else:
        p = int(p)
    # page是Page类的实例对象
    page = paginator.page(p)
    print(page.number)
    # 4.使用模板
    return render(request, 'show_img.html', {'page':page})

'''
Paginator 类对象的属性
num_pages : 返回分布之后的总页数
page_range : 返回分页后页码的列表
print(paginator.num_pages)
print(paginator.page_range)

Paginator 类对象的方法
page(self, number) : 返回第number页的Page类实例对象
page = paginator.page(1)
print(page.number)

Page 类对象的属性
number : 返回当前面的页码
paginator : 返回对应的Paginator对象

Page 类对象的方法
has_previous : 判断当前页是否有前一页
has_next : 判断当前页是否有下一页
previons_page_number : 返回前一页的页码
next_page_number : 返回下一页的页码
'''
<!--show_img.html文件-->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!-- 显示图片 -->
{% for img in page.object_list %}
{#    {{ img.gpic }}#}
    <img src="/static/media/{{ img.gpic }}" height="300" width="600"/>
{% endfor %}
<!-- 上一页 .has_previous判断是否有上一页 -->
<br>
{% if page.has_previous %}
    <!--.previous_page_number获取上一页-->
    <a href="/show_img{{ page.previous_page_number }}">&lt;上一页</a>
{% endif %}
&nbsp;
<!-- 遍历显示页数 -->
{% for pindex in page.paginator.page_range %}
    <!-- 如果是当前面,页码则不是<a>标签 -->
    {% if pindex == page.number %}
        {{ pindex }}
    {% else %}
        <a href="/show_img{{ pindex }}/">{{ pindex }}</a>
    {% endif %}
{% endfor %}
<!-- 下一页 has_next判断是否有上一页 -->
&nbsp;
{% if page.has_next %}
    <!--.next_page_number获取下一页-->
    <a href="/show_img{{ page.next_page_number }}">下一页&gt;</a>
{% endif %}
</body>
</html>
# urls.py
re_path('show_img(?P<p>\d*)/', views.show_img),

总结

Django 框架设计的大体思路:

1、创建Django项目
2、创建应用文件
3、创建模板夹 templates
Pycharm前三步可一次完成
4、创建静态 static 等文件夹
5、配置 settins.py (时区;STATICFILES_DIRS路径;MEDIA_ROOT路径;如果是Pycharm创建Django项目,注册应用与templates模板会自动完成)
1.配置其他数据库
2.编写项目文件下的 init.py 配置数据库
6、编写模型(APP文件下models.py)
1.执行迁移文件
2.创建管理员
3.配置APP下的admin.py后台管理模型
4.可使用管理员登录 管理后台
7、编写 views.py
8、配置项目urls.py
9、配置APP下的urls.py
10、根据需要配置APP下的中间件 middleware.py

相关文章

网友评论

      本文标题:Python - Django快速入门

      本文链接:https://www.haomeiwen.com/subject/biiorctx.html