Django框架介绍

Django框架

Django.pdf

框架思想

  • MVC思想

    • M:model 模型
      V:view 视图
      C:controller 控制器
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZHbvB871-1595435870776)(C:\Users\dujun\AppData\Roaming\Typora\typora-user-images\image-20200420232853449.png)] - ```
      流程:
      1、Browser请求Controller
      2、Controller接收到请求后的数据传给Model
      3、Model去访问数据库
      4、数据库返回信息给Model
      5、Model返回信息给Controller
      6、Controller把数据交给View处理
      7、View将处理好的信息返回给Controller
      8、Controller将信息返回给Browser

在这里插入图片描述

  • MVT思想(Django使用的思想)

    • M:model 模型
      V:view 视图
      T:template 模板
      

虚拟环境

1、virtualenv

2、virtualenvwrapper

3、pipenv

环境

开发环境:程序员写代码的地方
生成环境:项目上线的环境

项目介绍

创建项目和app

django-admin startproject '项目名字' #创建项目
python manage.py runserver # 运行项目
python manage.py startapp 'app名字' #创建app
python manage.py help # 查看全部命令

项目结构介绍

  • 项目目录中的文件

    • manage.py:以后和项目交互基本上都是基于这个文件。一般都是在终端输入python manage.py [子命令]。可 以输入python manage.py help看下能做什么事情。除非你知道你自己在做什么,一般情况下不应该编辑这个文件。
      
      settings.py:本项目的设置项,以后所有和项目相关的配置都是放在这个里面。
      
      urls.py:这个文件是用来配置URL路由的。比如访问http://127.0.0.1/news/是访问新闻列表页,这些东西就需 要在这个文件中完成。
      
      wsgi.py:项目与WSGI协议兼容的web服务器入口,部署的时候需要用到的,一般情况下也是不需要修改的。
      
      
  • app中的文件

    • __init__.py 说明目录是一个Pyyhon模块
      
      model.py 写和数据库相关的内容
      
      views.py 接受请求,处理数据,与Model和Template交互
      
      test.py	 写测试代码的文件
      
      admin.py 网站后台管理相关的文件
      

url映射

from django.contrib import admin
from django.urls import path
from django.http import HttpResponse
from news.views import news  #导入news  app中的视图函数

def index(request):
    return HttpResponse('<h1>首页</h1>')

def books(request):
    return HttpResponse('books')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',index),			#映射路由
    path('news/',news),
    path('books/',books)
]

DEBUG模式

在settings.py中设置
1、开启了debug模式,那么修改代码后按ctrl+s保存后,Django会自动重启项目
2、Django项目中代码出现了问题,会在浏览器页面和控制台中打印出错误信息
3、如果项目上线了,需要关闭debug模式,不然错误信息会暴露在浏览器页面中
4、关闭了debug模式后需要指定allowed_host,日志文件

视图与URL映射

指定url传递的参数的类型

两种参数:
1、在/后面传递的参数

path('<int:bid>',views.books)
这个参数可以规定类型,上面规定int
查看方式:from django.urls import converters

2、在?后面传递的参数

这种参数是直接在视图函数中通过request方法获取
xxx/?name=ddd
name = request.GET.get('name')   #name为ddd

URL模块化

include函数,将url路由模块化,分别在每个app下urls.py文件中映射好对应函数,然后集中在项目的urls.py中映射

作用:

分工合作,防止导入代码冲突
from django.urls import include

在项目下的urls.py文件下:
path('books/',include('books.urls')),

在books(app)下的urls.py文件下:
from django.urls import path
from . import views

urlpatterns = [
    path('',views.books),
]

重定向、反转、url命名、应用命名空间、实例命名空间

redirect、reverse、url命名、应用命名空间四个一般同时使用,实例命名空间一般少用

redirect、reverse、url命名、应用命名空间

front(app)下的views.py

from django.shortcuts import redirect,reverse  #导入对应的重定向和反转函数
from django.http import HttpResponse

def front(request):
    name = request.GET.get('name')  #判断前台界面是否有传参数name
    if name:
        return HttpResponse('front页面')  #有传就返回到front页面
    else:
		return redirect(reverse('front:login'))  #否则重定向到对应的app名为front下的url名为login的url路由中

front(app)下的urls.py

from django.urls import path
from . import views

app_name = 'front'  #应用命名空间

urlpatterns = [
    path('',views.front),
    path('signin/',views.login,name='login') #name为url命名
]
实例命名空间

是指在项目下的urls.py文件下的path函数中,如果一个app映射了对应的路由,就相当于创建了一个实例,下面是创建了两个实例,分别对应的路由是 cms1/ 和 cms2/

项目的urls.py下:

 path('cms1/',include('cms.urls', namespace='cms1')),  #namespace为实例命名空间关键字,相当于实例名字
 path('cms2/',include('cms.urls',namespace='cms2')),

cms(app)下的views.py:

def cms(request):
    name = request.GET.get('name')
    if name:
        return HttpResponse('cms页面')
    else:
        # return redirect('signin/')
        current_namespace = request.resolver_match.namespace #相对应这边需要接收对应的实例名字
        print(current_namespace)
        return redirect(reverse('{}:login'.format(current_namespace))) #反转到对应的实例下的url名为login下的url路由中

cms(app)下的urls.py:

from django.urls import path
from . import views

app_name = 'cms'

urlpatterns = [
    path('',views.cms),
    path('signin/',views.login,name='login'),
]
reverse反转中传递参数
传递/形式的参数

views.py下:

return redirect(reverse('front:login',kwargs={'fid':1})) # 传递的参数为fid,由于这是/形式的参数,对应的函数中也需要接收,还有对应url路由映射中也需要传递
def login(request,fid): #这里需要写参数fid
	return HttpResponse('front:login,接收到的参数:{}'.format(fid))

urls.py下:

path('signin/<fid>',views.login,name='login') #这里需要<fid>
传递?形式的参数

views.py下:

param = reverse('front:login') + '?name=ddd'  #这里是跳转到url名为login对应的url路由下
return redirect(param)

def login(request):
    param  = request.GET.get('name')
    return HttpResponse('front:login,接收到的参数:{}'.format(param))

指定默认参数

是指函数中给定的参数给它个默认值

例如:

def index(request,bid=1):  #这里的参数bid默认为1
	return HttpResponse('xxx')

re_path函数

urlpatterns = [ 
    re_path(r"^$",views.article), 
    re_path(r"^article_list/(?P<year>\d{4})/",views.article_list), 
    re_path(r"^article_list/(?P<mouth>\d{2})/",views.article_mouth) 
]
https://blog.csdn.net/dxcve/article/details/82108479

Template

Django模板引擎:DTL(django template language)

引用模板

1、在视图函数中使用render函数
2、创建对应的templates文件夹存放模板文件
3、在settings.py文件中设置模板的路径

模板变量

在视图函数中可以传递变量到模板中:render函数
模板中使用变量:{{}}方式
模板内置的变量:forloop.counter/forloop.first/forloop.last......

模板标签

通过{% %}方式使用标签
类型:if、for、url

模板过滤器

内置的过滤器:https://blog.csdn.net/a599174211/article/details/82751693

自定义过滤器:https://docs.djangoproject.com/zh-hans/2.2/howto/custom-template-tags/

{{需要过滤的值|过滤器函数:过滤器参数}}

模板结构优化

include和extends

为了不重复写代码

include标签引入模板
extends标签继承模板,模板中可以重写父模板中的block标签

加载静态资源文件

静态资源:CSS、JavaScript文件以及常用图片(不经常变动的资源)

  • 首先看settings.py中

    • INSTALLED_APP下是否有'django.contrib.staticfiles'
      
      如果有:
          STATIC_URL = '/static/'  # 设置App的静态资源文件夹static
          STATICFILES_DIRS = [
              os.path.join(BASE_DIR,'staitc') #设置根目录的静态资源文件夹static
          ]			
      
      如果没有:
      	可以在项目目录下的urls.py中添加:
      	from django.conf.urls.static import static
      	from django.conf import settings
      	urlpatterns = [
          path('admin/', admin.site.urls),
      ] + static(settings.STATIC_URL, document_root=settings.STATICFILES_DIRS[0])
      #在这里加了static后,可以在url中访问地到静态资源文件
      
  • 模板中引用

    • {% load static%}
      <link rel="stylesheet" href="{% static 'color.css' %}">
      <img src="{% static 'zuanshi.png'%}" alt="">
      
    • 如果想省略{% load static %}语句

      • 在settings.py下的TEMPLATES中添加
        'builtins':['django.templatetags.static']
        

加载媒体资源

媒体资源:经常变动的资源(用户头像、歌曲文件等)

  • MEDIA_URL

    • 用于设置媒体资源的路由地址
  • MEDIA_ROOT

    • 用于获取media文件夹在计算机系统的完整路径信息
  • settings.py中

    • MEDIA_URL = '/media/'
      MEDIA_ROOT = os.path.join(BASE_DIR,'media')
      
  • 配置属性设置后,还需将media文件夹注册到Django里,让Django知道如何找到媒体文件,否则无法在浏览器上访问到该文件夹的文件信息

    • 根目录下的urls.py

      • from django.urls import re_path
        from django.views.static import serve
        from django.conf import settings
        
      • urlpatterns = [
        	re_path('media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT},name='media')
        ]
        

Django<–>数据库

原生SQL和ORM模型方式

  • 通过写原生SQL语句
  • ORM模型:
    • object relational mapping 对象关系映射,通过把Python写好的类对象映射成数据库的表,来达到更好的操作数据库的效果,会把对应的python对象中操作转换为SQL原生语句,从而去查询或者修改数据库

前提

Django去操作数据库,实际上还是通过Python代码。因此想要使用Django去操作MySQL的话,需要安装驱动程序去创建Django与数据库之前的连接接口。
驱动程序:pymysql、mysqlclient等
Django也有封装好的接口对象:connection  

连接操作数据库

使用封装好的connection对象:
	需要在settings.py中写好对应的数据库信息,connection对象会自动去读取
	连接并操作:
		from django.db import connection
		cursor = connection.cursor() #创建游标
        cursor.execute('select * from books') #通过原生SQL语句去操作数据库
    	cursor.fetchall()
 
 使用驱动程序pymysql:
 	不需要在settings.py中配置数据库信息
 	from pymysql import *
 	connection = connect(host='127.0.0.1', port=3306, database='django0423', user='root', passwd='root')   #这里需要填写对应的数据库信息
 	cursor = connection.cursor()
        cursor.execute('select * from books')
    	cursor.fetchall()

小任务:图书管理系统

实现展示数据库中图书的信息,添加和删除功能

ORM模型应用

创建ORM模型
在models.py下创建
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20,null=False, default='')
    author = models.CharField(max_length=20, null=False, default='')
    price = models.FloatField(default=0)
将创建好的对象映射到数据库中
1、settings.py下配置好数据库信息,INSTALLED_APPS中添加app名字
2、在命令行终端项目路径下输入:python manage.py makemigrations 生成迁移脚本文件
3、python manage.py migrate 将迁移脚本文件映射到数据库中
ORM增删改查
from django.shortcuts import render
from .models import Book


def index(request):
    # book = Book(name='python',author='guishu',price=99) #实例化对象
    # book.save()	#保存到数据库中
    # print(book)

    # 查询数据
    # book = Book.objects.get(pk=1)
    # print(book)
    # book = Book.objects.filter(name='python')
    # print(book)
    # book = Book.objects.all()
    # print(book)

    # 删除数据
    # book = Book.objects.filter(pk=1)
    # book.delete()

    #修改数据
    # book = Book.objects.get(pk=2)
    # book.name = 'go'
    # book.save()
    # book = Book.objects.filter(pk=3)
    # for b in book:
    #     b.name = 'go'
    #     b.save()
    return render(request,'index.html')
模型常用字段

https://www.cnblogs.com/ivqi/p/10074877.html

AutoField
BigAutoField
BooleanField 	#可以用作伪删除is_delete
CharField		#相当于varchar
DateTimeField	
EmailField
ImageField
FloatField
IntegerField
BigIntegerField
PositiveIntegerField
SmallIntegerField
PositiveSmallIntegerField
TextField
UUIDField
URLField
Field的常用参数
null
db_column # 修改字段名字不常用
default
primary_key
unique
max_length
模型中Meta配置
对于一些模型级别的配置。我们可以在模型中定义一个类,就做Meta。然后再这个类中添加一些类属性来达到控制模型的作用。
例如:修改表名关键字: db_table 
	 排序关键字: ordering

例子:
class Book(models.Model):
    id = models.AutoField(primary_key=True) #定义字段
    price = models.FloatField(default=0)
    
    class Meta:
        db_table = 'book_model'  #修改表名
        ordering = ['-id','price']  #views中查询的顺序是按id倒序和price正序

外键和表
外键引用的表是在同一个app中:
models.ForeignKey('对应模型中的类名',on_delete=models.CASCADE) #级联删除

外键引用的表为自身:
class Comment(models.Model):
    content = models.TextField()
    user_comment = models.ForeignKey('self',on_delete=models.CASCADE) # self可以替换成自己的类名(Comment)

外键引用的表是不在同一个app中:
models.ForeignKey('appname:对应模型中的类名',on_delete=models.CASCADE) #级联删除

在视图中绑定外键

def article(request):
    # category = Category(name='LOL') 	# 外键表
    # category.save() 					# 若没有记录则先生成记录
    
    category = Category.objects.get(pk=2) # 主键为2的记录
    
    article = Article(name='zuoyi',author='zuoyi')
    article.category=category 			# 外键绑定对应的Category表中的主键为2的记录
    article.save()						# 保存

    article = Article.objects.first()
    print(article.category.name) 		# 直接获取外键表中的数据

    return HttpResponse('success')

外键和表的关系:

on_delete=models.CASCADE
on_delete=models.PROTECT
on_delete=models.SET_NULL
on_delete=models.SET_DEFAULT
on_delete=models.SET()
on_delete=models.DO_NOTHING
查询操作
get():只能返回一条数据,Books.objects.get(pk=1)
all():返回QuerySet查询集
filter():条件查询,返回QuerySet查询集,可以通过逗号连接多个条件,Books.objects.filter(pk=x,name=xxx)
exclude():filter函数取反,Books.objects.exclude(pk=3) 查询主键不为3的记录
__exact:精确查询相当于=号,区分大小写。iexact不区分
__contains:like查询(BINARY LIKE ‘%xxx%’),区分大小写。icontains不区分大小写
__startswith:like查询(BINARY LIKE 'x%')
__gt:大于。gte大于等于
__lt:小于。lte小于等于
__in:判断数据是否在给定的值里(__in=[1,3])
__range:判断数据是否在给定的范围里(__range=(x,x)),常用来判断日期
__date:针对date或datetime类型的字段(__date=datetime(year=xx,month=xx,day=xx))
__year:__year=2019
__time:__time=(hour=10,minute=20,second=30)
聚合函数
aggregate:返回聚合函数后的字段和值,返回的对象是一个字典
annotate:在查询结果集上添加一个字段,该字段是使用了聚合函数的字段,会对当前模型的主键进行分组,返回一个QuerySet查询结果对象集(可遍历)
from django.db.models import Avg,Sum,Count,Min,Max
Avg:Book.objects.aggregate(my_avg=Avg('price')) # my_avg是名字
Count('xxx')
Sum:book = Book.objects.aggregate(Sum('bookorder__price')) #bookorder是另一个模型的类名(这里bookorder模型与Book模型是有外键的关系,所以才可以跨表查询)
Mix('xxx')
Max('xxx')
F表达式和Q表达式
from django.db.models import F,Q
F表达式:适用于更新数据
	例子:
	#把三国演义书的价格提高10元
    book = Book.objects.filter(name='三国演义').update(price=F('price')+10)
    不使用F表达式:
    book = Book.objects.filter(name='三国演义')
    for b in book:
       b.price += 10
       b.save()
Q表达式:适用于多个查询条件
	例子:
	# 查询书的价格大于等于98 或 评分大于4.5的书籍
    book = Book.objects.filter(Q(price__gte=98)|Q(rating__gt=4.5))
    # 这里的|也可以换成&,取反Q表达式~Q
QuerySet

查看SQL语句

from django.db import connection
print(connection.queries)  #查看SQL语句

针对QuerySet对象:
例如:book=Book.objects.filter('xxx')
	 print(book.query) #查看SQL语句

QuerySet方法

filter
exclude
annotate
order_by
values
values_list
select_related # 一对一
prefetch_related #多对一、多对多
create
get_or_create
exists
update
切片操作:[1:3]

QuerySet对象在何时会被转换为SQL语句去执行?

1、迭代:QuerySet对象被迭代的时候
2、使用切片操作
3、调用len函数
4、调用list函数
5、判断:对某个QuerySet进行判断,也会被转换成SQL语句去执行
迁移脚本文件过程命令
  • 迁移过程

    • 1、使用makegrations生成迁移脚本文件 #文件存放在app文件下的migrations目录下
      2、migrate将迁移脚本文件迁移到数据库中:
      	2-1:将迁移脚本中的命令转换成对应的SQL语句并在数据库中执行
      	2-2:执行成功后,在数据库下的migrations表中记录此次迁移过程 	# 下面内容有引用此2-2阶段
      
  • makemigrations:生成迁移脚本文件

    • python manege.py makemigrations app_label  # 指定生成app的迁移文件,不指定则生成所有app和系统的迁移脚本文件
      
    • python manage.py makemigrations --name '迁移脚本文件名字'
      
    • python manage.py makemigrations --empty  # 生成一个空的迁移脚本
      
  • migrate:将生成好的迁移脚本映射到数据库中

    • python manege.py migrate app_label
      
    • python manege.py migrate app_label migrationname #将app下的指定号的迁移脚本文件映射到数据库中
      
    • python manege.py migrate --fake #只执行上述迁移过程的2-2阶段,并不会将迁移脚本文件转换成SQL语句去修改数据库。--fake适用于迁移脚本文件没有缺少,但是数据库下的迁移记录缺少的情况
      
    • python manege.py migrate --fake-initial # 和fake类似,也不会真正的执行迁移脚本文件,只是添加了迁移记录。适用于迁移脚本文件有缺少,且数据库下的迁移记录也缺少的情况。
      
      实现过程:
      		1、app模型字段和数据库保持一致
      		2、将app下的migrations目录下的迁移脚本文件都删除掉
      		3、删除数据库migrations表下对应app的记录
      		4、python manege.py makemigrations app_label
      		5、python manege.py migrate app_label --fake-initial
      
  • showmigrations:查看某个app下的迁移文件。如果后面没有app,那么将查看INSTALLED_APPS中所有的迁

    移文件

    • python manage.py showmigrations [app名字]
      
  • sqlmigrate:查看某个迁移文件在映射到数据库中的时候,转换的SQL语句

    • python manage.py sqlmigrate book [0001_initial]
      
根据已有的表自动生成模型

1、在settings.py中指定想要生成模型的数据库

2、python manage.py inspectdb > models.py #将数据库中的表转换为ORM模型的类对象语句并保存在models.py文件中

3、得到对应的模型后,需要去修正模型(因为会将一整个数据库的表转换后的模型语句都放在一起,我们要将他们手动分配到对应的app下)

  • 模型名、模型所属app、模型外键引用、删除Meta类下的managed=False语句、有多对多的模型时,将中间表注释掉,添加ManyToManyField字段
    
  • 修改完后,执行python manage.py makemigrations和python manage.py migrate --fake-initial

视图高级

Django限制请求method、页面重定向301和302

限制请求装饰器:

from django.views.decorators.http import require_http_methods,require_GET,require_POST,require_safe


# 视图高级部分
def index(request):
    return render(request,'index.html')

@require_http_methods(['GET','POST'])
def view_methods(request):
    # data = request.POST['pass']         #获取不到则会将错误信息暴露在页面上
    data = request.POST.get('passw')     #获取不到不会将错误信息展现在页面上
    if data == '123':
        print(data)
    return HttpResponse('view_methods')

# @require_GET
# @require_POST
@require_safe  #这个相当于GET和HEAD请求  因为都不会对数据库有修改,所以称作是safe
def view2(request):
    return HttpResponse('views')

def view_redirect(request):
    # return redirect(reverse('index'),permanent=False) #暂时重定向301
    return redirect(reverse('index'),permanent=True) #永久重定向302

HttpRequest对象

WSGI:Python社区创建了Web服务器网关接口(WebServerGatewayInterface,WSGI),这是创建跨服务器和框架工作的PythonWeb组件的标准。
Django在接收到http请求之后,会根据http请求携带的参数以及报文信息创建一个WSGIRequest对象,并且作 为视图函数第一个参数传给视图函数。也就是我们经常看到的request参数。在这个对象上我们可以找到客户端 上传上来的所有信息。这个对象的完整路径是django.core.handlers.wsgi.WSGIRequest。
  • WSGIRequest对象常用属性

    • WSGIRequest对象上大部分的属性都是只读的。因为这些属性是从客户端上传上来的,没必要做任何的修改。
      
    • def view_adv(request):
          # print(request)
          # print(request.path)
          # print(request.path_info)
          # print(request.method)
          # print(request.GET)
          # print(request.POST)
          # print(request.COOKIES)
          # print(request.session)
          # print(request.META)
          # print(request.content_type)
      
  • WSGIRequest对象常用方法

    •     # print(request.get_full_path())
          # print(request.is_secure())
          # print(request.is_ajax())
          # print(request.get_host())
          # print(request.get_raw_uri())
      

HttpResponse对象

Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个HttpRequest对象传给视图 函数。那么视图函数在处理完相关的逻辑后,也需要返回一个响应给浏览器。而这个响应,我们必须返回 HttpResponseBase或者他的子类的对象。而HttpResponse则是HttpResponseBase用得最多的子类
  • 常用属性

    •     # response = HttpResponse(content_type='text/plain;charset=utf-8')
          # response.content = 'response:view_adv'
      
          # response.status_code = 404
          # response['X-Access-Token'] = 'xxxx'。
          # return response
      
  • 常用方法

    • 1.set_cookie:用来设置cookie信息。 
      2.delete_cookie:用来删除cookie信息。 
      3.write:HttpResponse是一个类似于文件的对象,可以用来写入数据到数据体(content)中。
          # response.write('sdsdsd')
      

JsonResponse

用来对象dump成json字符串,然后返回将json字符串封装成Response对象返回给浏览器。并且他的Content- Type是application/json。
from django.http import JsonResponse

def index(request):
    data = {
            'username':'ddd',
            'pass':'asdasd'
        }
    data2 = ['哈哈','dasdasd']  #要传递非字典,需要去设置safe参数为False
    # return  JsonResponse(data=data) #传递字典
    return  JsonResponse(data=data2,safe=False,json_dumps_params={'ensure_ascii':False}) #要传递非字典,需要去设置safe参数为False,传递中文需要设置json_dumps_params参数

类视图

View
  • django.views.generic.base.View是主要的类视图,所有的类视图都是继承自他。如果我们写自己的类视图,也 可以继承自他。然后再根据当前请求的method,来实现不同的方法。比如这个视图只能使用get的方式来请求, 那么就可以在这个类中定义get(self,request,*args,**kwargs)方法。以此类推,如果只需要实现post方法,那么 就只需要在类中实现post(self,request,*args,**kwargs)。
    
  • views.py

    • from django.views import View
      class ArticleIndexView(View):
          def get(self,request,aid):
              params = request.GET.get('name')
              return HttpResponse('这是get方法-斜杠参数:{},问号参数:{}'.format(aid,params))
      
          def post(self,request):
              name = request.POST.get('name')
              return HttpResponse('这是post方法-{}'.format(name))
      
          def http_method_not_allowed(self, request, *args, **kwargs):
              return HttpResponse('您的请求方式为{},只允许为get和post'.format(request.method))
      
  • urls.py

    • urlpatterns = [
          path('', views.ArticleIndexView.as_view(), name='index'),  #这里是post请求
          path('<int:aid>',views.ArticleIndexView.as_view(),name='index'), #这里是get请求
          ]
      
TemplateView
  • django.views.generic.base.TemplateView,这个类视图是专门用来返回模版的。在这个类中,有两个属性是经常需要用到的,一个是template_name,这个属性是用来存储模版的路径,TemplateView会自动的渲染这个变量指向的模版。另外一个是get_context_data,这个方法是用来返回上下文数据的,也就是在给模版传的参数的
    
  • 有两种方式来应用模板类

    • 第一种:直接指定路由‘xxx/’去对应模板about.html(不需要写模板视图类)

      • urlpatterns = [	path('about/',TemplateView.as_view(template_name='about.html')),  #直接指定路由about/对应模板about.html(不需要写模板视图类)
        ]
        
    • 第二种:写模板视图类

      • views.py

        • from django.views.generic import TemplateView
          class Template_view(TemplateView):
              template_name = 'about2.html' #指定模板文件
          
              def get_context_data(self, **kwargs):   #这个方法作用是可以将变量传递到模板中
                  context = super().get_context_data(**kwargs)
                  # print(context)
                  # context['username']='ddd'
                  # context['pass']='ddd'
                  context = {
                      'age':18,
                      'gender':'man'
                  }       #这里写了字典,则上面的username和pass在模板中调用不了
                  return context
          
      • urls.py

        • urlpatterns = [
          path('about2/',views.Template_view.as_view(template_name='about2.html'))
          ]
          
ListView
在网站开发中,经常会出现需要列出某个表中的一些数据作为列表展示出来。比如文章列表,图书列表等等。在 Django中可以使用ListView来帮我们快速实现这种需求。
  • views.py

    • from django.views.generic import ListView
      class List_view(ListView):
          model = Article                     # 指定模型(所以要去创建模型)
          template_name = 'list_view.html'    # 指定模板文件
          paginate_by = 3                     # 页面中每一页显示的数据的数量(3个数据分一页)
          context_object_name = 'articles'    # 指定模型数据在模板中的参数名称(模型的数据赋给变量名为articles,然后传递到模板中去使用)
          ordering = 'create_time'            # 指定模型数据的排序方式
          page_kwarg = 'page'                 # 在页面中获取第几页的数据的参数名称,默认是page(?page=2,获取第二页)
      
          def get_context_data(self, **kwargs):  #获取上下文的数据。这个方法作用是可以将变量传递到模板中
              context = super().get_context_data(**kwargs)
              print(context)                     #这个context是字典,其中有键paginator和page_obj,分别对应着Paginator和Page类对象
              paginator = context['paginator']
              page_obj = context['page_obj']
              return context
      
          def get_queryset(self):  #提取数据库的数据
              return Article.objects.all() #提取所有数据(可以做过滤)
      
      
  • urls.py

    • urlpatterns = [
      path('listview/',views.List_view.as_view(),name='listview')
      ]
      
  • templates目录下list_view.html

    • <ul>
          {% for article in articles %} 这里的articles是传递过来的,数据库中的数据
          <li>{{ article }}</li>
          {% endfor %}
          <li>{{ paginator.count }}</li>
          <li>{{ paginator.num_pages }}</li>
          <li>{{ paginator.page_range }}</li>
          <br>
          <li>{{ page_obj.has_next }}</li>
          <li>{{ page_obj.has_previous }}</li>
          <li>{{ page_obj.has_other_pages }}</li>
          <li>{{ page_obj.next_page_number }}</li>
          {# <li>{{ page_obj.previous_page_number }}</li> #}
          <li>{{ page_obj.number }}</li>
          <li>{{ page_obj.start_index }}</li>
          <li>{{ page_obj.end_index }}</li>
      </ul>
      
Paginator和Page类

https://www.cnblogs.com/donghaiming/p/11007505.html

  • Paginator和Page类都是用来做分页的。他们在Django中的路径为django.core.paginator.Paginator和

    django.core.paginator.Page。以下对这两个类的常用属性和方法做解释:

  • Paginator常用属性和方法 :

    • 方法:
      	page(number):根据参数number返回一个Page对象。(number为1的倍数)
      属性:
          count:总共有多少条数据
          num_pages:总共有多少页
          page_range:页面的区间。比如有三页,那么就range(1,4)
      
  • Page常用属性和方法:

    • 方法:
          has_next:是否还有下一页 
          has_previous:是否还有上一页
          next_page_number:下一页的页码
          previous_page_number:上一页的页码
      属性:
          number:当前页
          start_index:当前这一页的第一条数据的索引值 
          end_index:当前这一页的最后一条数据的索引值
      

通用分页小练习

  • 效果图:

在这里插入图片描述

  • 思路:

    • 1、利用ListView类和Bootstrap网站来实现
      2、首先下面的分页组件去bootstrap查找并添加到模板中:https://v3.bootcss.com/components/#pagination-default
      3、页面中的数据是从数据库中提取过来的
      4、在视图和模板中完成逻辑代码
      
  • 实现过程:

    • views.py

      • from django.views.generic import ListView
        class List_view(ListView):
            model = Article                     # 指定模型(所以要去创建模型)
            # template_name = 'list_view.html'    # 指定模板文件
            template_name = 'list_view_opt.html'    # 指定模板文件
            # paginate_by = 3                     # 页面中每一页显示的数据的数量(3个数据分一页)
            paginate_by = 1
            context_object_name = 'articles'    # 指定模型数据在模板中的参数名称(模型的数据赋给变量名为articles,然后传递到模板中去使用)
            ordering = 'create_time'            # 指定模型数据的排序方式
            page_kwarg = 'page'                 # 在页面中获取第几页的数据的参数名称,默认是page(?page=2,获取第二页)
        
            def get_context_data(self, **kwargs):  #获取上下文的数据。这个方法作用是可以将变量传递到模板中
                context = super().get_context_data(**kwargs)
                # print(context)                     #这个context是字典,其中有键paginator和page_obj,分别对应着两个对象,对象中有需要的方法
                paginator = context['paginator']
                page_obj = context['page_obj']
                page_range_data = self.get_page_range(paginator,page_obj) #向方法中传递参数并接收返回值
                context.update(page_range_data)
                return context
        
            def get_queryset(self):  #提取数据库的数据
                return Article.objects.all() #提取所有数据(可以做过滤)
        
            def get_page_range(self,paginator,page_obj,page_offset=2):   #创建一个方法来写逻辑代码(如果当前页码是5,则显示3 4 5 6 7这5个分页)
                current_page = page_obj.number        #当前页码
        
                has_left_more = False   # 一个标志位(判断什么时候在模板中加入...符号和第一个分页)
                has_right_more = False # 同上
        
                if current_page <= page_offset+1: #当前页码是否小于3
                    left_range = range(1, page_obj.number) #页码小于3则左边范围从1开始
                else:
                    has_left_more = True #标志位
                    if current_page==4: #如果当前页面为4,则标志位为False。即在模板中不添加...符号
                        has_left_more = False
                    # 3 4 5
                    left_range = range(current_page-page_offset ,page_obj.number) #左边页码范围为2,如果当前页码是5则左边为3和4
        
                if current_page >= paginator.num_pages-page_offset: #当前页码是否大于 最大页码数-2
                    right_range = range(current_page + 1, paginator.num_pages+1) #右边分页
                else: #否则 下面在模板的右边分页中添加...和最后一个分页
                    has_right_more = True #标志位
                    if current_page== paginator.num_pages-page_offset-1:
                        has_right_more = False
                    # 5 6 7
                    right_range = range(current_page+1 ,page_obj.number+page_offset+1) #右边分页范围
        
                page_range = {
                    'left_range':left_range,
                    'right_range':right_range,
                    'has_left_more':has_left_more,
                    'has_right_more':has_right_more,
                }
                return page_range #将变量传入字典中放回到context中,然后可以在模板中引用
        
        
    • list_view_opt.html

      • <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
            <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
        </head>
        <body>
            <h1>listview</h1>
        
            <ul>
                {% for article in articles %}   # 显示数据库中的数据
                    <li>{{ article.id}}-{{article.title}}</li>
                {% endfor %}
        
            </ul>
        
            <nav aria-label="Page navigation">
                <ul class="pagination">
        
                <!--  最前面的<< -->
                    {% if page_obj.has_previous %}
                        <li>
                            <a href="{% url 'listview' %}?page={{ page_obj.previous_page_number }}" aria-label="Previous">
                                <span aria-hidden="true">&laquo;</span>
                            </a>
                        </li>
                    {% else %}
                        <li class="disabled">
                            <a href="#" aria-label="Previous">
                                <span aria-hidden="true">&laquo;</span>
                            </a>
                        </li>
                    {% endif %}
        
                <!-- 中间的跳转标-->
                    {% if has_left_more %}
                        <li><a href="{%url 'listview' %}?page=1">1</a></li>
                        <li><a href="#">...</a></li>
                    {% endif %}
                    {% for left in left_range %}
                        <li><a href="{%url 'listview' %}?page={{ left }}">{{ left }}</a></li>
                    {% endfor %}
        
                    <li class="active"><a href="{%url 'listview' %}?page={{ page_obj.number }}">{{ page_obj.number }}</a></li>
        
                    {% for right in right_range %}
                        <li><a href="{%url 'listview' %}?page={{ right }}">{{ right }}</a></li>
                    {% endfor %}
        
                    {% if has_right_more %}
                        <li><a href="#">...</a></li>
                        <li><a href="{%url 'listview' %}?page={{ paginator.num_pages }}">{{ paginator.num_pages }}</a></li>
                    {% endif %}
                <!--  最后面的>>-->
                    {% if page_obj.has_next %}
                        <li >
                            <a href="{% url 'listview' %}?page={{ page_obj.next_page_number }}" aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                            </a>
                        </li>
                    {% else %}
                        <li class="disabled">
                            <a href="#" aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                            </a>
                        </li>
                    {% endif %}
        
                </ul>
            </nav>
        
        </body>
        </html>
        

错误处理

常用的错误码

404:服务器没有指定过的url
403:没有权限访问相关的数据
405:请求的method错误
400:bad request,请求的参数错误
500:服务器内部错误,一般时代码出bug
502:一般部署的时候见的比较多,一般时nginx启动了,然后uwsgi有问题

错误处理方式:

  • 直接指定错误状态码模板文件

    • 1、项目的templates文件下直接创建404.html、500.html...
      2、在settings.py中修改
      	DEBUG = False
      	ALLOWED_HOSTS = ['127.0.0.1']
      
      在访问网页时如果访问到了没有指定好的url,django中页面会自动跳转到404页面
      
  • 专门定义一个app来处理

    • 在视图中做判断从而redirect到对应的错误处理(404\500)路由下
      

表单

用表单验证数据

前端提交上来的表单数据需要做验证,表单数据验证逻辑在forms.py文件中写(解耦思想,不然都在视图函数里面写)。
Django的表单文件(forms.py)有两个作用:
1、渲染表单模板,即在表单中定义的字段在模板中渲染
2、表单文件中做数据验证(对提交上来的表单数据,在html中form标签中的数据)
表单整体使用流程:
  • 在forms.py中定义一个表单类(继承自django.forms.Form)
  • 在视图中,根据GET或POST请求来做相应的操作
    • GET请求可以将表单渲染到模板中(一般不会使用的操作)
    • POST请求中去获取前端传来的form数据,传递到表单类中去做验证
  • 在froms.py下的表单类中写验证逻辑代码(会牵扯到数据库信息)
表单验证代码
  • views.py文件:

    • from django.shortcuts import render
      from django.http import HttpResponse
      from .froms import ArticleForm
      from django.views import View
      # django.forms.utils.ErrorDict
      from django.forms.utils import ErrorDict
      
      def index(request):
          return HttpResponse('首页')
          
      class ArticleView(View):
          def get(self,request):
              form = ArticleForm()  # 这里给表单类一个空的表单,因为这是get请求,前台不会有表单传输过来
              return render(request,'index.html',{'form':form}) #将form对象传入index.html模板中
      
          def post(self,request):
              form = ArticleForm(request.POST) # 给表单类传递一个前台提交上来的表单数据
              # print(form)           #form是网页的源代码
              if form.is_valid():     # is_valid()其实是表单数据在表单类中进行验证,全部验证通过才返回True
                  telephone = form.cleaned_data.get('telephone')  #通过cleaned_data去获取验证后的telephone表单字段对应的数据
                  # print(telephone)
                  return HttpResponse('验证成功')
              else:
                  # print(form.errors)   # 错误信息是带有标签的html代码
                  # print(type(form.errors))
                  print(form.errors.get_json_data())  # 以字典的形式打印错误信息
      
                  return HttpResponse('验证失败')
      
  • 模板文件index.html

    • <form action="" method="">
          <table>
              {{ form.as_table }} 			#表单渲染模板,这是在table标签中
          </table>
          <input type="submit" value="提交">
      </form>
      <!--{{ form.as_p }}-->  #可以在p标签中进行渲染
      <!--{{ form.as_ul }}--> #ul无序标签中
      
  • forms.py

    • from django import forms
      from django.core import validators
      from .models import User
      
      
      class ArticleForm(forms.Form):
          title = forms.CharField(max_length=10,min_length=2,error_messages={'min_length':'输入的值的最小长度为2'},label='标题')
          content = forms.CharField(widget=forms.Textarea,label='内容')
      
          email = forms.EmailField(required=False)  # EmailField已经是验证了
          # email = forms.CharField(validators=[validators.EmailValidator(message='请输入正确的邮箱')]) #和上面达到类似的效果(使用表单验证器)
      
          reply = forms.BooleanField(required=False) #required 表示这个reply字段在网页中是否一定要输入值,false为不需要输入值
          website = forms.URLField(required=False)
          # 这里只能验证手机号是否输入正确
          telephone = forms.CharField(validators=[validators.RegexValidator(r'1[3456789]\d{9}',message='请输入正确的手机号')])
          # print(telephone)
          pwd1 = forms.CharField(max_length=12)
          pwd2 = forms.CharField(max_length=12)
      
          #验证电话号码 是否 在数据库中已存在,验证一个表单字段
          def clean_telephone(self):
              telephone = self.cleaned_data.get('telephone')
              # print(telephone)
              exists = User.objects.filter(tele=telephone).exists()  #判断手机号在数据库中是否存在
              if exists:
                  raise forms.ValidationError('手机号码已存在')
      
              return telephone
      
          # clean函数可以验证多个表单字段
          def clean(self):
              cleaned_data =super().clean()
      
              pwd1 = cleaned_data.get('pwd1')
              pwd2 = cleaned_data.get('pwd2')
      
              if pwd1 != pwd2:
                  raise forms.ValidationError('两次密码输入不一致')
              return cleaned_data
      
  • models.py

    • from django.db import models
      
      # Create your models here.
      
      class User(models.Model):
          username = models.CharField(max_length=10)
          password = models.CharField(max_length=10)
          tele = models.CharField(max_length=11)
      
  • urls.py

    • from . import views
      from django.urls import path
      
      
      urlpatterns = [
          path('',views.index),
          path('article/',views.ArticleView.as_view())
      
      ]
      
ModelForm

model+form:模型表单

写表单验证时,表单类中的字段几乎都是对应着模型中的字段,当做大项目时,几百个字段时,重复在表单类下写字段就显得比较冗余。

所以有ModelForm类,直接引用model中的字段即可。表单中的其他操作(渲染模板和验证表单数据)不受影响。只是不用写那么多代码

  • models.py

    • from django.db import models
      
      class Front(models.Model):
          title = models.CharField(max_length=5)
          content = models.TextField()
          author = models.CharField(max_length=5)
          create_time = models.DateTimeField(auto_now_add=True)
      
  • forms.py

    • from django.forms import Form,ModelForm
      from django import forms
      from .models import Front
      
      class FrontForm(ModelForm): # 注意:这里是ModelForm
          author = forms.IntegerField()  #这里还是可以自己定义表单字段
          class Meta:
              model = Front
              fields = '__all__'  #引用model中的所有字段
              # fields = ['title']  # 只引用title字段
              # exclude = ['title'] # 引用除了title字段以外的其他字段
      
              error_messages = {    # 由于引用了model字段,不能在字段里面写错误信息,故通过这个方式
                  'title':{     #指定title字段
                      'max_length':'最大长度为5个字符' #错误信息
                  }
              }
      

文件图片上传

  • 思路

    • 1、在模板template中定义好上传的接口(form标签,form标签需要指定属性enctype,input类型是file)
      2、在模型model中写好对应的字段,字段类型也是对应的FileField和ImageField
      3、表单form中使用ModelForm
      4、在视图view中将前端上传上来的文件或图片对象传入表单类中,做表单验证
      5、验证成功后考虑将文件/图片保存在哪个目录下(可以在settings.py指定目录,也可以使用模型的字段中up_load='xxx'参数来指定存放目录)
      

在这里插入图片描述

  • 代码如下:

    • 在模板template中定义好接口

      • <form action="" method="post" enctype="multipart/form-data">
            文件上传:<input type="file" value="文件" name="file"><br>
            图片上传:<input type="file" value="图片" name="image"><br>
            <input type="submit" value="上传">
        </form>
        
    • 在模型中定义对应的类

      • class FileModel(models.Model):
            # upload_to 指定上传上来的文件的目录
            # file = models.FileField(upload_to='files')  #这里指定的路径是目录,如果settings.py中已经配置了MEDIA_ROOT,那么上传上来的文件将会保存在MEDIA_ROOT指定的目录下的files目录下
            file = models.FileField(upload_to='%Y/%m/%d',validators=[validators.FileExtensionValidator(['txt','pdf'])])
        
            image = models.ImageField(upload_to='%Y/%m/%d',null=True)
        
    • 模型中可以指定文件保存的目录,也可以保存在settings.py配置好的目录下(常用)

      • MEDIA_ROOT = os.path.join(BASE_DIR,'media')
        MEDIA_URL = '/media/'
        
    • 表单验证

      • class FileForm(ModelForm):
            class Meta:
                model = FileModel
                fields = '__all__'
        
                error_messages = {
                    'file':{'invalid_extension':'只允许传入txt和pdf格式的文件'}
                }
        
    • 视图任务:接收前端文件/图片数据,然后把文件/图片保存到目录中,添加到数据库记录中

      • from django.shortcuts import render
        from django.http import HttpResponse
        from django.views import View
        from .models import FileModel
        from .forms import FileForm
        
        
        def index(request):
            return HttpResponse('首页')
        
        
        class File_upload(View):
            def get(self,request):
                return render(request,'file_upload.html') #模板文件
        
            def post(self,request):
                file_form = FileForm(request.POST,request.FILES) #给表单类传递数据
        
                # with open('django.jpg','wb') as f: #将上传上来的文件直接保存在当前目录下
                #     f.write(file.read())
        
                # 上传成功之后需要将上传上来的文件保存下载,并在数据库中做记录
                if file_form.is_valid():  #做表单验证
                    file = request.FILES.get('file')  #获取上传上来的文件
                    image = request.FILES.get('image') #获取上传上来的图片
                    FileModel.objects.create(file=file,image=image) # 上传上来的文件保存到数据库指定的目录
                    return HttpResponse('上传成功')
                else:
                    print(file_form.errors.get_json_data()) #打印错误信息
                    return HttpResponse('fail')
        

session和cookie

Django上操作

from django.shortcuts import render
from django.http import HttpResponse
from datetime import datetime
from django.utils.timezone import make_aware,now


def cs(request):
    response = HttpResponse('cookies—session')
    # response.delete_cookie('username')
    # response.set_cookie(key='username',value='ddd',max_age=60,expires=make_aware(datetime(year=2020,month=5,day=3,hour=14)))
    # response.set_cookie(key='username',value='ddd',max_age=60,expires=now())

    # request.session['pass'] = '123'
    # request.session['word'] = 'hhh'
    # p = request.session.get('word')
    # p = request.session['pass']
    # p = request.session.pop('pass')
    # p = request.session.keys()
    # p = request.session.items()
    # request.session.clear()
    # request.session.flush()
    # p = request.session.get('word')
    # request.session.set_expiry(-1)
    # request.session.clear_expired()
    # print(p)
    return response


def get_cs(request):
    cookie = request.COOKIES
    print(cookie)
    cookie_dict = {key:value for key,value in cookie.items()}
    print(cookie_dict)
    print(cookie.get('username'))
    username = cookie.get('username')
    return HttpResponse(username)

memcached

Memcached是一个高性能的分布式的内存对象缓存系统

https://www.cnblogs.com/wayne173/p/5652034.html

特性:

1.保存内存中 
2.重启服务,数据会丢失 
3.LRU算法,根据最近使用的变量,将长时间没有使用的变量删除 
4.memcache服务端是不安全的, 
5.不适合单机使用,对内存的消耗比较大 
6.格式简单,不支持list数据格式

操作和redis差不多

缺点:

安全性的问题,memcached的操作不需要任何用户名和密码,只需要知道memcached服务器的ip地址和端口号即可。因此 memcached使用的时候尤其要注意他的安全性

在Django中使用memcached

settings.py

CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211',}}

views.py

from django.core.cache import cache 
def index(request): 
	cache.set('abc','juran',60) 
	print(cache.get('abc')) 
	response = HttpResponse('index') 
	return response
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值