Python就业班——Django开发与项目实战

1、更新pip

python -m pip install --upgrade pip

2、安装django:pip install django

pip install django==2.2.17

3、生成模板

django-admin startproject mysite

 4、启动服务:python manage.py runserver(默认8080)

python manage.py runserver 8082

 5、创建模块:python manage.py startapp 模块名

python manage.py startapp accounts

 6、路由设置(2.0+)urls.py

 使用正则:

from django.urls import  re_path
from . import views urlpatterns = [ re_path(r'^article/(?P<year>[0-9]{4})/$', views.article), ]

创建模块路由:

from django.urls import path
from oauth import views

urlpatterns = [
    path('index/', views.index),
]

调用模块路由:

from django.urls import path, include

urlpatterns = [
    path('auth/', include('oauth.urls')),
]

设计优雅的URL

_1.使用简洁的URL

_2.给URL指定命名空间namespace

_3.给URL指定名称name

urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    re_path(r'^article/(?P<year>[0-9]{4})/$', views.article, name='article_detail'),

    path('oauth/', include('oauth.urls')),
]

views.py

from django.http import HttpResponse
from django.urls import reverse


def index(request):
    url = reverse('article_detail', args=(2020, ))  # 逆向解析URL
    return HttpResponse('ok:' + url)


def article(request, year):
    return HttpResponse('article:' + year)

oauth\urls.py

from django.urls import path
from oauth import views

urlpatterns = [
    path('index/', views.index, name='index'),
]

views.py

from django.http import HttpResponse
from django.urls import reverse


def index(request):
url = reverse('article_detail', args=(2020, )) # 不带命名空间的逆向解析URL
url_auth = reverse('auth:index') # 带命名空间的URL逆向解析
print(url_auth)
return HttpResponse('ok:' + url)


def article(request, year):
return HttpResponse('article:' + year)

urls.py

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

urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
re_path(r'^article/(?P<year>[0-9]{4})/$', views.article, name='article_detail'),

path('oauth/', include(('oauth.urls', 'oauth'), namespace='auth')),
]

 路由函数include设有参数arg和namespace,参数arg指向项目应用模块的urls.py文件,其数据格式以元组或字符串表示;可选参数namespace是路由的命名空间。
若要对路由设置参数namespace,则参数arg必须以元组格式表示,并且元组的长度必须为2.元组的元素说明如下:

  • 第一个元素为项目模块的urls.py文件,比如(“index.urls”,“index”)的"index.urls",这是代表项目应用index模块的urls.py文件。
  • 第二个元素可以自行命名,但不能为空,一般情况下是以项目应用模块的名称进行命名,(“index.urls”,“index”)的"index"是以项目应用模块index进行命名的。

如果路由设置参数namespace并且参数arg为字符串或元组长度不足2的时候,在运行MyDjango的时候,Django就会提示错误信息。

 7、视图

_1.一个简单的python视图函数,简称视图。

_2.接收一个请求,返回一个响应

_3.响应可以是HTML内容、文本、图像、404、重定向等

定义视图:

urls.py

from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),# 展示当前的时间
    path('time/', views.now_time),
]

views.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:views.py
# Data:2020/11/18 11:51
# Author:LGSP_Harold
from datetime import datetime

from django.http import HttpResponse
# 定义视图
def now_time(request):
    """ 展示系统当前的时间 """
    now = datetime.now()
    html = """
    <html>
        <head>
            <style type="text/css">
                body{{color: red;}}
            </style>
        </head>
        <body>
            now: {0}
        </body>
    </html>
    """.format(now)

    return HttpResponse(html)


if __name__ == '__main__':
    pass

获取get请求参数

from django.http import HttpResponse
from django.urls import reverse


def article(request, year):
    """ 获取url中的参数 """
    print('year: {0}'.format(year))
    # 获取GET参数中的月份
    month = request.GET.get('month', None)  # http://127.0.0.1:8082/article/2020/?month=12&day=31
    print('month: {0}'.format(month))
    month = request.GET.get('month', '11')  # http://127.0.0.1:8082/article/2020/?day=31
    print('month: {0}'.format(month))
    # request.POST.get()
    return HttpResponse('article:' + year)

返回HTML信息:

urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    re_path(r'^article/(?P<year>[0-9]{4})/$', views.article, name='article_detail'),

    path('oauth/', include(('oauth.urls', 'oauth'))),

    # 展示当前的时间
    path('time/', views.now_time),
    # 页面从html加载
    path('now/', views.now_use_file),
]

views.py(通过读取文件方式返回)

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:views.py
# Data:2020/11/18 11:51
# Author:LGSP_Harold
import os
from datetime import datetime

from django.conf import settings
from django.http import HttpResponse
from django.template import loader
from django.urls import reverse


def index(request):
    url = reverse('article_detail', args=(2020, ))  # 不带命名空间的逆向解析URL
    url_auth = reverse('oauth:index')    # 带命名空间的URL逆向解析
    print(url_auth)
    return HttpResponse('ok:' + url)


def article(request, year):
    """ 获取url中的参数 """
    print('year: {0}'.format(year))
    # 获取GET参数中的月份
    month = request.GET.get('month', None)  # http://127.0.0.1:8082/article/2020/?month=12&day=31
    print('month: {0}'.format(month))
    month = request.GET.get('month', '11')  # http://127.0.0.1:8082/article/2020/?day=31
    print('month: {0}'.format(month))
    # request.POST.get()
    return HttpResponse('article:' + year)


# 定义视图
def now_time(request):
    """ 展示系统当前的时间 """
    now = datetime.now()
    html = """
    <html>
        <head>
            <style type="text/css">
                body{{color: red;}}
            </style>
        </head>
        <body>
            now: {0}
        </body>
    </html>
    """.format(now)

    return HttpResponse(html)


def now_use_file(request):
    # 从HTML文件读取内容,并响应

    html = ''
    # 文件名称
    # file_name = os.path.join(settings.BASE_DIR, 'templates', 'index.html')
    # with open(file_name) as f:
    #     html = f.read()

    html = loader.get_template('index.html')

    return HttpResponse(html.template.source)

P.S.3:loader.get_template()获取模板时,请先在settings.py文件添加模板的目录(TEMPLATES__DIRS默认为空,未添加时会报:django.template.exceptions.TemplateDoesNotExist: index.html)

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',
            ],
        },
    },
]

views.py(通过loader.get_template()返回)

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:views.py
# Data:2020/11/18 11:51
# Author:LGSP_Harold
import os
from datetime import datetime

from django.conf import settings
from django.http import HttpResponse
from django.template import loader
from django.urls import reverse


def index(request):
    url = reverse('article_detail', args=(2020, ))  # 不带命名空间的逆向解析URL
    url_auth = reverse('oauth:index')    # 带命名空间的URL逆向解析
    print(url_auth)
    return HttpResponse('ok:' + url)


def article(request, year):
    """ 获取url中的参数 """
    print('year: {0}'.format(year))
    # 获取GET参数中的月份
    month = request.GET.get('month', None)  # http://127.0.0.1:8082/article/2020/?month=12&day=31
    print('month: {0}'.format(month))
    month = request.GET.get('month', '11')  # http://127.0.0.1:8082/article/2020/?day=31
    print('month: {0}'.format(month))
    # request.POST.get()
    return HttpResponse('article:' + year)


# 定义视图
def now_time(request):
    """ 展示系统当前的时间 """
    now = datetime.now()
    html = """
    <html>
        <head>
            <style type="text/css">
                body{{color: red;}}
            </style>
        </head>
        <body>
            now: {0}
        </body>
    </html>
    """.format(now)

    return HttpResponse(html)


def now_use_file(request):
    # 从HTML文件读取内容,并响应

    html = ''
    now = datetime.now()
    # # 文件名称
    # file_name = os.path.join(settings.BASE_DIR, 'templates', 'index.html')
    # with open(file_name) as f:
    #     html = f.read()
    # html = html.replace('{{now}}', now.strftime('%Y-%m-%d'))
    # return HttpResponse(html)

    # 使用django提供的方法
    # temp = loader.get_template('index.html')
    # html = temp.render({
    #     'now': now.strftime('%Y-%m-%d')
    # })
    # return HttpResponse(html)

views.py(通过render()或render_to_response()返回)

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:views.py
# Data:2020/11/18 11:51
# Author:LGSP_Harold
import os
from datetime import datetime

from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render, render_to_response
from django.template import loader
from django.urls import reverse


def index(request):
    url = reverse('article_detail', args=(2020, ))  # 不带命名空间的逆向解析URL
    url_auth = reverse('oauth:index')    # 带命名空间的URL逆向解析
    print(url_auth)
    return HttpResponse('ok:' + url)


def article(request, year):
    """ 获取url中的参数 """
    print('year: {0}'.format(year))
    # 获取GET参数中的月份
    month = request.GET.get('month', None)  # http://127.0.0.1:8082/article/2020/?month=12&day=31
    print('month: {0}'.format(month))
    month = request.GET.get('month', '11')  # http://127.0.0.1:8082/article/2020/?day=31
    print('month: {0}'.format(month))
    # request.POST.get()
    return HttpResponse('article:' + year)


# 定义视图
def now_time(request):
    """ 展示系统当前的时间 """
    now = datetime.now()
    html = """
    <html>
        <head>
            <style type="text/css">
                body{{color: red;}}
            </style>
        </head>
        <body>
            now: {0}
        </body>
    </html>
    """.format(now)

    return HttpResponse(html)


def now_use_file(request):
    # 从HTML文件读取内容,并响应

    html = ''
    now = datetime.now()
    # # 文件名称
    # file_name = os.path.join(settings.BASE_DIR, 'templates', 'index.html')
    # with open(file_name) as f:
    #     html = f.read()
    # html = html.replace('{{now}}', now.strftime('%Y-%m-%d'))
    # return HttpResponse(html)

    # 使用django提供的方法
    # temp = loader.get_template('index.html')
    # html = temp.render({
    #     'now': now.strftime('%Y-%m-%d')
    # })
    # return HttpResponse(html)

    # 使用render函数
    # return render(request, 'index.html', {
    #     'now': now.strftime('%Y-%m-%d')
    # })

    # 使用render_to_response函数
    return render_to_response('index.html', {
        'now': now.strftime('%Y-%m-%d')
    })

重定向:

urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    # 重定向的实验
path('index1/', views.index_one, name='index_one'),
path('index2/', views.index_two, name='index_two'), re_path(r'^article/(?P<year>[0-9]{4})/$', views.article, name='article_detail'), path('oauth/', include(('oauth.urls', 'oauth'))), # 展示当前的时间 path('time/', views.now_time), # 页面从html加载 path('now/', views.now_use_file), ]

views.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:views.py
# Data:2020/11/18 11:51
# Author:LGSP_Harold
import os
from datetime import datetime

from django.conf import settings
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, render_to_response, redirect
from django.template import loader
from django.urls import reverse


def index(request):
    url = reverse('article_detail', args=(2020, ))  # 不带命名空间的逆向解析URL
    url_auth = reverse('oauth:index')    # 带命名空间的URL逆向解析
    print(url_auth)
    return HttpResponse('ok:' + url)


def index_one(request):
url = reverse('index_two')
""" 访问该视图时,重定向到 index two """
# return HttpResponseRedirect('/index2/')
# return HttpResponseRedirect(url)
# return redirect('/index2/')
# return redirect(url)
return redirect('index_two') # return HttpResponse('index one') def index_two(request): return HttpResponse('index two') def article(request, year): """ 获取url中的参数 """ print('year: {0}'.format(year)) # 获取GET参数中的月份 month = request.GET.get('month', None) # http://127.0.0.1:8082/article/2020/?month=12&day=31 print('month: {0}'.format(month)) month = request.GET.get('month', '11') # http://127.0.0.1:8082/article/2020/?day=31 print('month: {0}'.format(month)) # request.POST.get() return HttpResponse('article:' + year) # 定义视图 def now_time(request): """ 展示系统当前的时间 """ now = datetime.now() html = """ <html> <head> <style type="text/css"> body{{color: red;}} </style> </head> <body> now: {0} </body> </html> """.format(now) return HttpResponse(html) def now_use_file(request): # 从HTML文件读取内容,并响应 html = '' now = datetime.now() # # 文件名称 # file_name = os.path.join(settings.BASE_DIR, 'templates', 'index.html') # with open(file_name) as f: # html = f.read() # html = html.replace('{{now}}', now.strftime('%Y-%m-%d')) # return HttpResponse(html) # 使用django提供的方法 # temp = loader.get_template('index.html') # html = temp.render({ # 'now': now.strftime('%Y-%m-%d') # }) # return HttpResponse(html) # 使用render函数 # return render(request, 'index.html', { # 'now': now.strftime('%Y-%m-%d') # }) # 使用render_to_response函数 return render_to_response('index.html', { 'now': now.strftime('%Y-%m-%d') })

8、重写内置的错误处理视图(django2.0+传参和django1.x传参有区别)

settings.py切换到生产模式:DEBUG=False

开启生产模式后,需设置ALLOWED_HOSTS运行访问的链接:ALLOWED_HOSTS = ['*']

urls.py

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

from . import views

# 设置404等异常路由
handler500 = 'mysite.views.page_500'
handler404 = 'mysite.views.page_404' urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), # 重定向的实验 path('index1/', views.index_one, name='index_one'), path('index2/', views.index_two, name='index_two'), re_path(r'^article/(?P<year>[0-9]{4})/$', views.article, name='article_detail'), path('oauth/', include(('oauth.urls', 'oauth'))), # 展示当前的时间 path('time/', views.now_time), # 页面从html加载 path('now/', views.now_use_file), ]

views.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:views.py
# Data:2020/11/18 11:51
# Author:LGSP_Harold
import os
from datetime import datetime

from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, render_to_response, redirect
from django.template import loader
from django.urls import reverse
from django.views.defaults import bad_request


def index(request):
    url = reverse('article_detail', args=(2020, ))  # 不带命名空间的逆向解析URL
    url_auth = reverse('oauth:index')    # 带命名空间的URL逆向解析
    print(url_auth)
    return HttpResponse('ok:' + url)


def index_one(request):
    url = reverse('index_two')
    """ 访问该视图时,重定向到 index two """
    # return HttpResponseRedirect('/index2/')
    # return HttpResponseRedirect(url)
    # return redirect('/index2/')
    # return redirect(url)
    return redirect('index_two')

    # return HttpResponse('index one')


def index_two(request):
    return HttpResponse('index two')


def article(request, year):
    """ 获取url中的参数 """
    print('year: {0}'.format(year))
    # 获取GET参数中的月份
    month = request.GET.get('month', None)  # http://127.0.0.1:8082/article/2020/?month=12&day=31
    print('month: {0}'.format(month))
    month = request.GET.get('month', '11')  # http://127.0.0.1:8082/article/2020/?day=31
    print('month: {0}'.format(month))
    # request.POST.get()
    # # 触发一个异常会产生500
    # raise ValueError
    # # 触发一个异常会产生403
    # raise PermissionDenied
    return HttpResponse('article:' + year)


# 定义视图
def now_time(request):
    """ 展示系统当前的时间 """
    now = datetime.now()
    html = """
    <html>
        <head>
            <style type="text/css">
                body{{color: red;}}
            </style>
        </head>
        <body>
            now: {0}
        </body>
    </html>
    """.format(now)

    return HttpResponse(html)


def now_use_file(request):
    # 从HTML文件读取内容,并响应

    html = ''
    now = datetime.now()
    # # 文件名称
    # file_name = os.path.join(settings.BASE_DIR, 'templates', 'index.html')
    # with open(file_name) as f:
    #     html = f.read()
    # html = html.replace('{{now}}', now.strftime('%Y-%m-%d'))
    # return HttpResponse(html)

    # 使用django提供的方法
    # temp = loader.get_template('index.html')
    # html = temp.render({
    #     'now': now.strftime('%Y-%m-%d')
    # })
    # return HttpResponse(html)

    # 使用render函数
    # return render(request, 'index.html', {
    #     'now': now.strftime('%Y-%m-%d')
    # })

    # 使用render_to_response函数
    return render_to_response('index.html', {
        'now': now.strftime('%Y-%m-%d')
    })


def page_500(request):
    # 重写500响应页面
    return HttpResponse('服务器正忙')


# django2.0+需要添加一个exception参数,否则报"handler404() got an unexpected keyword argument 'exception'"
def page_404(request, exception):
    # 重写404响应页面
    return HttpResponse('资源已丢失!')

9、static.serve处理静态文件

settings.py

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'hax*=-xp1(3fb_k(scbc1ooe0^gid@-6ibcmn2142mgv@96q7z'

# SECURITY WARNING: don't run with debug turned on in production!
# 上生产环境时一定记得改为False
DEBUG = False

# 开启生产模式后,需设置ALLOWED_HOSTS运行访问的链接:ALLOWED_HOSTS = ['*']
ALLOWED_HOSTS = ['*']


# Application definition

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

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',
]

ROOT_URLCONF = 'mysite.urls'

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',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'

# 用户上传目录
MEDIA_URL = '/medias/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'medias')

urls.py

from django.conf import settings
from django.contrib import admin
from django.urls import path, re_path, include
from django.views.static import serve

from . import views

# 设置404等异常路由
handler500 = 'mysite.views.page_500'
handler404 = 'mysite.views.page_404'


urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    # 重定向的实验
    path('index1/', views.index_one, name='index_one'),
    path('index2/', views.index_two, name='index_two'),
    re_path(r'^article/(?P<year>[0-9]{4})/$', views.article, name='article_detail'),

    path('oauth/', include(('oauth.urls', 'oauth'))),

    # 展示当前的时间
    path('time/', views.now_time),
    # 页面从html加载
    path('now/', views.now_use_file),
]

# 添加自定义的静态资源目录访问
urlpatterns += [re_path(r'^medias/(?P<path>.*)$', serve, {
    'document_root': settings.MEDIA_ROOT
})]

10、请求响应对象

 _1.请求对象HttpRequest

请求方式method:POST/GET/PUT/DELETE....

请求头信息META:

REMOTE_ADDR——请求的IP地址

HTTP_USER_AGENT——用户请求终端信息

获取请求传递参数{

GET——GET请求参数

POST——POST请求参数

COOKIES——cookie信息

FILES——文件信息

 urls.py

    # 打印请求对象
    path('print/request/', views.print_request, name='print_request'),

views.py

def print_request(request):
    print(request)
    ip = request.META['REMOTE_ADDR']
    print(ip)
    # 用户的浏览器,终端信息
    user_agent = request.META['HTTP_USER_AGENT']
    print(user_agent)
    session = request.session
    print(session)
    print('______________________')
    print(dir(request))
    return HttpResponse()

 _2.响应对象:HttpResponse、HttpResponseRedirect、JsonResponse、FileResponse....

常见的Content-Type:HTML(text/html)、普通文本(text/plain)、XML文档(text/xml)、image(image/png、jpeg、gif....)、JSON(application/json)....

urls.py

    # 响应对象实验
    path('print/response/', views.print_response, name='print_response'),
    # 响应JSON对象
    path('print/json/', views.print_json, name='print_json'),

views.py

def print_json(request):
    # 响应JSON对象
    user_info = {
        'username': '张三',
        'age': 19,
    }
    # import json
    # user_info = json.dumps(user_info)
    # return HttpResponse(user_info, content_type='application/json')
    return JsonResponse(user_info)

打印响应对象

urls.py

    # 打印响应对象
    path('print/resp/attr/', views.print_resp_attr, name='print_resp_attr'),

views.py

def print_resp_attr(request):
    # 打印响应对象
    # 设置状态码
    # return HttpResponse('打印响应对象', status=201)

    # # 重新设置HTTP的状态码
    # resp = HttpResponse(status=404)
    # print(resp.status_code)
    # resp.status_code = 204
    # print(resp.status_code)
    # return resp

    # 写入响应内容
    resp = HttpResponse('打印响应对象')
    resp.write('2019')

    return resp

打印图片,Excel

urls.py

    # 打印图片
    path('print/images/', views.print_images, name='print_images'),
    # Excel
    path('print/excels/', views.print_excels, name='print_excels'),

views.py

def print_images(request):
    # 打印图片
    from django.http import FileResponse
    try:
        file_name = os.path.join(settings.BASE_DIR, 'medias/images/pic_160.png')
        f = open(file_name, 'rb')
    except Exception as e:
        print(e)
    return FileResponse(f, content_type='image/png')


def print_excels(request):
    # Excel
    from django.http import FileResponse
    try:
        file_name = os.path.join(settings.BASE_DIR, 'medias/files/课程链接.xls')
        f = open(file_name, 'rb')
    except Exception as e:
        print(e)
    return FileResponse(f, content_type='application/vnd.ms-excel')

11、使用class重写视图

步骤:继承视图(django.view.generic.TemplateView)>配置模板地址>配置URL

 urls.py

    # 使用类改写视图
    path('show/class/', views.ShowClassView.as_view(), name='show_class'),

views.py

from django.views.generic import TemplateView


class ShowClassView(TemplateView):
    # class 视图
    template_name = 'show_class.html'

templates/show_class.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    使用class展示视图
</body>
</html>

访问 http://127.0.0.1:8082/show/class/

将show_class.html移到oauth/templates/目录下

oauth/urls.py

    # 使用类改写视图
    path('show/class/', views.ShowClassView.as_view(), name='show_class'),

oauth/views.py

from django.views.generic import TemplateView


class ShowClassView(TemplateView):
    # class 视图
    template_name = 'show_class.html'

settings.py

INSTALLED_APPS = [
    'oauth.apps.OauthConfig',
]

访问 http://127.0.0.1:8082/oauth/show/class/

TemplateView原理解析:从项目主目录寻找模板文件 > 从app的模板目录寻找模板文件

 

12、Django内置通用视图

_1.django.views.generic.ListView:列表类数据的封装,如:商品列表,支持分页

_2.django.views.generic.DetailView:详情类数据的封装,如:商品详情

 

13、配置使用jinja2引擎

 settings.py

TEMPLATES = [
    {
        # 模板引擎配置
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 模板引擎按列表顺序搜索这些目录以查找模板源文件
        # 每种模板引擎后端都定义了一个惯用的名称作为应用内部存放模板的子目录名称
        # DTL——templates
        # Jinja2——jinja2
        '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',
            ],
        },
    },
    {
        # 模板引擎配置
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        # 模板引擎按列表顺序搜索这些目录以查找模板源文件
        # 每种模板引擎后端都定义了一个惯用的名称作为应用内部存放模板的子目录名称
        # DTL——templates
        # Jinja2——jinja2
        'DIRS': [os.path.join(BASE_DIR, 'jinja2'), ],
        # 决定模板引擎是否进入每个已安装的应用中查找模板
        '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',
            ],
        },
    },
]

urls.py

    # 模板引擎选择
    path('templ/show/', views.templ_show, name='templ_show'),

views.py

def templ_show(request):
    # 模板引擎选择
    return render(request, 'detail.html')

jinja2/detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>详细信息</title>
</head>
<body>
    使用jinja2模板
</body>
</html>

 

14、模板变量的使用

urls.py

    # 变量在模板中渲染——图片
    path('templ/image/', views.templ_image, name='templ_image'),

views.py

def templ_image(request):
    # 变量在模板中渲染——图片
    img_url = '/medias/images/pic_160.png'
    return render(request, 'pic.html', {
        'img_url': img_url
    })

templates/pic.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>显示图片</title>
</head>
<body>
<img src="{{ img_url }}" alt="pic">
</body>
</html>

_渲染对象:{{ object.attribute }}

views.py

def templ_image(request):
    # 变量在模板中渲染——图片
    img_url = '/medias/images/pic_160.png'
    user_info = {
        'username': 'Harold',
        'age': 95
    }
    list_city = [
        '北京', '上海', '南宁'
    ]
    list_prods = [
        {'name': '名称1', 'price': 100},
        {'name': '名称2', 'price': 100},
        {'name': '名称3', 'price': 100},
    ]
    bool = True
    return render(request, 'pic.html', {
        'img_url': img_url,
        'user_info': user_info,
        'list_city': list_city,
        'list_prods': list_prods,
        'bool': bool,
    })

pic.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>显示图片</title>
</head>
<body>
<ul>
    <li>{{ user_info.username }}</li>
    <li>{{ user_info.age }}</li>
</ul>
<select name="" id="">
    <option value="">{{ list_city.0 }}</option>
    <option value="">{{ list_city.1 }}</option>
    <option value="">{{ list_city.2 }}</option>
</select>
<img src="{{ img_url }}" alt="pic">
<h5>显示商品信息</h5>
<ul>
    <li>{{ list_prods.0.name }} : {{ list_prods.0.price }}</li>
    <li>{{ list_prods.1.name }} : {{ list_prods.1.price }}</li>
    <li>{{ list_prods.2.name }} : {{ list_prods.2.price }}</li>
</ul>
<p>
    {{ bool }}
</p>
</body>
</html>

 _模板标签:{% tag %}

 循环控制

{% for item in data_list %}
<li>内容</li>
{% endfor %}

条件控制

{% if condition_a %}
满足A条件
{% elif condition_b %}
满足B条件
{% else %}
都不满足
{% endif %}

添加注释

{# 注释内容 #}

for循环对象

{% for key, value in data.items %}
{{ key }}:{{ value }}
{% endfor %}

重复循环

{% cycle 'row1' 'row2' %}

url标签的使用

{% url 'url_name' params %}

当前时间显示

{% now "jS F Y H:i" %}

urls.py

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.contrib import admin
from django.urls import path, re_path, include
from django.views.static import serve

from . import views

# 设置404等异常路由
handler500 = 'mysite.views.page_500'
handler404 = 'mysite.views.page_404'


urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    # 重定向的实验
    path('index1/', views.index_one, name='index_one'),
    path('index2/', views.index_two, name='index_two'),
    re_path(r'^article/(?P<year>[0-9]{4})/$', views.article, name='article_detail'),

    path('oauth/', include(('oauth.urls', 'oauth'), namespace='oauth')),

    # 打印请求对象
    path('print/request/', views.print_request, name='print_request'),
    # 响应对象实验
    path('print/response/', views.print_response, name='print_response'),
    # 响应JSON对象
    path('print/json/', views.print_json, name='print_json'),
    # 打印响应对象
    path('print/resp/attr/', views.print_resp_attr, name='print_resp_attr'),
    # 打印图片
    path('print/images/', views.print_images, name='print_images'),
    # Excel
    path('print/excels/', views.print_excels, name='print_excels'),


    # 展示当前的时间
    path('time/', views.now_time),
    # 页面从html加载
    path('now/', views.now_use_file),

    # # 模板引擎选择
    # path('templ/show/', views.templ_show, name='templ_show'),

    # 变量在模板中渲染——图片
    path('templ/image/', views.templ_image, name='templ_image'),
    # 模板标签的使用
    path('templ/tag/', views.templ_tag, name='templ_tag'),
]

# 添加自定义的静态资源目录访问
urlpatterns += [re_path(r'^medias/(?P<path>.*)$', serve, {
    'document_root': settings.MEDIA_ROOT
})]

views.py

def templ_tag(request):
    # 模板标签的使用
    list_city = [
        '北京', '上海', '南宁'
    ]
    list_prods = [
        {'name': '名称1', 'price': 95},
        {'name': '名称2', 'price': 100},
        {'name': '名称3', 'price': 100},
        {'name': '名称4', 'price': 800},
        {'name': '名称5', 'price': 150},
    ]
    list_order = []
    user_info = {
        'username': 'Harold',
        'age': 95
    }
    return render(request, 'tag.html', {
        'list_city': list_city,
        'list_prods': list_prods,
        'list_order': list_order,
        'user_info': user_info,
    })

tag.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板标签的使用</title>
    <style type="text/css">
        .row1 {
            background-color: #F32;
            color: #EE7;
            font-weight: bold;
        }
        .row2 {
            background-color: #EE7;
            color: #F32;
            font-weight: bold;
        }
    </style>
</head>
<body>
{# 不解析到浏览器上 #}
<ul>
    {% for item in list_city %}
    <li>{{ item }}</li>
    {% endfor %}
</ul>

<ul>
    {# for循环list/tuple #}
    {% for item in list_prods reversed %}   {# 排序 reversed 倒序输出 #}
        {# if判断 #}
        {% if item.price > 95 %}
        {# {% cycle 'row1' 'row2' %} 重复循环 #}
     {# forloop.counter 编号从1开始;forloop.counter0 编号从0开始 #} <li class="{% cycle 'row1' 'row2' %}">{{ forloop.counter }}: {{ item.name }} => {{ item.price }}</li> {% endif %} {% endfor %} </ul> {# 列表为空输出 empty的使用 #} <ul> {% for item in list_order %} <li>{{ item }}</li> {% empty %} <li>订单没有</li> {% endfor %} </ul> {# dict #} {% for key,value in user_info.items %} <p>{{ key }}: {{ value }}</p> {% endfor %} {# url标签的使用,根据ulrs.py的name来解析地址,不管urls.py链接如何的更改 #} <a href="{% url 'print_resp_attr' %}" target="_blank">打印响应函数</a> <br /> {# 带参数的URL #} <a href="{% url 'article_detail' 2019 %}?month=12" target="_blank">2019年的文章</a> <br /> {# 带命名空间的url namespace:name #} <a href="{% url 'oauth:show_class' %}" target="_blank">带命名空间</a> {# 当前时间显示 #} <p>{% now "jS F Y H:i" %}</p> </body> </html>

 

15、模板过滤器

_过滤器语法:

{{ value|filter_name:params }}

使用过滤器将首字母大写

{{ value|capfirst}}

使用过滤器将日期格式化

{{ value|date:'Y-m-d H:m:s' }}

默认值显示

{{ value|default:'' }}
{{ value|default_if_none:'无' }}

数字的四舍五入

{{ value|floatformat:3 }}

富文本内容转义显示

{{ value|safe }}

字符串截取

{{ value|truncatechars:9 }}
{{ value|truncatechars_html:9 }}
{{ value|truncatewords:3 }}

urls.py

    # 模板过滤器的使用
    path('templ/filter/', views.templ_filter, name='templ_filter'),

views.py

def templ_filter(request):
    # 模板过滤器的使用
    list_word = [
        'view',
        'model',
        'django',
    ]
    now = datetime.now()
    user_info = {
        'username': '李四',
        'age': None,
        'sex': False,
        'school': '',
    }
    import math
    pi = math.pi
    html = '<h1>富文本转义</h1>'
    content1 = '<h1>我是主标题,大家好,大家好,大家好</h1>'
    content2 = '<h1>Hello, my name is Harold, I am testing.</h1>'
    return render(request, 'filter.html', {
        'list_word': list_word,
        'now': now,
        'user_info': user_info,
        'pi': pi,
        'html': html,
        'content1': content1,
        'content2': content2,
    })

filter.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>过滤器的使用</title>
</head>
<body>
<ol>
    {% for item in list_word %}
    <li>{{ item|capfirst }}</li>
    {% endfor %}
</ol>
{{ now|date:'Y-m-d H:m:s' }}
<br />
{# 默认值显示 #}
{{ user_info.username  }}
<br />
{{ user_info.age|default_if_none:'未知' }}
<br />
{{ user_info.sex|default:'不详' }}
<br />
{{ user_info.school|default:'未知' }}
<br />
{# 数字的四舍五入 #}
{{ pi|floatformat:4 }}
<br />
{# 富文本内容转义显示 #}
{{ html|safe }}
{# 字符串截取 #}
{{ content1|truncatechars:10 }}
<br />
{{ content1|truncatechars:10|safe }}
<hr />
{{ content1|truncatechars_html:6|safe }}
<br />
{{ content2|truncatewords:5|safe }}
<br />
</body>
</html>

 

自定义过滤器:在模块目录下新建templatetags包 > 实现并注册过滤器 > 在模板中使用过滤器 > 重启服务器 > 看效果

oauth/templatetags/oauth_extras.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:oauth_extras.py
# Data:2020/4/22 21:11
# Author:LGSP_Harold
from django import template


register = template.Library()


def warning(value):
    # 将第一个字符变红
    if value:
        return '<span class="text-red">' + value[0] + '</span>' + value[1:]
    return value

# 注册过滤器
register.filter('warning', warning)

oauth/urls.py

    # 使用自定义过滤器
    path('templ/filter/mine/', views.templ_filter_mine, name='templ_filter_mine'),

oauth/views.py

def templ_filter_mine(request):
    # 使用自定义的过滤器
    list_city = [
        '北京', '上海', '南宁'
    ]
    return render(request, 'filter_mine.html', {
        'list_city': list_city,
    })

templates/filter_mine.html

{# 加载过滤器 #}
{% load oauth_extras %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用自定义的过滤器</title>
    <style type="text/css">
        .text-red {
            color: red;
        }
    </style>
</head>
<body>
{% for city in list_city %}
{# 使用过滤器 #}
<p>{{ forloop.counter }}.{{ city|warning|safe }}</p>
{% endfor  %}
</body>
</html>

 ——自定义金额格式过滤器

settings.py

INSTALLED_APPS = [
    'course.apps.CourseConfig',
]

urls.py

urlpatterns = [
    path('course/', include(('course.urls', 'course'), namespace='course')),
]

course/urls.py

from django.urls import path

from course import views

urlpatterns = [
    path('cart/', views.cart, name='cart')
]

course/views.py

from django.shortcuts import render


# Create your views here.
def cart(request):
    money = 1111110000.599995
    return render(request, 'cart.html', {
        'money': money,
    })

course/templatetags/money_extras.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:money_extras.py
# Data:2020/4/22 21:41
# Author:LGSP_Harold
import locale
from decimal import Decimal

from django import template


def money_format(value, place=2):
    # 用逗号分割数据
    try:
        place = int(place)
    except:
        place = 2

    try:
        value = Decimal(value)
        locale.setlocale(locale.LC_ALL, '')
        return locale.format("%.*f", (place, value), 1)
    except Exception as e:
        return value


register = template.Library()
register.filter('money_format', money_format)

course/template/cart.html

{% load money_extras %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义金额过滤器</title>
</head>
<body>
{{ money|money_format:2 }}
</body>
</html>

 

16、模板的抽象和继承

_1、将可变的部分圈出来(base.html)

{% block sidebar %}
    <!-- 菜单栏的内容 -->
{% endblock %}

_2、继承父模板

{% extends "base.html" %}

_3、填充新的内容(index.html)

{% extends "base.html" %}
{% block sidebar %}
    <!-- 新的菜单栏的内容 -->
{% endblock %}

_4、复用父模板的内容(可选)

{% extends "base.html" %}
{% block sidebar %}
    {{ supper }}
    <!-- 新的菜单栏的内容 -->
{% endblock %}

 _4_、在模板中添加公共部分

_4_1、将可变部分拆出来(footer.html)

<footer>
这是页脚公共的部分
</footer>

_4_2、将拆出来的部分包进去(index.html)

{% extends "base.html" %}
{% block content %}
<!- 页面主要内容区域 -->
 {# 公用的footer #}
{% include "footer.html" %}
{% endblock %}

 

17、ORM

 _1、ORM配置

default——默认的数据库,可配置多个数据,使用名称来区分

ENGINE——数据库引擎【'django.db.backends.postgresql','django.db.backends.mysql','django.db.backends.sqlite3','django.db.backends.oracle'】

NAME——数据库名称

USER——数据库登录用户名

PASSWORD——数据库登录密码

HOST——数据库访问地址

PORT——数据库访问端口

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydatabase',
        'USER': 'database_user',
        'PASSWORD': 'database_pwd',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}

P.S.5:sqlte3的配置选项:只需指定数据库引擎和数据库文件名查即可

DATABASE = {
    'default': {
        'ENGINE': 'django.db.backends.sqlte3',
        'NAME': 'mydatabase',
    }
}

_2、常见类型

_2_1、类型的选项

_、null、blank——是否为Null、空值

_、db_column——数据库表中对应的字段名称

_、default——不填写该字段值时的默认值

_、primary_key、unique——主键、唯一索引

_、verbose_name——供编程查看的字段名称(便于阅读)

_、help_text——帮助文字

_、choices——可供选择的选项,如:性别的选项(男,女)

_、get_FOO_display()——展示choices对应的值

_2_2、Django常见类型

_、CharField、TextField——字符串、文本

_、DateField、DatetimeField——日期时间

_、FileField、ImageField——文件、图片

_、IntegerField、SmallIntegerField——整数

_、FloatField、DecimalField——小数

——CharField:max_length最大长度、EmailField邮箱输入、URLField网址输入、TextField长文本输入、FildPathField文件路径输入、ImageField图片输入

——DatimeField:auto_now更新时间为记录更改时的时间、auto_now_add记录创建的时间

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'students',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}

oauth/models.py

from django.db import models

# Create your models here.


class Student(models.Model):
    """ 学生表 """
    # name = models.CharField(verbose_name='姓名', max_length=64)
    name = models.CharField('姓名', max_length=64)
    sex = models.CharField('性别', max_length=1, choices=(
        ('1', ''),
        ('0', ''),
    ), default='1')
    age = models.PositiveIntegerField('年龄', default=0)
    username = models.CharField('登录名', max_length=64, unique=True)
    password = models.CharField('密码', max_length=256)
    create_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('最后修改时间', auto_now=True)

_3、模型同步步骤

_、开启数据库服务

_、创建好数据库

_、将模块添加进settings.py的INSTALLED_APPS里面

INSTALLED_APPS = [
    'oauth.apps.OauthConfig',
]

_、终端检查模型

python manage.py check

/由于django2.2.*版本兼容问题,执行上述命令会报错

Traceback (most recent call last):
  File "D:\Python36\lib\site-packages\django\db\backends\mysql\base.py", line 15, in <module>
    import MySQLdb as Database
ModuleNotFoundError: No module named 'MySQLdb'

....

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?

/然后安装mysqlclient

pip install mysqlclient

/又报错

Using legacy 'setup.py install' for mysqlclient, since package 'wheel' is not installed.
Installing collected packages: mysqlclient
    Running setup.py install for mysqlclient ... error
    ERROR: Command errored out with exit status 1:

....

building 'MySQLdb._mysql' extension
    error: Microsoft Visual C++ 14.0 is required. Get it with "Build Tools for Visual Studio": https://visualstudio.microsoft.com/downloads/

/安装Microsoft Visual C++ Build Tools,再安装mysqlclient,还是报错

Using legacy 'setup.py install' for mysqlclient, since package 'wheel' is not installed.
Installing collected packages: mysqlclient
    Running setup.py install for mysqlclient ... error
    ERROR: Command errored out with exit status 1:
     
....
Complete output (29 lines): running install running build running build_py creating build creating build\lib.win32-3.6 creating build\lib.win32-3.6\MySQLdb copying MySQLdb\__init__.py -> build\lib.win32-3.6\MySQLdb copying MySQLdb\_exceptions.py -> build\lib.win32-3.6\MySQLdb copying MySQLdb\connections.py -> build\lib.win32-3.6\MySQLdb copying MySQLdb\converters.py -> build\lib.win32-3.6\MySQLdb copying MySQLdb\cursors.py -> build\lib.win32-3.6\MySQLdb copying MySQLdb\release.py -> build\lib.win32-3.6\MySQLdb copying MySQLdb\times.py -> build\lib.win32-3.6\MySQLdb creating build\lib.win32-3.6\MySQLdb\constants copying MySQLdb\constants\__init__.py -> build\lib.win32-3.6\MySQLdb\constants copying MySQLdb\constants\CLIENT.py -> build\lib.win32-3.6\MySQLdb\constants copying MySQLdb\constants\CR.py -> build\lib.win32-3.6\MySQLdb\constants copying MySQLdb\constants\ER.py -> build\lib.win32-3.6\MySQLdb\constants copying MySQLdb\constants\FIELD_TYPE.py -> build\lib.win32-3.6\MySQLdb\constants copying MySQLdb\constants\FLAG.py -> build\lib.win32-3.6\MySQLdb\constants running build_ext building 'MySQLdb._mysql' extension creating build\temp.win32-3.6 creating build\temp.win32-3.6\Release creating build\temp.win32-3.6\Release\MySQLdb
....
_mysql.c MySQLdb/_mysql.c(29): fatal error C1083: Cannot open include file: 'mysql.h': No such file or directory error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\cl.exe' failed with exit status 2 ---------------------------------------- ERROR: Command errored out with exit status 1:

....

/放弃挣扎,下载对应的mysqlclient安装包:(地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysql-python)

pip install mysqlclient-1.4.6-cp36-cp36m-win32.whl

/再运行

python manage.py check

/就正常了!

_、使用makemigrations生成同步原语(查询命令:python manage.py)

python manage.py makemigrations

 _、使用migrate执行同步

python manage.py migrate

_、如果新增列

from django.db import models

# Create your models here.


class Student(models.Model):
    """ 学生表 """
    # name = models.CharField(verbose_name='姓名', max_length=64)
    name = models.CharField('姓名', max_length=64)
    sex = models.CharField('性别', max_length=1, choices=(
        ('1', ''),
        ('0', ''),
    ), default='1')
    id_no = models.CharField('学号', max_length=10)
    age = models.PositiveIntegerField('年龄', default=0)
    username = models.CharField('登录名', max_length=64, unique=True)
    password = models.CharField('密码', max_length=256)
    create_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('最后修改时间', auto_now=True)

在生成同步原语

python manage.py makemigrations

会出现选项(选1,添加一个默认值空值'',)

You are trying to add a non-nullable field 'id_no' to student without a default; we can't do that (the database needs something to popul
ate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option:
1) 立即提供一个一次性的默认值(将对所有现有行设置此列的空值)

2) 退出,让我在models.py添加一个默认值

再使用migrate执行同步

python manage.py migrate

_4、元数据的描述

_4_1、使用Meta类来表示

_4_2、对模型的补充说明

db_table——模型映射的数据库表的名称
ordering——指定数据表的默认排序规则
verbose_name——供编程查看的字段名称(便于阅读)
abstract——抽象类(抽象类不会生成数据库表)
proxy——代理模型(对父模型的功能进行扩充)
class Meta:
    verbose_name = '用户基础信息'
    verbose_name_plural = '用户基础信息'
    db_table = 'oauth_user'
from django.db import models

# Create your models here.


class Student(models.Model):
    """ 学生表 """
    # name = models.CharField(verbose_name='姓名', max_length=64)
    name = models.CharField('姓名', max_length=64)
    sex = models.CharField('性别', max_length=1, choices=(
        ('1', ''),
        ('0', ''),
    ), default='1')
    id_no = models.CharField('学号', max_length=10)
    age = models.PositiveIntegerField('年龄', default=0)
    username = models.CharField('登录名', max_length=64, unique=True)
    password = models.CharField('密码', max_length=256)
    create_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('最后修改时间', auto_now=True)

    # 内部类
    class Meta:
        db_table = 'students'

_、抽象类

oauth/models.py

from django.db import models

# Create your models here.


# 抽象类 class CommonUtils(models.Model): create_at = models.DateTimeField('创建时间', auto_now_add=True) updated_at = models.DateTimeField('最后修改时间', auto_now=True) class Meta: abstract = True class Student(CommonUtils): """ 学生表 """ # name = models.CharField(verbose_name='姓名', max_length=64) name = models.CharField('姓名', max_length=64) sex = models.CharField('性别', max_length=1, choices=( ('1', ''), ('0', ''), ), default='1') id_no = models.CharField('学号', max_length=10) age = models.PositiveIntegerField('年龄', default=0) username = models.CharField('登录名', max_length=64, unique=True) password = models.CharField('密码', max_length=256) # 内部类 class Meta: db_table = 'students' ordering = ['-updated_at']
class Course(CommonUtils): """ 课程 """ name = models.CharField('课程名称', max_length=64)

_、代理模型

oauth/models.py

from django.db import models

# Create your models here.


class CommonUtils(models.Model):
    create_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('最后修改时间', auto_now=True)

    class Meta:
        abstract = True


class Student(CommonUtils):
    """ 学生表 """
    # name = models.CharField(verbose_name='姓名', max_length=64)
    name = models.CharField('姓名', max_length=64)
    sex = models.CharField('性别', max_length=1, choices=(
        ('1', ''),
        ('0', ''),
    ), default='1')
    id_no = models.CharField('学号', max_length=10)
    age = models.PositiveIntegerField('年龄', default=0)
    username = models.CharField('登录名', max_length=64, unique=True)
    password = models.CharField('密码', max_length=256)

    # 内部类
    class Meta:
        db_table = 'students'
        ordering = ['-updated_at']


class Course(CommonUtils):
    """ 课程 """
    name = models.CharField('课程名称', max_length=64)


# 代理模型
class ProxyStudent(Student):
    class Meta:
        proxy = True

    def get_name(self):
        """ 获取学生的姓 """
        return self.name[0]

_5、外键关联类型的对应关系

_一对一:OneToOneField(class_name, on_delete=models.CASCADE)

_一对多:ForeignKey

_多对多:ManyToManyField(class_name)

P.S.6:django2.0+,OneToOneField和ForeignKey里面有两个参数必填,第一个参数传关联的表名称,第二个参数on_delete=models.CASCADE(对象删除后,包含OneToOneField和ForeignKey的字段也会被删除)

oauth/models.py

from django.db import models

# Create your models here.


class CommonUtils(models.Model):
    create_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('最后修改时间', auto_now=True)

    class Meta:
        abstract = True


class Course(CommonUtils):
    """ 课程 """
    name = models.CharField('课程名称', max_length=64)


class Student(CommonUtils):
    """ 学生表 """
    # name = models.CharField(verbose_name='姓名', max_length=64)
    name = models.CharField('姓名', max_length=64)
    sex = models.CharField('性别', max_length=1, choices=(
        ('1', ''),
        ('0', ''),
    ), default='1')
    id_no = models.CharField('学号', max_length=10)
    age = models.PositiveIntegerField('年龄', default=0)
    username = models.CharField('登录名', max_length=64, unique=True)
    password = models.CharField('密码', max_length=256)

    # 多对多关系
    courses = models.ManyToManyField(Course)

    # 内部类
    class Meta:
        db_table = 'students'
        ordering = ['-updated_at']


class UserDetail(models.Model):
    """ 用户详细信息表 1对多的关系 """
    student = models.OneToOneField(Student, on_delete=models.CASCADE)
    sign = models.CharField('座右铭', max_length=256)
    # 补充其他用户详细信息



class UserAddress(CommonUtils):
    """ 用户地址 1对多的关系 """
    user = models.ForeignKey(Student, on_delete=models.CASCADE, verbose_name='学生')
    phone = models.CharField('收件人电话', max_length=11)
    address = models.CharField('收件人地址', max_length=64)
    zip_code = models.CharField('邮编', max_length=10, null=True, blank=True)
    is_valid = models.BooleanField('是否有效', default=True)


# 代理模型
class ProxyStudent(Student):
    class Meta:
        proxy = True

    def get_name(self):
        """ 获取学生的姓 """
        return self.name[0]

_6、复合类型(收藏商品,收藏店铺)

 _、模型类型:ContentType

 _、关联复合模型:ForeignKey(ContentType)

_、关联模型:GenericForeignKey

_、反向关联:GenericRelation

mall/models.py

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models

# Create your models here.
from oauth.models import Student


class Product(models.Model):
    """ 商品表 """
    name = models.CharField('商品名称', max_length=64)

    collection = GenericRelation('Collection')


class Store(models.Model):
    """ 店铺表 """
    name = models.CharField('店铺名称', max_length=64)


class Collection(models.Model):
    """ 收藏表 """
    user = models.ForeignKey(Student, on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.IntegerField('关联的ID')
    content_object = GenericForeignKey('content_type', 'object_id')

    created_at = models.DateTimeField('收藏时间', auto_now_add=True)

# 树形菜单
# parent = models.ForeignKey('self', )

 ~定义微博模型(DEMO)

from django.db import models


# Create your models here.
class WeiboUser(models.Model):
    """ 微博用户 """
    username = models.CharField('用户名', max_length=64)
    password = models.CharField('密码', max_length=128)
    nickname = models.CharField('暱称', max_length=32)

    class Meta:
        db_table = 'weibo_user'


class Weibo(models.Model):
    """ 微博 """
    content = models.CharField('微博内容', max_length=500)
    user = models.ForeignKey(WeiboUser, on_delete=models.CASCADE, verbose_name='用户')
    created_at = models.DateTimeField('发布时间', auto_now_add=True)
    source = models.CharField('发布来源', null=True, blank=True, max_length=50)

    class Meta:
        db_table = 'weibo'


class WeiboImage(models.Model):
    """ 微博图片 """
    weibo = models.ForeignKey(Weibo, on_delete=models.CASCADE)
    image = models.ImageField('微博图片', upload_to='weibo')

    class Meta:
        db_table = 'weibo_images'


class Comment(models.Model):
    """ 微博评论 """
    content = models.CharField('评论', max_length=250)
    created_at = models.DateTimeField('评论时间', auto_now_add=True)
    user = models.ForeignKey(WeiboUser, on_delete=models.CASCADE, verbose_name='评论的用户')
    weibo = models.ForeignKey(Weibo, on_delete=models.CASCADE, verbose_name='评论的微博')

    class Meta:
        db_table = 'weibo_comments'


class Friend(models.Model):
    """ 好友关系 """
    user_from = models.ForeignKey(WeiboUser, verbose_name='关注人', on_delete=models.CASCADE, related_name='user_from')
    user_to = models.ForeignKey(WeiboUser, verbose_name='被关注人', on_delete=models.CASCADE, related_name='user_to')
    created_at = models.DateTimeField('关注时间', auto_now_add=True)

    class Meta:
        db_table = 'weibo_friends'


# user = WeiboUser()
# # 我关注的用户
# user.user_from.xx
# # 关注我的用户
# user.user_to.xx

_7、数据库的CRUD(Create\Read\Update\Delete)增查改删

_新增:

在终端需先执行 :

python manage.py shell

使用模型的save()新增数据

from weibo.models import WeiboUser as User


user_obj = User(username='Harold', password='111111', nickname='Harold')
user_obj.save()

user_obj

user_obj.id
user_obj.pk

使用模型的create()新增数据

from weibo.models import WeiboUser as User


user = User.objects.create(username='Jim', password='111', nickname='Jim')

user

user.pk

 _查询:

在终端需先执行 :

python manage.py shell
from weibo.models import WeiboUser as User


# 使用模型的get()查询单条数据(查询的数据唯一且存在)
try:
    user_obj = User.objects.get(pk=15)
except User.DoesNotExist as e:
    print('error');

user_obj = User.objects.get(pk=1)
user_obj.pk
user_obj.id
user_obj.username
user_obj.password
user_obj.nickname


# 使用模型的all()查询所有的数据
list_all = User.objects.all()
list_all
for user in list_all:
    print(user.username)

_修改:

在终端需先执行 :

python manage.py shell
from weibo.models import WeiboUser as User


user_obj = User.object.get(pk=1)
user_obj.nickname

# 单条数据修改
user_obj.nickname = 'Leslie'
user_obj.save()
user_obj.nickname



# 批量修改
user_list = User.object.all()
user_list.update(password='123456')

 _删除:

在终端需先执行 :

python manage.py shell
from weibo.models import WeiboUser as User


# 删除单条数据
user_obj = User.objects.get(pk=1)
user_obj.username
user_obj.delete()

# 删除多条数据
user_list = User.objects.all().delete()
user_list

P.S.7:什么是逻辑删除?什么是物理删除?有什么区别?

——物理删除:将数据从数据库干掉,删除不占磁盘空间,删除找不回来

——逻辑删除:将数据标记删除,删除还占据磁盘空间,删除后还可以恢复,删除后通过查询条件不展示给用户

weibo/models.py

from django.db import models


# Create your models here.
class WeiboUser(models.Model):
    """ 微博用户 """
    username = models.CharField('用户名', max_length=64)
    password = models.CharField('密码', max_length=128)
    nickname = models.CharField('暱称', max_length=32)
    # 逻辑删除
    USER_STATUS = (
        (2, '限制用户'),
        (1, '正常'),
        (0, '删除'),
    )
    status = models.SmallIntegerField('用户状态', choices=USER_STATUS, default=1)

    class Meta:
        db_table = 'weibo_user'

在终端需先执行 :

python manage.py shell
from weibo.models import WeiboUser as User


user = User.objects.get(pk=2)
user.status = '0'
user.save()

_8、QuerySet

从数据库取出的对象集合;可以含有零个、一个或多个过滤器;从模型的Manager那里取得QuerySet;QuerySet的筛选结果本身还是QuerySet;QuerySet是惰性的。

——常用方法:

_、get()返回单条记录

_、create()新增一条记录

_、get_or_create()有则返回,没有则创建记录

_、bulk_create()新增多条数据库记录

_、first()查询第一条记录

_、last()查询最后一条记录

_、count()返回记录的行数之和

_、exists()结果集是否存在(是否存在1条以上的记录)

_、update()修改记录

_、delete()物理删除记录

在终端需先执行 :

python manage.py shell
from weibo.models import WeiboUser as User


# get_or_create()有则返回,没有则创建记录
user_obj = User.objects.get_or_create(username='Leslie', password='111', nickname='Leslie', status=1)
user_obj
user_obj[0].username

user_obj = User.objects.get_or_create(username='Jim', password='111', nickname='Jim', status=)
user_obj
user_obj[0].username


# bulk_create()新增多条数据库记录
user1_obj = User(username='Harold', password='111', nickname='Harold')
user2_obj = User(username='Lucifer', password='111', nickname='Lucifer')
User.objects.bulk_create([user1_obj, user2_obj])
user_list = []
for i in range(100):
  user_list.append(User(username='user{0}'.format(i), password='1', nickname='用户{0}'.format(i)))
len(user_list)
User.objects.bulk_create(user_list) # first()查询第一条记录,last()查询最后一条记录 User.objects.first() User.objects.last()


# count()查询记录的行数之和
User.objects.all().count()

# 结果集是否存在
User.objects.filter(username='Jim').exists()
 

——QuerySet链式查询方法

_、filter()筛选出满足条件的多条记录

_、exclude()排除满足条件的多条记录

_、order_by()对查询的记录排序

_、all()查询所有记录

_、none()创建一个空的结果集

_、using()使用指定的数据库查询(多数据库支持,需到settings.py配置多个数据库,未指定默认是default)

from weibo.models import WeiboUser as User


User.objects.all().count()
# 排除满足条件的多条记录
User.objects.all().exclude(username='Jim').count()


# 排序
User.objcts.all().order_by('-id')
# 排序后取第几条数据
User.objcts.all().order_by('-id')[0]
User.objcts.all().order_by('-id')[2]


# 使用指定的数据库查询(需到settings.py配置多个数据库)
User.objects.all().order_by('-id').using('settings_database_name')

 ——分页查询

_1、分片分页处理

_、返回前n个对象(LIMIT 5)

User.objects.all()[:5]

_、返回第6到第10个对象(OFFSET 5 LIMIT 5)【[从第几条开始:到第几条结束],后面参数不是往后取多少条】

User.objects.all()[5:10]

 _2、使用django.core.paginator进行分页处理

_、Paginator分页器

_、Page某一页对象

_、异常(InvalidPage无效的页码;PageNotAnInteger页码必须是正整数;EmptyPage空页(没有数据))

_2_1、步骤一:取得分页器Paginator(objects, page_size)

_、objects要进行分页的数据

_、page_size每页的数据多少

p = Paginator(objects, page_size)

_2_1_1、分页器属性

_、count数据记录的总条数

_、总页数(总记录条数/每页大小)

_、页码范围

 _2_2、步骤二:取得页面实例

page = p.page(page_num)

_、page_num当前页的页面,如第几页

_2_2_1、页面实例的属性

 _、number当前页的页码

_、object_list当前页的数据列表

_、paginator分页器对象的引用

_2_2_2、页面实例的常用方法

_、has_next()判断是否还有下一页

_、has_previous()判断是否还有上一页

_、has_other_pages()判断是否还有其他页(上/下一页)

_、next_page_number()下一页的页码,如果没有,触发InvalidPage异常

_、previous_page_number()上一页的页码,如果没有,触发InvalidPage异常

p = paginator(objects, 2)
page = p.page(3)

 urls.py

urlpatterns = [
    # 微博模块
    path('weibo/', include(('weibo.urls', 'weibo'), namespace='weibo')),
]

weibo/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/26 18:15
# Author:LGSP_Harold
from django.urls import path, re_path
from weibo import views


urlpatterns = [
    # 对用户数据进行分页
    re_path(r'^user/(?P<page>\d+)/$', views.page_user, name='page_user'),
]

weibo/views.py

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from weibo.models import WeiboUser as User


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10 # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')

_9、模型的打印

class WeiboUser(models.Model):
    """ 微博用户 """
    ...
    def __str__(self):
        return 'User:{0}, Nickname{1}'.format(self.username, self.nickname)

weibo/models.py

from django.db import models


# Create your models here.
class WeiboUser(models.Model):
    """ 微博用户 """
    username = models.CharField('用户名', max_length=64, unique=True)
    password = models.CharField('密码', max_length=128)
    nickname = models.CharField('暱称', max_length=32)
    # 逻辑删除
    USER_STATUS = (
        (2, '限制用户'),
        (1, '正常'),
        (0, '删除'),
    )
    status = models.SmallIntegerField('用户状态', choices=USER_STATUS, default=1)

    class Meta:
        db_table = 'weibo_user'

    def __str__(self):
        return 'User:{0}, Nickname:{1}, pk:{2}'.format(self.username, self.nickname, self.id)

 _10、查询条件

_1、查询语法:字段名称__查询条件="查询内容"

—— 查询条件:

_、exact等于**值(默认的形式):id__exact = 6或者id = 6

_、iexact像**值:name__iexact = 'Harold'

_、contains包含**值,区分大小写:name__contains = 'Jim'

_、icontains包含**值,不区分大小写:name__icontains = 'jim'

_、startswith、istartwith以**开始

_、endswith、iendswith以**结束

_、in在**选项(列表)之内

_、gt大于某个值

_、gte大于或等于某个值

_、lt小于某个值

_、lte小于或等于某个值

_、isnull是否是空值

weibo/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/26 18:15
# Author:LGSP_Harold
from django.urls import path, re_path
from weibo import views


urlpatterns = [
    # 对用户数据进行分页
    re_path(r'^user/(?P<page>\d+)/$', views.page_user, name='page_user'),
    # ORM查询练习
    path('search/', views.page_search, name='page_search'),
]

weibo/views.py

from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from weibo.models import WeiboUser as User, Comment


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10 # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)


    return HttpResponse('OK')

在终端需先执行 :

python manage.py shell
from weibo.models import Comment, WeiboUser, Weibo

user1 = WeiboUser.objects.get(pk=1)
user2 = WeiboUser.objects.get(pk=2)

weibo = Weibo.objects.create(user=user1, content='微博内容1')

Comment.objects.create(user=user1, content='user1评论内容1', weibo=weibo)
Comment.objects.create(user=user1, content='user1评论内容2', weibo=weibo)
Comment.objects.create(user=user2, content='user2评论内容1', weibo=weibo)

weibo/views.py

from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from weibo.models import WeiboUser as User, Comment


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10 # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)


# 查询user1的微博评论 # user = User.objects.get(pk=2) # 外键是对象 # comment_list = Comment.objects.filter(user=user) # for item in comment_list: # print(item) # print(item.user.username) # print(item.content) # 根据外键的某个属性(字段)进行查询 # comment_list = Comment.objects.filter(user__username='user1') # for item in comment_list: # print(item) # print(item.user.username) # print(item.content) return HttpResponse('OK')

 

18、事务与回滚

事务:多个数据库逻辑操作的集合

回滚:多个逻辑中某个操作出错,回到初始状态

事务的原子性要求事务要么全部完成,要么全部不完成,不可能停滞在某个中间状态

_自动提交

from django.db import transaction


@transaction.atomic
def viewfunc(request):
    # 事务内的代码
    do_stuff()

atomic自动提交过程解析:

_进入到最外层的atomic代码块时会打开一个事务

_进入到内曾atomic代码块时会创建一个标记

_退出内部块时会释放或回滚至标记

_退出外部块时提交或回滚事务。

weibo/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/26 18:15
# Author:LGSP_Harold
from django.urls import path, re_path
from weibo import views


urlpatterns = [
    # 对用户数据进行分页
    re_path(r'^user/(?P<page>\d+)/$', views.page_user, name='page_user'),
    # ORM查询练习
    path('search/', views.page_search, name='page_search'),
    # 事务练习
    path('trans/', views.page_trans, name='page_trans'),
path('trans_with/', views.page_trans_with, name='page_trans_with'), ]

weibo/views.py

from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db import transaction
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10 # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')

@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('OK')
def page_trans_with(request):
""" 事务练习 """
# 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
with transaction.atomic():
user2 = User.objects.get(pk=2)
# 发布微博
weibo = Weibo.objects.create(user=user2, content='事务练习with')
# 发布评论
comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
print('weibo:', weibo.id, ';comment:', comment.id)
return HttpResponse('trans_with')

 _手动提交和回滚

from django.db import transaction


try:
    a.save()
    b.save()
    transaction.commit()    # 提交事务
except:
    transaction.rollback()    # 回滚

 weibo/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/26 18:15
# Author:LGSP_Harold
from django.urls import path, re_path
from weibo import views


urlpatterns = [
    # 对用户数据进行分页
    re_path(r'^user/(?P<page>\d+)/$', views.page_user, name='page_user'),
    # ORM查询练习
    path('search/', views.page_search, name='page_search'),
    # 事务练习
    path('trans/', views.page_trans, name='page_trans'),
    path('trans_with/', views.page_trans_with, name='page_trans_with'),
    # 手动控制事务
    path('trans_hand/', views.page_trans_hand, name='page_trans_hand')

]

weibo/views.py

from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db import transaction
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10 # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')


@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('page_trans')


def page_trans_with(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    with transaction.atomic():
        user2 = User.objects.get(pk=2)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='事务练习with')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('trans_with')


def page_trans_hand(request):
    """ 手动控制事务 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    try:
        # 放弃自动提交
        transaction.set_autocommit(False)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='手动控制事务')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论手动控制事务', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
        # 手动提交事务
        transaction.commit()
    except:
        # 不使用事务则手动删除数据
        # weibo.delete()
        # 手动控制事务,实现回滚
        transaction.rollback()

    return HttpResponse('trans_hand')

 

19、内置聚合函数:django.db.models

_求和:Sum

_求平均数:Avg

_计数:Count

_最大/最小值:Max/Min

聚合的两种方式

19_1、使用aggregate从整个查询结果集生成统计数据

from django.db.models import Avg


Book.objects.all().aggregate(Avg('price'))

创建grade模块

python manage.py startapp grade

settings.py

INSTALLED_APPS = [
    'grade.apps.GradeConfig',
]

urls.py

urlpatterns = [
    path('grade/', include(('grade.urls', 'grade'), namespace='grade')),
]

grade/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/30 21:07
# Author:LGSP_Harold
from django.urls import path

from grade import views

urlpatterns = [
    # 聚合及统计
    path('stas/', views.page_stas, name='page_stas'),
]

grade/models.py

from django.db import models

# Create your models here.


class Student(models.Model):
    student_name = models.CharField('学生的姓名', max_length=32)

    class Meta:
        db_table = 'grade_students'

    def __str__(self):
        return 'student_name:{0}'.format(self.student_name)


class Grade(models.Model):
    """ 学生成绩 """
    student = models.ForeignKey(Student, on_delete=models.CASCADE, null=True, related_name='stu_grade')
    subject_name = models.CharField('科目', max_length=32)
    score = models.FloatField('分数', default=0)
    year = models.SmallIntegerField('年份')

    class Meta:
        db_table = 'grade'

    def __str__(self):
        return 'subject_name:{0},score:{1},year:{2}'.format(self.subject_name, self.score, self.year)

grade/views.py

from django.db.models import Sum, Max
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
from grade.models import Grade, Student


def page_stas(request):
    """ 聚合及统计 """
    # 计算张三的期末成绩总和
    # student = Student.objects.get(student_name='张三')
    # print(type(student))  # 此处得到的是:<class 'grade.models.Student'>
    # print(student)
    # grade_list = Grade.objects.filter(student=student).aggregate(Sum('score'))

    # 此处需注意,外键查询,需带上字段:
    # grade_list = Grade.objects.filter(student_name='张三').aggregate(Sum('score'))
    # grade_list = Grade.objects.filter(student='张三').aggregate(Sum('score'))
    # 按上面写法,会报类似如下错误
    # django.core.exceptions.FieldError: Cannot resolve keyword 'student_name' into field. Choices are: id, score, student, student_id, subject_name, year
    # ValueError: invalid literal for int() with base 10: '张三'

# 正确写法如下: grade_list = Grade.objects.filter(student__student_name='张三').aggregate(Sum('score')) print(grade_list) # 求语文期末成绩的最高分 grade_list = Grade.objects.filter(subject_name='语文').aggregate(Max('score')) print('语文的最高分:', grade_list) print('语文的最高分:', grade_list['score__max']) grade_list = Grade.objects.filter(subject_name='语文').aggregate(high_score = Max('score')) print('语文的最高分:', grade_list['high_score']) return HttpResponse('OK')

 

19_2、使用annotate为查询结果集中的每一项生成统计数据

from django.db.models import Count


q = Book.objects.annotate(Count('authors'))
q[0]
q[0].authors__count

grade/views.py

from django.db.models import Sum, Max
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
from grade.models import Grade, Student


def page_stas(request):
    """ 聚合及统计 """
    # 计算张三的期末成绩总和
    # student = Student.objects.get(student_name='张三')
    # print(type(student))  # 此处得到的是:<class 'grade.models.Student'>
    # print(student)
    # grade_list = Grade.objects.filter(student=student).aggregate(Sum('score'))

    # 此处需注意,外键查询,需带上字段:
    # grade_list = Grade.objects.filter(student_name='张三').aggregate(Sum('score'))
    # grade_list = Grade.objects.filter(student='张三').aggregate(Sum('score'))
    # 按上面写法,会报类似如下错误
    # django.core.exceptions.FieldError: Cannot resolve keyword 'student_name' into field. Choices are: id, score, student, student_id, subject_name, year
    # ValueError: invalid literal for int() with base 10: '张三'

    # 正确写法如下:
    grade_list = Grade.objects.filter(student__student_name='张三').aggregate(Sum('score'))
    print(grade_list)

    # 求语文期末成绩的最高分
    grade_list = Grade.objects.filter(subject_name='语文').aggregate(Max('score'))
    print('语文的最高分:', grade_list)
    print('语文的最高分:', grade_list['score__max'])

    grade_list = Grade.objects.filter(subject_name='语文').aggregate(high_score=Max('score'))
    print('语文的最高分:', grade_list['high_score'])

    # 统计每个学生的成绩总和  select student_name, SUM(score) from grade group by student_name
    # total_list = Grade.objects.all().annotate(Sum('score'))
    # for item in total_list:
    #     print(item.student.student_name, item.score__sum)

    total_list = Grade.objects.values_list('student__student_name').annotate(total_score=Sum('score'))
    for item in total_list:
        print(item[0], item[1])
    print("—————————————————————1———————————————————————")

    user_zhangsan = Student.objects.get(pk=1)
    print(type(user_zhangsan))      # <class 'grade.models.Student'>
    for item in user_zhangsan.stu_grade.all():
        print(item.subject_name, item.score)
    print("—————————————————————2———————————————————————")
    user_list = Student.objects.all().annotate(Sum('stu_grade__score'))
    for user in user_list:
        print(user.student_name, user.stu_grade__score__sum)
    print("—————————————————————3———————————————————————")
    user_list = Student.objects.all().annotate(total_score=Sum('stu_grade__score'))
    print(user_list)
    for user in user_list:
        print(user.student_name, user.total_score)

    return HttpResponse('OK')

 

20、自定义查询

_Q函数的使用:使用Q()函数实现复杂的查询;Q()函数支持&(且)和|(或),对应SQL中的AND和OR

weibo/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/26 18:15
# Author:LGSP_Harold
from django.urls import path, re_path
from weibo import views


urlpatterns = [
    # 对用户数据进行分页
    re_path(r'^user/(?P<page>\d+)/$', views.page_user, name='page_user'),
    # ORM查询练习
    path('search/', views.page_search, name='page_search'),
    # 事务练习
    path('trans/', views.page_trans, name='page_trans'),
    path('trans_with/', views.page_trans_with, name='page_trans_with'),
    # 手动控制事务
    path('trans_hand/', views.page_trans_hand, name='page_trans_hand'),
    # Q函数
    path('q/', views.page_q, name='page_q'),
]

weibo/views.py

from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db import transaction
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10 # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')


@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('page_trans')


def page_trans_with(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    with transaction.atomic():
        user2 = User.objects.get(pk=2)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='事务练习with')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('trans_with')


def page_trans_hand(request):
    """ 手动控制事务 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    try:
        # 放弃自动提交
        transaction.set_autocommit(False)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='手动控制事务')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论手动控制事务', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
        # 手动提交事务
        transaction.commit()
    except:
        # 不使用事务则手动删除数据
        # weibo.delete()
        # 手动控制事务,实现回滚
        transaction.rollback()

    return HttpResponse('trans_hand')


def page_q(request):
    """ Q函数的使用 """
    # 查询username或者nickname都为user1的用户
    # user_list1 = User.objects.filter(username='user1')
    # user_list2 = User.objects.filter(nickname='user1')
    # print(user_list1)
    # print(user_list2)
    # | 或着
    query = Q(username='user1') | Q(nickname='user1')
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # 从URL获取查询参数
    # http://127.0.0.1:8082/weibo/q/?name=user1
    print('访问:http://127.0.0.1:8082/weibo/q/?name=user1')
    name = request.GET.get('name', None)
    query = Q(username=name) | Q(nickname=name)
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # & 且 查询用户名是xxx,且暱称是xxx的用户
    query = Q()
    # username 按用户名查询
    username = request.GET.get('username', None)
    if username is not None:
        query = query & Q(username=username)

    # nickname 按暱称来查询
    nickname = request.GET.get('nickname', None)
    if nickname is not None:
        query = query & Q(nickname=nickname)

    # http://127.0.0.1:8082/weibo/q/?username=user1
    # http://127.0.0.1:8082/weibo/q/?nickname=user1
    # http://127.0.0.1:8082/weibo/q/?username=user11&nickname=user1
    user_list_q2 = User.objects.filter(query)
    print(user_list_q2)

    return HttpResponse("ok")

 

_F函数的使用:F()函数从数据库操作层面修改数据;F()函数可避免同时操作时竞态条件

 weibo/test_f.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:test_f.py
# Data:2020/12/1 20:07
# Author:LGSP_Harold
import threading

from django.db.models import F

from weibo.models import WeiboUser


class ChangeThread(threading.Thread):
    """ 改变用户的状态 """
    def __init__(self, max_count=100, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.max_count = max_count

    def run(self):
        count = 0
        user = WeiboUser.objects.get(pk=1296)
        while True:
            # 最多循环max_count次
            if count >= self.max_count:
                break
            print(self.getName(), count)
            # 10 11 SET status = 11
            # user.status += 1

            # set status = status + 1
            user.status = F('status') + 1
            user.save()
            count += 1


def main():
    t1 = ChangeThread(max_count=800)
    t2 = ChangeThread(max_count=500)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
python manage.py shell

from weibo.test_f import main

main()

 

21、使用SQL查询

_方式一:使用管理器的raw(sql)函数

raw(raw_query, params=None, translations=None)

返回django.db.models.query.RawQuerySet实例

weibo/urls.py

    # 使用SQL查询
    path('sql/', views.page_sql, name='page_sql'),

weibo/views.py

from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db import transaction
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10 # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')


@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('page_trans')


def page_trans_with(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    with transaction.atomic():
        user2 = User.objects.get(pk=2)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='事务练习with')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('trans_with')


def page_trans_hand(request):
    """ 手动控制事务 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    try:
        # 放弃自动提交
        transaction.set_autocommit(False)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='手动控制事务')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论手动控制事务', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
        # 手动提交事务
        transaction.commit()
    except:
        # 不使用事务则手动删除数据
        # weibo.delete()
        # 手动控制事务,实现回滚
        transaction.rollback()

    return HttpResponse('trans_hand')


def page_q(request):
    """ Q函数的使用 """
    # 查询username或者nickname都为user1的用户
    # user_list1 = User.objects.filter(username='user1')
    # user_list2 = User.objects.filter(nickname='user1')
    # print(user_list1)
    # print(user_list2)
    # | 或着
    query = Q(username='user1') | Q(nickname='user1')
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # 从URL获取查询参数
    # http://127.0.0.1:8082/weibo/q/?name=user1
    print('访问:http://127.0.0.1:8082/weibo/q/?name=user1')
    name = request.GET.get('name', None)
    query = Q(username=name) | Q(nickname=name)
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # & 且 查询用户名是xxx,且暱称是xxx的用户
    query = Q()
    # username 按用户名查询
    username = request.GET.get('username', None)
    if username is not None:
        query = query & Q(username=username)

    # nickname 按暱称来查询
    nickname = request.GET.get('nickname', None)
    if nickname is not None:
        query = query & Q(nickname=nickname)

    # http://127.0.0.1:8082/weibo/q/?username=user1
    # http://127.0.0.1:8082/weibo/q/?nickname=user1
    # http://127.0.0.1:8082/weibo/q/?username=user11&nickname=user1
    user_list_q2 = User.objects.filter(query)
    print(user_list_q2)

    return HttpResponse("ok")


# http://127.0.0.1:8082/weibo/sql/?username=user1
def page_sql(request):
    """ 使用SQL查询 """
    username = request.GET.get('username', '')
    # 方式一:使用raw函数进行sql查询
    # sql = 'select id, username, nickname from weibo_user'
    # user_list = User.objects.raw(sql)
    sql = 'select id, username, nickname from weibo_user where username=%s'
    user_list = User.objects.raw(sql, (username, ))
    for item in user_list:
        print(item)
        print(item.username, item.nickname, item.id)
    return HttpResponse('ok')

_方式二:获取数据库连接、游标,直接执行sql

# 获取数据库连接
from django.db import connection
# 从连接得到游标
cursor = connection.cursor()
# 执行SQL
cursor.execute("SELECT * FROM table_name WHERE xxx=%s", [xxx])
# 查询结果
row = cursor.fetchone()

weibo/urls.py

    # 使用纯SQL进行查询
    path('pure/sql/', views.page_pure_sql, name='page_pure_sql'),

weibo/views.py

from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db import transaction
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10 # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')


@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('page_trans')


def page_trans_with(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    with transaction.atomic():
        user2 = User.objects.get(pk=2)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='事务练习with')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('trans_with')


def page_trans_hand(request):
    """ 手动控制事务 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    try:
        # 放弃自动提交
        transaction.set_autocommit(False)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='手动控制事务')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论手动控制事务', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
        # 手动提交事务
        transaction.commit()
    except:
        # 不使用事务则手动删除数据
        # weibo.delete()
        # 手动控制事务,实现回滚
        transaction.rollback()

    return HttpResponse('trans_hand')


def page_q(request):
    """ Q函数的使用 """
    # 查询username或者nickname都为user1的用户
    # user_list1 = User.objects.filter(username='user1')
    # user_list2 = User.objects.filter(nickname='user1')
    # print(user_list1)
    # print(user_list2)
    # | 或着
    query = Q(username='user1') | Q(nickname='user1')
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # 从URL获取查询参数
    # http://127.0.0.1:8082/weibo/q/?name=user1
    print('访问:http://127.0.0.1:8082/weibo/q/?name=user1')
    name = request.GET.get('name', None)
    query = Q(username=name) | Q(nickname=name)
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # & 且 查询用户名是xxx,且暱称是xxx的用户
    query = Q()
    # username 按用户名查询
    username = request.GET.get('username', None)
    if username is not None:
        query = query & Q(username=username)

    # nickname 按暱称来查询
    nickname = request.GET.get('nickname', None)
    if nickname is not None:
        query = query & Q(nickname=nickname)

    # http://127.0.0.1:8082/weibo/q/?username=user1
    # http://127.0.0.1:8082/weibo/q/?nickname=user1
    # http://127.0.0.1:8082/weibo/q/?username=user11&nickname=user1
    user_list_q2 = User.objects.filter(query)
    print(user_list_q2)

    return HttpResponse("ok")


# http://127.0.0.1:8082/weibo/sql/?username=user1
def page_sql(request):
    """ 使用SQL查询 """
    username = request.GET.get('username', '')
    # 方式一:使用raw函数进行sql查询
    # sql = 'select id, username, nickname from weibo_user'
    # user_list = User.objects.raw(sql)
    sql = 'select id, username, nickname from weibo_user where username=%s'
    user_list = User.objects.raw(sql, (username, ))
    for item in user_list:
        print(item)
        print(item.username, item.nickname, item.id)
    return HttpResponse('ok')


# http://127.0.0.1:8082/weibo/pure/sql/?username=user1
def page_pure_sql(request):
    """ 使用纯SQL查询 """
    username = request.GET.get('username', '')
    # 1、获取数据库连接
    from django.db import connection
    sql = 'select id, username, nickname from weibo_user where username=%s'

    # 2、根据连接获取游标
    cursor = connection.cursor()
    # 3、根据游标来执行SQL
    cursor.execute(sql, (username, ))
    # 4、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')

 

22、使用Paginator分页器和自定义SQL分页

weibo/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/26 18:15
# Author:LGSP_Harold
from django.urls import path, re_path
from weibo import views


urlpatterns = [
    # 对用户数据进行分页
    re_path(r'^user/(?P<page>\d+)/$', views.page_user, name='page_user'),
    # ORM查询练习
    path('search/', views.page_search, name='page_search'),
    # 事务练习
    path('trans/', views.page_trans, name='page_trans'),
    path('trans_with/', views.page_trans_with, name='page_trans_with'),
    # 手动控制事务
    path('trans_hand/', views.page_trans_hand, name='page_trans_hand'),
    # Q函数
    path('q/', views.page_q, name='page_q'),
    # 使用SQL查询
    path('sql/', views.page_sql, name='page_sql'),
    # 使用纯SQL进行查询
    path('pure/sql/', views.page_pure_sql, name='page_pure_sql'),


    # 分页思考练习
    # paginator分页器django2.2.17
    path('page/sql/', views.page_page_sql, name='page_page_sql'),
    # paginator分页器django1.x
    # 自定义分页器
    path('paginator/sql/', views.page_paginator_sql, name='page_paginator_sql'),
    # 封装分页器
    path('paginator/sql2/', views.page_paginator_sql2, name='page_paginator_sql2'),

]

utils/sqlpage.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:sqlpage.py
# Data:2020/12/2 15:51
# Author:LGSP_Harold
import math

from django.db import connection


class PageNumError(Exception):
    """ 自定义异常,用于处理页面超出范围 """
    pass


class SqlPaginator:
    """ 实现sql分页 """

    def __init__(self, sql, params, page_size):
        super().__init__()
        self.sql = sql  # 要查询的sql
        self.params = params    # sql查询时的参数
        self.page_size = page_size

    @property
    def page_count(self):
        """ 总共有多少页 """
        # 总记录数 / 当前页面大小,向上取整
        return math.ceil(self.count / self.page_size)

    @property
    def count(self):
        """ 数据库中总共有多少条记录 """
        try:
            # 查询数据总数的SQL
            print('4444', self.sql, self.params)
            count_sql = 'select count(*) from ({}) as count_record'.format(self.sql)
            cursor = connection.cursor()
            cursor.execute(count_sql, self.params)
            rest = cursor.fetchone()[0]
        except:
            rest = 0
        return rest

    def page(self, now_page):
        """
        获取当前页
        :param now_page: 页码
        """
        # 嵌套子查询
        # select count (*) from (select id, username, nickname from weibo_user where id > 20) as count_record
        # 判断页面是否超出了范围
        if now_page > self.page_count or now_page < 1:
            raise PageNumError
        offset = (now_page - 1) * self.page_size
        sql = self.sql + ' LIMIT %s OFFSET %s'
        cursor = connection.cursor()

        sql_params = []
        print('1111', self.params)
        for p in self.params:
            sql_params.append(p)
        sql_params.extend([self.page_size, offset])
        print('2222', sql_params)

        # sql_params = self.params      # 浅拷贝,修改sql_params会导致self.params的值跟着改变
        # sql_params.extend([self.page_size, offset])
        # print('333', sql_params)

        rest = cursor.execute(sql, sql_params)
        rows = cursor.fetchall()
        return rows

weibo/views.py

import math
from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage
from django.db import transaction
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from utils.sqlpage import SqlPaginator, PageNumError
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10   # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')


@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('page_trans')


def page_trans_with(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    with transaction.atomic():
        user2 = User.objects.get(pk=2)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='事务练习with')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('trans_with')


def page_trans_hand(request):
    """ 手动控制事务 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    try:
        # 放弃自动提交
        transaction.set_autocommit(False)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='手动控制事务')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论手动控制事务', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
        # 手动提交事务
        transaction.commit()
    except:
        # 不使用事务则手动删除数据
        # weibo.delete()
        # 手动控制事务,实现回滚
        transaction.rollback()

    return HttpResponse('trans_hand')


def page_q(request):
    """ Q函数的使用 """
    # 查询username或者nickname都为user1的用户
    # user_list1 = User.objects.filter(username='user1')
    # user_list2 = User.objects.filter(nickname='user1')
    # print(user_list1)
    # print(user_list2)
    # | 或着
    query = Q(username='user1') | Q(nickname='user1')
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # 从URL获取查询参数
    # http://127.0.0.1:8082/weibo/q/?name=user1
    print('访问:http://127.0.0.1:8082/weibo/q/?name=user1')
    name = request.GET.get('name', None)
    query = Q(username=name) | Q(nickname=name)
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # & 且 查询用户名是xxx,且暱称是xxx的用户
    query = Q()
    # username 按用户名查询
    username = request.GET.get('username', None)
    if username is not None:
        query = query & Q(username=username)

    # nickname 按暱称来查询
    nickname = request.GET.get('nickname', None)
    if nickname is not None:
        query = query & Q(nickname=nickname)

    # http://127.0.0.1:8082/weibo/q/?username=user1
    # http://127.0.0.1:8082/weibo/q/?nickname=user1
    # http://127.0.0.1:8082/weibo/q/?username=user11&nickname=user1
    user_list_q2 = User.objects.filter(query)
    print(user_list_q2)

    return HttpResponse("ok")


# http://127.0.0.1:8082/weibo/sql/?username=user1
def page_sql(request):
    """ 使用SQL查询 """
    username = request.GET.get('username', '')
    # 方式一:使用raw函数进行sql查询
    # sql = 'select id, username, nickname from weibo_user'
    # user_list = User.objects.raw(sql)
    sql = 'select id, username, nickname from weibo_user where username=%s'
    user_list = User.objects.raw(sql, (username, ))
    for item in user_list:
        print(item)
        print(item.username, item.nickname, item.id)
    return HttpResponse('ok')


# http://127.0.0.1:8082/weibo/pure/sql/?username=user1
def page_pure_sql(request):
    """ 使用纯SQL查询 """
    username = request.GET.get('username', '')
    # 1、获取数据库连接
    from django.db import connection
    sql = 'select id, username, nickname from weibo_user where username=%s'

    # 2、根据连接获取游标
    cursor = connection.cursor()
    # 3、根据游标来执行SQL
    cursor.execute(sql, (username, ))
    # 4、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')


def page_page_sql(request):
    # 分页思考练习
    # sql = 'select id, username, nickname from weibo_user'
    # count_sql = 'select count(*) from {}'.format(sql)
    #
    # page_size = 10
    # count_page = math.ceil(count / page_size)

    page = ''
    page_size = 10
    sql = 'select id, username, nickname from weibo_user'

    user_list = User.objects.raw(sql, )
    print(len(user_list))
    # 使用分页器进行查询(低版本的django无法直接使用分页器进行分页,当前版本django2.2.17)
    paginator = Paginator(user_list, page_size)
    try:
        page = int(request.GET.get('page', 1))
        # 获取总页数
        count_page = paginator.num_pages
        print(count_page)
        # if page > count_page:
            # raise PageNumError
        page_data = paginator.page(page)
        print(page_data.object_list)
    # except PageNumError as e:
    #     return HttpResponse('invalid page number')
    except EmptyPage as e:
        return HttpResponse('没有数据')
    except PageNotAnInteger as e:
        return HttpResponse('页码错误,必须是正整数')
    except ValueError as e:
        return HttpResponse('页码错误,必须是正整数')
    except InvalidPage as e:
        return HttpResponse('Invalid Page')
    # 使用切片进行分页[不推荐]
    # print(user_list[50: 60])

    return HttpResponse('ok')


def page_paginator_sql(request):
    """ 自定义SQL分页器 """
    # 1、准备sql
    try:
        # page = 1    # 表示页码,当前第几页
        page = int(request.GET.get('page', 1))
    except:
        return HttpResponse('no valid page')
    page_size = 10  # 每一页的数据大小
    offset = (page - 1) * page_size     # 偏移量
    sql = 'select id, username, nickname from weibo_user limit %s offset %s'
    # 2、获取数据库连接
    from django.db import connection
    # 3、根据连接获取游标
    cursor = connection.cursor()
    # 4、根据游标来执行SQL
    cursor.execute(sql, (page_size, offset, ))
    # 5、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')


def page_paginator_sql2(request):
    # 封装分页类
    page = ''
    try:
        page = int(request.GET.get('page', 1))  # 表示页码,当前第几页
    except:
        print('no valid page')
    sql = 'select id, username, nickname from weibo_user where id > %s'
    sql_params = [5]    # id大于5
    page_size = 10
    try:
        paginator = SqlPaginator(sql, sql_params, page_size)
        page_data = paginator.page(page)
        for row in page_data:
            print(row)

        # 记录总数
        count = paginator.count
        print('总记录数:', count)
        page_count = paginator.page_count
        print('总页数:', page_count)
    except PageNumError as e:
        return HttpResponse('invalid page number')
    return HttpResponse('ok')

 

23、管理器Manager

_Manager是Django的模型进行数据库查询操作的接口

_每个模型都拥有至少一个Manager

_Django为每个模型类添加一个名为objects的默认Manager

23_1、自定义管理器

_添加新的管理器

from django.db import models


class User(models.Model):
    # ....
    users = models.Manager()

weibo/models.py

from django.db import models


class UserManager(models.Manager):
    """ 自定义用户管理器 """
    def top_users(self):
        """ 最近添加的用户 """
        # 方式1.直接通过操作数据库查询
        from django.db import connection
        with connection.cursor() as cursor:
            pass
        # 方式2.调用现有的方法
        return self.all().order_by('-create_at')[:5]


# Create your models here.
class WeiboUser(models.Model):
    """ 微博用户 """
    username = models.CharField('用户名', max_length=64, unique=True)
    password = models.CharField('密码', max_length=128)
    nickname = models.CharField('暱称', max_length=32)
    # 逻辑删除
    USER_STATUS = (
        (2, '限制用户'),
        (1, '正常'),
        (0, '删除'),
    )
    status = models.SmallIntegerField('用户状态', choices=USER_STATUS, default=1)
    create_at = models.DateTimeField('用户创建时间', null=True, blank=True)
    remark = models.CharField('备注信息', blank=True, default='', max_length=100)

    # 添加一个自定义的管理器
    users = UserManager()

    class Meta:
        db_table = 'weibo_user'

    def __str__(self):
        return 'User:{0}, Nickname:{1}, pk:{2}, status:{3}'.format(self.username, self.nickname, self.id, self.status)


class Weibo(models.Model):
    """ 微博 """
    content = models.CharField('微博内容', max_length=500)
    user = models.ForeignKey(WeiboUser, on_delete=models.CASCADE, verbose_name='用户')
    created_at = models.DateTimeField('发布时间', auto_now_add=True)
    source = models.CharField('发布来源', null=True, blank=True, max_length=50)

    class Meta:
        db_table = 'weibo'


class WeiboImage(models.Model):
    """ 微博图片 """
    weibo = models.ForeignKey(Weibo, on_delete=models.CASCADE)
    image = models.ImageField('微博图片', upload_to='weibo')

    class Meta:
        db_table = 'weibo_images'


class Comment(models.Model):
    """ 微博评论 """
    content = models.CharField('评论', max_length=250)
    created_at = models.DateTimeField('评论时间', auto_now_add=True)
    user = models.ForeignKey(WeiboUser, on_delete=models.CASCADE, verbose_name='评论的用户')
    weibo = models.ForeignKey(Weibo, on_delete=models.CASCADE, verbose_name='评论的微博')

    class Meta:
        db_table = 'weibo_comments'


class Friend(models.Model):
    """ 好友关系 """
    user_from = models.ForeignKey(WeiboUser, verbose_name='关注人', on_delete=models.CASCADE, related_name='user_from')
    user_to = models.ForeignKey(WeiboUser, verbose_name='被关注人', on_delete=models.CASCADE, related_name='user_to')
    created_at = models.DateTimeField('关注时间', auto_now_add=True)

    class Meta:
        db_table = 'weibo_friends'


# user = WeiboUser()
# # 我关注的用户
# user.user_from.xx
# # 关注我的用户
# user.user_to.xx

python manage.py shell

from weibo.models import WeiboUser


WeiboUser.users.top_users()

23_2、代理

weibo/models.py

from django.db import models
from django.contrib.auth.models import User


class UserManager(models.Manager):
    """ 自定义用户管理器 """
    def top_users(self):
        """ 最近添加的用户 """
        # 方式1.直接通过操作数据库查询
        from django.db import connection
        with connection.cursor() as cursor:
            pass
        # 方式2.调用现有的方法
        return self.all().order_by('-create_at')[:5]


# Create your models here.
class WeiboUser(models.Model):
    """ 微博用户 """
    username = models.CharField('用户名', max_length=64, unique=True)
    password = models.CharField('密码', max_length=128)
    nickname = models.CharField('暱称', max_length=32)
    # 逻辑删除
    USER_STATUS = (
        (2, '限制用户'),
        (1, '正常'),
        (0, '删除'),
    )
    status = models.SmallIntegerField('用户状态', choices=USER_STATUS, default=1)
    create_at = models.DateTimeField('用户创建时间', null=True, blank=True)
    remark = models.CharField('备注信息', blank=True, default='', max_length=100)

    # 添加一个自定义的管理器
    users = UserManager()

    class Meta:
        db_table = 'weibo_user'

    def __str__(self):
        return 'User:{0}, Nickname:{1}, pk:{2}, status:{3}'.format(self.username, self.nickname, self.id, self.status)


class MyUser(WeiboUser):
    """ 代理WeiboUser,扩充其功能 """
    class Meta:
        proxy = True

    def get_format_username(self):
        """ 格式化用户名显示,只显示前3个字符,后面的用*号代替 """
        return self.username[:3] + '****'


class Weibo(models.Model):
    """ 微博 """
    content = models.CharField('微博内容', max_length=500)
    user = models.ForeignKey(WeiboUser, on_delete=models.CASCADE, verbose_name='用户')
    created_at = models.DateTimeField('发布时间', auto_now_add=True)
    source = models.CharField('发布来源', null=True, blank=True, max_length=50)

    class Meta:
        db_table = 'weibo'


class WeiboImage(models.Model):
    """ 微博图片 """
    weibo = models.ForeignKey(Weibo, on_delete=models.CASCADE)
    image = models.ImageField('微博图片', upload_to='weibo')

    class Meta:
        db_table = 'weibo_images'


class Comment(models.Model):
    """ 微博评论 """
    content = models.CharField('评论', max_length=250)
    created_at = models.DateTimeField('评论时间', auto_now_add=True)
    user = models.ForeignKey(WeiboUser, on_delete=models.CASCADE, verbose_name='评论的用户')
    weibo = models.ForeignKey(Weibo, on_delete=models.CASCADE, verbose_name='评论的微博')

    class Meta:
        db_table = 'weibo_comments'


class Friend(models.Model):
    """ 好友关系 """
    user_from = models.ForeignKey(WeiboUser, verbose_name='关注人', on_delete=models.CASCADE, related_name='user_from')
    user_to = models.ForeignKey(WeiboUser, verbose_name='被关注人', on_delete=models.CASCADE, related_name='user_to')
    created_at = models.DateTimeField('关注时间', auto_now_add=True)

    class Meta:
        db_table = 'weibo_friends'


# user = WeiboUser()
# # 我关注的用户
# user.user_from.xx
# # 关注我的用户
# user.user_to.xx

python manage.py shell

from weibo.models import MyUser


user = MyUser.users.get(pk=1)
user.get_format_username()

 

24、调试及查询优化调优

方式1:QuerySet.query属性查看执行的SQL

q = User.objects.all()
print(q.query)
SELECT ....
python manage.py shell
from weibo.models import WeiboUser as User


user_list = User.objects.all()
user_list.query
str(user_list.quesy)

 

方式2:django-debug-toolbar

安装:pip install django-debug-toolbar

settings.py

"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 2.2.17.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'hax*=-xp1(3fb_k(scbc1ooe0^gid@-6ibcmn2142mgv@96q7z'

# SECURITY WARNING: don't run with debug turned on in production!
# 上生产环境时一定记得改为False
DEBUG = True

# 开启生产模式后,需设置ALLOWED_HOSTS运行访问的链接:ALLOWED_HOSTS = ['*']
ALLOWED_HOSTS = ['*']

# The Debug Toolbar is shown only if your IP address is listed in the INTERNAL_IPS setting.
INTERNAL_IPS = [
    '127.0.0.1',
]

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'oauth.apps.OauthConfig',
    'course.apps.CourseConfig',
    'mall.apps.MallConfig',
    'weibo.apps.WeiboConfig',
    'grade.apps.GradeConfig',
    # django_debug_toolbar Make sure that 'django.contrib.staticfiles' is set up properly and add 'debug_toolbar' to your INSTALLED_APPS setting:
    'debug_toolbar',
]

MIDDLEWARE = [
    # The Debug Toolbar is mostly implemented in a middleware. Enable it in your settings module as follows:
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    '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',
]

ROOT_URLCONF = 'mysite.urls'

TEMPLATES = [
    {
        # 模板引擎配置
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 模板引擎按列表顺序搜索这些目录以查找模板源文件
        # 每种模板引擎后端都定义了一个惯用的名称作为应用内部存放模板的子目录名称
        # DTL——templates
        # Jinja2——jinja2
        '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',
            ],
        },
    },
    {
        # 模板引擎配置
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        # 模板引擎按列表顺序搜索这些目录以查找模板源文件
        # 每种模板引擎后端都定义了一个惯用的名称作为应用内部存放模板的子目录名称
        # DTL——templates
        # Jinja2——jinja2
        'DIRS': [os.path.join(BASE_DIR, 'jinja2'), ],
        # 决定模板引擎是否进入每个已安装的应用中查找模板
        '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',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'student',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'

# 用户上传目录
MEDIA_URL = '/medias/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'medias')

urls.py

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.contrib import admin
from django.urls import path, re_path, include
from django.views.static import serve
import debug_toolbar

from . import views

# 设置404等异常路由
handler500 = 'mysite.views.page_500'
handler404 = 'mysite.views.page_404'


urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    # 重定向的实验
    path('index1/', views.index_one, name='index_one'),
    path('index2/', views.index_two, name='index_two'),
    re_path(r'^article/(?P<year>[0-9]{4})/$', views.article, name='article_detail'),

    path('oauth/', include(('oauth.urls', 'oauth'), namespace='oauth')),

    # 打印请求对象
    path('print/request/', views.print_request, name='print_request'),
    # 响应对象实验
    path('print/response/', views.print_response, name='print_response'),
    # 响应JSON对象
    path('print/json/', views.print_json, name='print_json'),
    # 打印响应对象
    path('print/resp/attr/', views.print_resp_attr, name='print_resp_attr'),
    # 打印图片
    path('print/images/', views.print_images, name='print_images'),
    # Excel
    path('print/excels/', views.print_excels, name='print_excels'),


    # 展示当前的时间
    path('time/', views.now_time),
    # 页面从html加载
    path('now/', views.now_use_file),

    # # 模板引擎选择
    # path('templ/show/', views.templ_show, name='templ_show'),

    # 变量在模板中渲染——图片
    path('templ/image/', views.templ_image, name='templ_image'),
    # 模板标签的使用
    path('templ/tag/', views.templ_tag, name='templ_tag'),

    # 模板过滤器的使用
    path('templ/filter/', views.templ_filter, name='templ_filter'),

    path('course/', include(('course.urls', 'course'), namespace='course')),

    # 微博模块
    path('weibo/', include(('weibo.urls', 'weibo'), namespace='weibo')),

    path('grade/', include(('grade.urls', 'grade'), namespace='grade')),

    # django.debug.toolbar
    path('__debug__/', include(debug_toolbar.urls)),
]

# 添加自定义的静态资源目录访问
urlpatterns += [re_path(r'^medias/(?P<path>.*)$', serve, {
    'document_root': settings.MEDIA_ROOT
})]

weibo/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/26 18:15
# Author:LGSP_Harold
from django.urls import path, re_path
from weibo import views


urlpatterns = [
    # 对用户数据进行分页
    re_path(r'^user/(?P<page>\d+)/$', views.page_user, name='page_user'),
    # ORM查询练习
    path('search/', views.page_search, name='page_search'),
    # 事务练习
    path('trans/', views.page_trans, name='page_trans'),
    path('trans_with/', views.page_trans_with, name='page_trans_with'),
    # 手动控制事务
    path('trans_hand/', views.page_trans_hand, name='page_trans_hand'),
    # Q函数
    path('q/', views.page_q, name='page_q'),
    # 使用SQL查询
    path('sql/', views.page_sql, name='page_sql'),
    # 使用纯SQL进行查询
    path('pure/sql/', views.page_pure_sql, name='page_pure_sql'),


    # 分页思考练习
    # paginator分页器django2.2.17
    path('page/sql/', views.page_page_sql, name='page_page_sql'),
    # paginator分页器django1.x
    # 自定义分页器
    path('paginator/sql/', views.page_paginator_sql, name='page_paginator_sql'),
    # 封装分页器
    path('paginator/sql2/', views.page_paginator_sql2, name='page_paginator_sql2'),

    # SQL优化,调优
    path('enhance/sql/', views.page_enhance_sql, name='page_enhance_sql'),
]

weibo/views.py

import math
from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage
from django.db import transaction
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from utils.sqlpage import SqlPaginator, PageNumError
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10   # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')


@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('page_trans')


def page_trans_with(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    with transaction.atomic():
        user2 = User.objects.get(pk=2)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='事务练习with')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('trans_with')


def page_trans_hand(request):
    """ 手动控制事务 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    try:
        # 放弃自动提交
        transaction.set_autocommit(False)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='手动控制事务')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论手动控制事务', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
        # 手动提交事务
        transaction.commit()
    except:
        # 不使用事务则手动删除数据
        # weibo.delete()
        # 手动控制事务,实现回滚
        transaction.rollback()

    return HttpResponse('trans_hand')


def page_q(request):
    """ Q函数的使用 """
    # 查询username或者nickname都为user1的用户
    # user_list1 = User.objects.filter(username='user1')
    # user_list2 = User.objects.filter(nickname='user1')
    # print(user_list1)
    # print(user_list2)
    # | 或着
    query = Q(username='user1') | Q(nickname='user1')
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # 从URL获取查询参数
    # http://127.0.0.1:8082/weibo/q/?name=user1
    print('访问:http://127.0.0.1:8082/weibo/q/?name=user1')
    name = request.GET.get('name', None)
    query = Q(username=name) | Q(nickname=name)
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # & 且 查询用户名是xxx,且暱称是xxx的用户
    query = Q()
    # username 按用户名查询
    username = request.GET.get('username', None)
    if username is not None:
        query = query & Q(username=username)

    # nickname 按暱称来查询
    nickname = request.GET.get('nickname', None)
    if nickname is not None:
        query = query & Q(nickname=nickname)

    # http://127.0.0.1:8082/weibo/q/?username=user1
    # http://127.0.0.1:8082/weibo/q/?nickname=user1
    # http://127.0.0.1:8082/weibo/q/?username=user11&nickname=user1
    user_list_q2 = User.objects.filter(query)
    print(user_list_q2)

    return HttpResponse("ok")


# http://127.0.0.1:8082/weibo/sql/?username=user1
def page_sql(request):
    """ 使用SQL查询 """
    username = request.GET.get('username', '')
    # 方式一:使用raw函数进行sql查询
    # sql = 'select id, username, nickname from weibo_user'
    # user_list = User.objects.raw(sql)
    sql = 'select id, username, nickname from weibo_user where username=%s'
    user_list = User.objects.raw(sql, (username, ))
    for item in user_list:
        print(item)
        print(item.username, item.nickname, item.id)
    return HttpResponse('ok')


# http://127.0.0.1:8082/weibo/pure/sql/?username=user1
def page_pure_sql(request):
    """ 使用纯SQL查询 """
    username = request.GET.get('username', '')
    # 1、获取数据库连接
    from django.db import connection
    sql = 'select id, username, nickname from weibo_user where username=%s'

    # 2、根据连接获取游标
    cursor = connection.cursor()
    # 3、根据游标来执行SQL
    cursor.execute(sql, (username, ))
    # 4、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')


def page_page_sql(request):
    # 分页思考练习
    # sql = 'select id, username, nickname from weibo_user'
    # count_sql = 'select count(*) from {}'.format(sql)
    #
    # page_size = 10
    # count_page = math.ceil(count / page_size)

    page = ''
    page_size = 10
    sql = 'select id, username, nickname from weibo_user'

    user_list = User.objects.raw(sql, )
    print(len(user_list))
    # 使用分页器进行查询(低版本的django无法直接使用分页器进行分页,当前版本django2.2.17)
    paginator = Paginator(user_list, page_size)
    try:
        page = int(request.GET.get('page', 1))
        # 获取总页数
        count_page = paginator.num_pages
        print(count_page)
        # if page > count_page:
            # raise PageNumError
        page_data = paginator.page(page)
        print(page_data.object_list)
    # except PageNumError as e:
    #     return HttpResponse('invalid page number')
    except EmptyPage as e:
        return HttpResponse('没有数据')
    except PageNotAnInteger as e:
        return HttpResponse('页码错误,必须是正整数')
    except ValueError as e:
        return HttpResponse('页码错误,必须是正整数')
    except InvalidPage as e:
        return HttpResponse('Invalid Page')
    # 使用切片进行分页[不推荐]
    # print(user_list[50: 60])

    return HttpResponse('ok')


def page_paginator_sql(request):
    """ 自定义SQL分页器 """
    # 1、准备sql
    try:
        # page = 1    # 表示页码,当前第几页
        page = int(request.GET.get('page', 1))
    except:
        return HttpResponse('no valid page')
    page_size = 10  # 每一页的数据大小
    offset = (page - 1) * page_size     # 偏移量
    sql = 'select id, username, nickname from weibo_user limit %s offset %s'
    # 2、获取数据库连接
    from django.db import connection
    # 3、根据连接获取游标
    cursor = connection.cursor()
    # 4、根据游标来执行SQL
    cursor.execute(sql, (page_size, offset, ))
    # 5、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')


def page_paginator_sql2(request):
    # 封装分页类
    page = ''
    try:
        page = int(request.GET.get('page', 1))  # 表示页码,当前第几页
    except:
        print('no valid page')
    sql = 'select id, username, nickname from weibo_user where id > %s'
    sql_params = [5]    # id大于5
    page_size = 10
    try:
        paginator = SqlPaginator(sql, sql_params, page_size)
        page_data = paginator.page(page)
        for row in page_data:
            print(row)

        # 记录总数
        count = paginator.count
        print('总记录数:', count)
        page_count = paginator.page_count
        print('总页数:', page_count)
    except PageNumError as e:
        return HttpResponse('invalid page number')
    return HttpResponse('ok')


def page_enhance_sql(request):
    """ SQL 优化练习 """
    user_list = User.objects.all()

    return render(request, 'users.html', {
        'user_list': user_list,
    })

 

weibo/templates/users.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户列表</title>
</head>
<body>
为你查到如下列表
<ul>
    {% for item in user_list %}
    <li>{{ item.username }} - {{ item.nickname }}</li>
    {% endfor %}
</ul>
</body>
</html>

 P.S.8:运行时,可能会出现以下错误,请将多余的'django.contrib.staticfiles',删除

django.core.exceptions.ImproperlyConfigured: Application labels aren't unique, duplicates: staticfiles

P.S.9:如果访问出现如下错误,是由于urls.py配置导致

Page not found (404)
Request Method:     GET
Request URL:     http://127.0.0.1:8082/oauth/templ/filter/mine/

Using the URLconf defined in mysite.urls, Django tried these URL patterns, in this order:

    __debug__/

The current path, oauth/templ/filter/mine/, didn't match any of these.

You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.

——不要使用如下写法,正确写法参考上面的代码

if settings.DEBUG:
    import debug_toolbar
    urlpatterns = [
        path('__debug__/', include(debug_toolbar.urls)),
    ]

 

25、优化外键关联查询

QuerySet.select_related()将外键关联的对象查询合并到主查询,一次性查询结果,减少SQL执行的数量

weibo/views.py

import math
from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage
from django.db import transaction
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from utils.sqlpage import SqlPaginator, PageNumError
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10   # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')


@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('page_trans')


def page_trans_with(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    with transaction.atomic():
        user2 = User.objects.get(pk=2)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='事务练习with')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('trans_with')


def page_trans_hand(request):
    """ 手动控制事务 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    try:
        # 放弃自动提交
        transaction.set_autocommit(False)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='手动控制事务')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论手动控制事务', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
        # 手动提交事务
        transaction.commit()
    except:
        # 不使用事务则手动删除数据
        # weibo.delete()
        # 手动控制事务,实现回滚
        transaction.rollback()

    return HttpResponse('trans_hand')


def page_q(request):
    """ Q函数的使用 """
    # 查询username或者nickname都为user1的用户
    # user_list1 = User.objects.filter(username='user1')
    # user_list2 = User.objects.filter(nickname='user1')
    # print(user_list1)
    # print(user_list2)
    # | 或着
    query = Q(username='user1') | Q(nickname='user1')
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # 从URL获取查询参数
    # http://127.0.0.1:8082/weibo/q/?name=user1
    print('访问:http://127.0.0.1:8082/weibo/q/?name=user1')
    name = request.GET.get('name', None)
    query = Q(username=name) | Q(nickname=name)
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # & 且 查询用户名是xxx,且暱称是xxx的用户
    query = Q()
    # username 按用户名查询
    username = request.GET.get('username', None)
    if username is not None:
        query = query & Q(username=username)

    # nickname 按暱称来查询
    nickname = request.GET.get('nickname', None)
    if nickname is not None:
        query = query & Q(nickname=nickname)

    # http://127.0.0.1:8082/weibo/q/?username=user1
    # http://127.0.0.1:8082/weibo/q/?nickname=user1
    # http://127.0.0.1:8082/weibo/q/?username=user11&nickname=user1
    user_list_q2 = User.objects.filter(query)
    print(user_list_q2)

    return HttpResponse("ok")


# http://127.0.0.1:8082/weibo/sql/?username=user1
def page_sql(request):
    """ 使用SQL查询 """
    username = request.GET.get('username', '')
    # 方式一:使用raw函数进行sql查询
    # sql = 'select id, username, nickname from weibo_user'
    # user_list = User.objects.raw(sql)
    sql = 'select id, username, nickname from weibo_user where username=%s'
    user_list = User.objects.raw(sql, (username, ))
    for item in user_list:
        print(item)
        print(item.username, item.nickname, item.id)
    return HttpResponse('ok')


# http://127.0.0.1:8082/weibo/pure/sql/?username=user1
def page_pure_sql(request):
    """ 使用纯SQL查询 """
    username = request.GET.get('username', '')
    # 1、获取数据库连接
    from django.db import connection
    sql = 'select id, username, nickname from weibo_user where username=%s'

    # 2、根据连接获取游标
    cursor = connection.cursor()
    # 3、根据游标来执行SQL
    cursor.execute(sql, (username, ))
    # 4、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')


def page_page_sql(request):
    # 分页思考练习
    # sql = 'select id, username, nickname from weibo_user'
    # count_sql = 'select count(*) from {}'.format(sql)
    #
    # page_size = 10
    # count_page = math.ceil(count / page_size)

    page = ''
    page_size = 10
    sql = 'select id, username, nickname from weibo_user'

    user_list = User.objects.raw(sql, )
    print(len(user_list))
    # 使用分页器进行查询(低版本的django无法直接使用分页器进行分页,当前版本django2.2.17)
    paginator = Paginator(user_list, page_size)
    try:
        page = int(request.GET.get('page', 1))
        # 获取总页数
        count_page = paginator.num_pages
        print(count_page)
        # if page > count_page:
            # raise PageNumError
        page_data = paginator.page(page)
        print(page_data.object_list)
    # except PageNumError as e:
    #     return HttpResponse('invalid page number')
    except EmptyPage as e:
        return HttpResponse('没有数据')
    except PageNotAnInteger as e:
        return HttpResponse('页码错误,必须是正整数')
    except ValueError as e:
        return HttpResponse('页码错误,必须是正整数')
    except InvalidPage as e:
        return HttpResponse('Invalid Page')
    # 使用切片进行分页[不推荐]
    # print(user_list[50: 60])

    return HttpResponse('ok')


def page_paginator_sql(request):
    """ 自定义SQL分页器 """
    # 1、准备sql
    try:
        # page = 1    # 表示页码,当前第几页
        page = int(request.GET.get('page', 1))
    except:
        return HttpResponse('no valid page')
    page_size = 10  # 每一页的数据大小
    offset = (page - 1) * page_size     # 偏移量
    sql = 'select id, username, nickname from weibo_user limit %s offset %s'
    # 2、获取数据库连接
    from django.db import connection
    # 3、根据连接获取游标
    cursor = connection.cursor()
    # 4、根据游标来执行SQL
    cursor.execute(sql, (page_size, offset, ))
    # 5、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')


def page_paginator_sql2(request):
    # 封装分页类
    page = ''
    try:
        page = int(request.GET.get('page', 1))  # 表示页码,当前第几页
    except:
        print('no valid page')
    sql = 'select id, username, nickname from weibo_user where id > %s'
    sql_params = [5]    # id大于5
    page_size = 10
    try:
        paginator = SqlPaginator(sql, sql_params, page_size)
        page_data = paginator.page(page)
        for row in page_data:
            print(row)

        # 记录总数
        count = paginator.count
        print('总记录数:', count)
        page_count = paginator.page_count
        print('总页数:', page_count)
    except PageNumError as e:
        return HttpResponse('invalid page number')
    return HttpResponse('ok')


def page_enhance_sql(request):
    """ SQL 优化练习 """
    user_list = User.objects.all()
    return render(request, 'users.html', {
        'user_list': user_list,
    })


def page_comments(request):
    """ 评论查询优化 """
    comment_list = Comment.objects.all().select_related('weibo', 'user')
    return render(request, 'comments.html', {
        'comment_list': comment_list,
    })

weibo/urls.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:urls.py
# Data:2020/11/26 18:15
# Author:LGSP_Harold
from django.urls import path, re_path
from weibo import views


urlpatterns = [
    # 对用户数据进行分页
    re_path(r'^user/(?P<page>\d+)/$', views.page_user, name='page_user'),
    # ORM查询练习
    path('search/', views.page_search, name='page_search'),
    # 事务练习
    path('trans/', views.page_trans, name='page_trans'),
    path('trans_with/', views.page_trans_with, name='page_trans_with'),
    # 手动控制事务
    path('trans_hand/', views.page_trans_hand, name='page_trans_hand'),
    # Q函数
    path('q/', views.page_q, name='page_q'),
    # 使用SQL查询
    path('sql/', views.page_sql, name='page_sql'),
    # 使用纯SQL进行查询
    path('pure/sql/', views.page_pure_sql, name='page_pure_sql'),


    # 分页思考练习
    # paginator分页器django2.2.17
    path('page/sql/', views.page_page_sql, name='page_page_sql'),
    # paginator分页器django1.x
    # 自定义分页器
    path('paginator/sql/', views.page_paginator_sql, name='page_paginator_sql'),
    # 封装分页器
    path('paginator/sql2/', views.page_paginator_sql2, name='page_paginator_sql2'),

    # SQL优化,调优
    path('enhance/sql/', views.page_enhance_sql, name='page_enhance_sql'),
    # 微博评论查询优化
    path('comments/sql/', views.page_comments, name='page_comments')
]

weibo/templates/comments.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>微博评论</title>
</head>
<body>
<ul>
    {% for item in comment_list %}
    <li>
        <h4>评论内容:{{ item.content }}</h4>
        <p>{{ item.created_at }} ——来自{{ item.user.nickname }}</p>
        <p>微博内容:{{ item.weibo.content }}</p>
    </li>
    {% endfor %}
</ul>
</body>
</html>

 

 26、创建第一个表单

 _1、创建表单类

weibo/forms.py

from django import forms


class LoginForm(forms.Form):
    """ 自定义表单 """
    username = forms.CharField(label='用户名', max_length=100)
    

_2、添加到视图

weibo/views.py

from weibo.forms import LoginForm
def page_form_first(request):
    # 表单对象
    form = LoginForm()
    return render(request, 'page_form_first.html', {
        'form': form
    })

 

_3、配置urls.py

weibo/urls.py

    # form表单练习
    path('form/first/', views.page_form_first, name='page_form_first'),

 

_4、渲染到模板

weibo/templates/page_form_first.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>我的第一个表单</title>
</head>
<body>
{#<form action="{% url 'weibo:page_form_first' %}"></form>#}
<form action="." method="post">
    {{ form }}
    <input type="submit" value="提交">
</form>
</body>
</html>

_5、在视图中处理表单数据

from weibo.forms import LoginForm

def page_form_first(request):
    """ 第一个表单 """
    # 如果是POST,要接收参数,然后处理里面的逻辑
    if request.method == 'POST':
        form = LoginForm(request.POST)
        # 验证表单里面数据的合法性
        if form.is_valid():
            # 取数据
            data = form.cleaned_data
            print('data:', data)
            # 其他业务逻辑
    else:
        # 如果GET请求,展示我们的页面
        form = LoginForm()
        print(form)
    return render(request, 'page_form_first.html', {
        'form': form
    })

 

27、Form对象的属性和方法

1.is_bound——是否已经绑定数据

def page_form_first(request):
    """ 第一个表单 """
    # 如果是POST,要接收的参数,然后处理里面的逻辑
    if request.method == 'POST':
        form = LoginForm(request.POST)
        # 验证表单里面数据的合法性
        print('POST:', form.is_bound)
        if form.is_valid():
            # 取数据
            data = form.cleaned_data
            print(data)
            # 其他的业务逻辑
    else:
        # 如果GET请求,展示我们的页面
        form = LoginForm()
        print(form)
        print('GET:', form.is_bound)
    return render(request, 'page_form_first.html', {
        'form': form
    })

2.is_valid()——表单是否已经通过验证

3.cleaned_data——访问表单验证后的数据

4.as_p()/as_us()/as_table()——渲染表单

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>我的第一个表单</title>
</head>
<body>
{#<form action="{% url 'weibo:page_form_first' %}" method="post">#}
{#</form>#}
<form action="." method="post">
    {{ form }}
    {{ form.as_p }}
    <input type="submit" value="提交">
</form>
</body>
</html>

5.errors——表单验证后的错误信息

6.fields——表单中的字段

_表单字段继承自django.forms.Field

_每个字段都具有核心参数

_1、required——是否为必填,默认为必填

_2、label——label标签(如:输入框前的文字描述)

_3、initial——初始化数据

_4、widget——定制界面显示方式(如:文本框、选择框)

_5、help_text——帮助文字

_6、error_messages——覆盖字段引发异常后的错误显示

_7、localize——本地化,根据用户所在地区格式进行显示

_8、disabled——禁用表单,界面上不可操作

_9、has_changed()——值是否发生了改变

_每个字段都可以有自定义的验证逻辑

_每个字段都有钩子函数,方便扩充功能

 

7.initial——初始化数据

def page_form_first(request):
    """ 第一个表单 """
    # 如果是POST,要接收的参数,然后处理里面的逻辑
    if request.method == 'POST':
        form = LoginForm(request.POST)
        # 验证表单里面数据的合法性
        print('POST:', form.is_bound)
        if form.is_valid():
            # 取数据
            data = form.cleaned_data
            print(data)
            # 其他的业务逻辑
    else:
        # 如果GET请求,展示我们的页面
        form = LoginForm(initial={'username': '用户名'})
        print(form)
        print('GET:', form.is_bound)
    return render(request, 'page_form_first.html', {
        'form': form
    })

 

28、Django内置的字段

_文本/字符串

_1.CharField——字符串输入

_2.EmailField——邮件输入

_3.URLField——URL地址输入

_4.UUIDField——uuid字符串输入

_数值(整数,小数)

_1.FloatField——浮点数输入

_2.IntegerField——整数输入

_3.DecimalField——小数输入(更精确)

_选择

_1.ChoiceField——单选

_2.MultipleChoiceField——多选

_3.TypedChoiceField——高级选择(支持结果转换类型)

_日期/时间

_1.DateField——日期选择

_2.DateTimeField——日期时间选择

_3.DurationField——时间片段timedelta

_4.TimeField——时间选择

_文件/文件上传

_1.FileField——文件

_2.FilePathField——文件路径

_3.ImageField——图片

_布尔

_1.BooleanField——True/False

_2.NullBooleanField——None/True/False

_正则

_1.RegexField——正则输入

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:forms.py
# Data:2020/12/14 21:13
# Author:LGSP_Harold
from django import forms


class LoginForm(forms.Form):
    """ 自定义表单  """
    SEX_CHOICES = (
        (0, '未知'),
        (1, ''),
        (2, ''),
    )

    username = forms.CharField(label='用户名', max_length=64, required=False, help_text='使用帮助')
    email = forms.EmailField(label='电子邮箱')
    sex = forms.ChoiceField(label='性别', choices=SEX_CHOICES)
    birth = forms.DateField(label='生日', help_text='1944-9-5')     # 1944-9-5

 

29、自定义界面显示

_文本输入

_1.TextInput

_2.NumberInput

_3.EmailInput

_4.URLInput

_5.PasswordInput

_6.HiddenInput

_7.DateInput

_8.DateTimeInput

_9.TimeInput

_10.Textarea

_选择输入

_1.Select

_2.NullBooleanSelect

_3.RadioSelect

_4.SelectMultiple

_5.CheckboxSelectMultiple

_文件上传

_1.FileInput

_复合Widget

_1.MultipleHiddenInput

_2.SplitDateTimeWidget

_3.SplitHiddenDateTimeWidget

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:forms.py
# Data:2020/12/14 21:13
# Author:LGSP_Harold
from django import forms


class LoginForm(forms.Form):
    """ 自定义表单  """
    SEX_CHOICES = (
        (0, '未知'),
        (1, ''),
        (2, ''),
    )

    username = forms.CharField(label='用户名', max_length=64, required=False, help_text='使用帮助')
    email = forms.EmailField(label='电子邮箱')
    sex = forms.ChoiceField(label='性别', choices=SEX_CHOICES)
    birth = forms.DateField(label='生日', help_text='1944-9-5')     # 1944-9-5
    remark = forms.CharField(label='备注信息', max_length=256, widget=forms.Textarea)
    age = forms.IntegerField(label='年龄', widget=forms.NumberInput)
    password = forms.CharField(label='密码', widget=forms.PasswordInput)

 

30、表单验证

_单个字段验证

_1.定义clean_xxx验证方法

_2.获取对应的字段值并验证

_3.验证失败触发form.ValidationError异常

_4.返回验证后的数据

_5.在html页面显示错误

——举例:验证用户名为手机号

weibo/forms.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:forms.py
# Data:2020/12/14 21:13
# Author:LGSP_Harold
import re

from django import forms


class LoginForm(forms.Form):
    """ 自定义表单  """
    SEX_CHOICES = (
        (0, '未知'),
        (1, ''),
        (2, ''),
    )

    username = forms.CharField(label='用户名', max_length=64, required=False, help_text='使用帮助')
    email = forms.EmailField(label='电子邮箱')
    sex = forms.ChoiceField(label='性别', choices=SEX_CHOICES)
    birth = forms.DateField(label='生日', help_text='1944-9-5')     # 1944-9-5
    remark = forms.CharField(label='备注信息', max_length=256, widget=forms.Textarea)
    age = forms.IntegerField(label='年龄', widget=forms.NumberInput)
    password = forms.CharField(label='密码', widget=forms.PasswordInput)


class UserLogin(forms.Form):
    """ 用户登录 """
    username = forms.CharField(label='用户名', max_length=64)
    password = forms.CharField(label='密码', max_length=64, widget=forms.PasswordInput)
    verify_code = forms.CharField(label='验证码', max_length=4)

    def clean_username(self):
        """ 验证用户名 hook 钩子函数 """
        username = self.cleaned_data['username']
        print(username)
        pattern = r'^0{0, 1}1[0-9]{10}$'
        if not username:
            raise forms.ValidationError('用户名不能为空')
        if not re.search(pattern, username):
            raise forms.ValidationError('用户名必须是手机号')
        return username


class UserRegister(forms.Form):
    """ 用户注册 """
    username = forms.CharField(label='用户名', max_length=64)
    nickname = forms.CharField(label='暱称', max_length=64)
    password = forms.CharField(label='设置密码', max_length=64, widget=forms.PasswordInput)
    password_repeat = forms.CharField(label='确认密码', max_length=64, widget=forms.PasswordInput)
    verify_code = forms.CharField(label='验证码', max_length=4)

weibo/views.py

def user_login(request):
    """ 用户登录 """
    # 第一次访问URL,GET展示表单,供用户输入
    # 第二次访问URL,POST
    if request.method == 'POST':
        form = UserLogin(request.POST)
        # 表单是否验证
        if form.is_valid():
            # 执行登录
            pass
        else:
            print(form.errors)
    else:
        form = UserLogin()
    return render(request, 'user_login.html', {
        'form': form,
    })

weibo/templates/user_login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<form action="." method="post">
{#    {{ form.as_p }}#}
    <!-- 表单内容的展示 -->
    <div>
        {{ form.username.label }}:
        {{ form.username }}
        {{ form.username.errors.as_text }}
    </div>
    <div>
        {{ form.password.label }}:
        {{ form.password }}
        {{ form.password.errors.as_text }}
    </div>
    <div>
        {{ form.verify_code.label }}:
        {{ form.verify_code }}
        {{ form.verify_code.errors.as_text }}
    </div>
    <div><input type="submit" value="登录"></div>
</form>
</body>
</html>

 

_多个字段综合验证

_1.重写clean()方法

_2.获取数据并验证

_3.验证失败触发forms.ValidationError异常

_4.返回验证后的表单数据cleaned_data

_5.在html页面显示错误non_field_errors

——举例:验证用户名和密码是否正确

weibo/forms.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:mysite
# File:forms.py
# Data:2020/12/14 21:13
# Author:LGSP_Harold
import re

from django import forms

from weibo.models import WeiboUser


class LoginForm(forms.Form):
    """ 自定义表单  """
    SEX_CHOICES = (
        (0, '未知'),
        (1, ''),
        (2, ''),
    )

    username = forms.CharField(label='用户名', max_length=64, required=False, help_text='使用帮助')
    email = forms.EmailField(label='电子邮箱')
    sex = forms.ChoiceField(label='性别', choices=SEX_CHOICES)
    birth = forms.DateField(label='生日', help_text='1944-9-5')     # 1944-9-5
    remark = forms.CharField(label='备注信息', max_length=256, widget=forms.Textarea)
    age = forms.IntegerField(label='年龄', widget=forms.NumberInput)
    password = forms.CharField(label='密码', widget=forms.PasswordInput)


class UserLogin(forms.Form):
    """ 用户登录 """
    username = forms.CharField(label='用户名', max_length=64)
    password = forms.CharField(label='密码', max_length=64, widget=forms.PasswordInput)
    verify_code = forms.CharField(label='验证码', max_length=4)

    def clean_username(self):
        """ 验证用户名 hook 钩子函数 """
        username = self.cleaned_data['username']
        print(username)
        pattern = r'^[0]{0,1}[1]\d{10}$'
        if not username:
            raise forms.ValidationError('用户名不能为空')
        if not re.search(pattern, username):
            raise forms.ValidationError('用户名必须是手机号')
        return username

    def clean(self):
        clean_data = super().clean()
        print(clean_data)
        # 获取用户名和密码
        username = clean_data.get('username', None)
        password = clean_data.get('password', None)

        if username and password:
            # 查询用户名和密码匹配的用户

            # count = WeiboUser.objects.filter(username=username, password=password).count()
            # if count == 0:
            #     raise forms.ValidationError('用户名或密码不匹配')

            user_list = WeiboUser.objects.filter(username=username)
            if user_list.count() == 0:
                raise forms.ValidationError('用户名不存在')
            # 验证密码是否正确
            # if user_list.filter(password=password).count() == 0:
            if not user_list.filter(password=password).exists():
                raise forms.ValidationError('密码错误')

        return clean_data


class UserRegister(forms.Form):
    """ 用户注册 """
    username = forms.CharField(label='用户名', max_length=64)
    nickname = forms.CharField(label='暱称', max_length=64)
    password = forms.CharField(label='设置密码', max_length=64, widget=forms.PasswordInput)
    password_repeat = forms.CharField(label='确认密码', max_length=64, widget=forms.PasswordInput)
    verify_code = forms.CharField(label='验证码', max_length=4)

weibo/views.py

import math
from datetime import datetime

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage
from django.db import transaction
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
from utils.sqlpage import SqlPaginator, PageNumError
from weibo.forms import LoginForm, UserLogin, UserRegister
from weibo.models import WeiboUser as User, Comment, Weibo


def page_user(request, page):
    """ 对用户进行分页处理 """
    page_size = 10   # 每页展示10条记录
    user_list = User.objects.all()  # 要分页的数据结果集QuerySet
    p = Paginator(user_list, page_size)
    print('总记录数:', p.count)
    print('总页数:', p.num_pages)
    print('页码范围:', p.page_range)

    # 获取某一页的数据
    try:
        page_data = p.page(page)
        print('数据列表:', page_data.object_list)
        print('当前第几页', page_data.number)
        print('是否有上一页:', page_data.has_previous())
        print('是否有下一页:', page_data.has_next())
    # 永远不要相信用户的输入
    except PageNotAnInteger as e:
        print('页码错误:', e)
    except EmptyPage as e:
        print('没有数据:', e)

    return HttpResponse('OK')


def page_search(request):
    """ ORM查询练习 """
    # user_list = User.objects.filter(username='user1')

    # user_list = User.objects.filter(nickname__contains='JIM')

    # user_list = User.objects.filter(nickname__startswith='JIM')
    # user_list = User.objects.filter(nickname__endswith='JIM')

    # user_list = User.objects.filter(status__in=(2, 4, 5))

    # user_list = User.objects.filter(status__gt=2)
    # user_list = User.objects.filter(status__gte=2)

    # 是否是空值
    # user_list = User.objects.filter(create_at__isnull=True)
    # 是否是空字符串
    # user_list = User.objects.filter(remark__exact='')
    # print(user_list.count())

    # settings.py 需将USE_TZ设置为False,不然会因为时区不一致导致查询到的数据出现错误
    # user_list = User.objects.filter(create_at__date='2020-11-18')
    # 查询当前日期的数据
    # date = datetime.now()
    # print(date)
    # user_list = User.objects.filter(create_at__date=date)
    # 查询某个月份的数据
    # user_list = User.objects.filter(create_at__month=10)
    # 查询某个日期之后的数据
    # date = datetime(2020, 12, 1)
    # print(date)
    # user_list = User.objects.filter(create_at__gt=date)
    # print(user_list)

    # 查询user1的微博评论
    # user = User.objects.get(pk=2)
    # 外键是对象
    # comment_list = Comment.objects.filter(user=user)
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    # 根据外键的某个属性(字段)进行查询
    # comment_list = Comment.objects.filter(user__username='user1')
    # for item in comment_list:
    #     print(item)
    #     print(item.user.username)
    #     print(item.content)

    return HttpResponse('OK')


@transaction.atomic()
def page_trans(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    # 发布微博
    weibo = Weibo.objects.create(user=user2, content='事务练习')
    # 发布评论
    comment = Comment.objects.create(user=user2, content='微博评论', weibo=weibo)
    print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('page_trans')


def page_trans_with(request):
    """ 事务练习 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    with transaction.atomic():
        user2 = User.objects.get(pk=2)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='事务练习with')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论with', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
    return HttpResponse('trans_with')


def page_trans_hand(request):
    """ 手动控制事务 """
    # 用户发布微博的时候,顺便发布一条评论,只能同时成功,不能失败
    user2 = User.objects.get(pk=2)
    try:
        # 放弃自动提交
        transaction.set_autocommit(False)
        # 发布微博
        weibo = Weibo.objects.create(user=user2, content='手动控制事务')
        # 发布评论
        comment = Comment.objects.create(user=user2, content='微博评论手动控制事务', weibo=weibo)
        print('weibo:', weibo.id, ';comment:', comment.id)
        # 手动提交事务
        transaction.commit()
    except:
        # 不使用事务则手动删除数据
        # weibo.delete()
        # 手动控制事务,实现回滚
        transaction.rollback()

    return HttpResponse('trans_hand')


def page_q(request):
    """ Q函数的使用 """
    # 查询username或者nickname都为user1的用户
    # user_list1 = User.objects.filter(username='user1')
    # user_list2 = User.objects.filter(nickname='user1')
    # print(user_list1)
    # print(user_list2)
    # | 或着
    query = Q(username='user1') | Q(nickname='user1')
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # 从URL获取查询参数
    # http://127.0.0.1:8082/weibo/q/?name=user1
    print('访问:http://127.0.0.1:8082/weibo/q/?name=user1')
    name = request.GET.get('name', None)
    query = Q(username=name) | Q(nickname=name)
    user_list_q = User.objects.filter(query)
    print(user_list_q)
    print('——————————————————————————————————————————————')

    # & 且 查询用户名是xxx,且暱称是xxx的用户
    query = Q()
    # username 按用户名查询
    username = request.GET.get('username', None)
    if username is not None:
        query = query & Q(username=username)

    # nickname 按暱称来查询
    nickname = request.GET.get('nickname', None)
    if nickname is not None:
        query = query & Q(nickname=nickname)

    # http://127.0.0.1:8082/weibo/q/?username=user1
    # http://127.0.0.1:8082/weibo/q/?nickname=user1
    # http://127.0.0.1:8082/weibo/q/?username=user11&nickname=user1
    user_list_q2 = User.objects.filter(query)
    print(user_list_q2)

    return HttpResponse("ok")


# http://127.0.0.1:8082/weibo/sql/?username=user1
def page_sql(request):
    """ 使用SQL查询 """
    username = request.GET.get('username', '')
    # 方式一:使用raw函数进行sql查询
    # sql = 'select id, username, nickname from weibo_user'
    # user_list = User.objects.raw(sql)
    sql = 'select id, username, nickname from weibo_user where username=%s'
    user_list = User.objects.raw(sql, (username, ))
    for item in user_list:
        print(item)
        print(item.username, item.nickname, item.id)
    return HttpResponse('ok')


# http://127.0.0.1:8082/weibo/pure/sql/?username=user1
def page_pure_sql(request):
    """ 使用纯SQL查询 """
    username = request.GET.get('username', '')
    # 1、获取数据库连接
    from django.db import connection
    sql = 'select id, username, nickname from weibo_user where username=%s'

    # 2、根据连接获取游标
    cursor = connection.cursor()
    # 3、根据游标来执行SQL
    cursor.execute(sql, (username, ))
    # 4、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')


def page_page_sql(request):
    # 分页思考练习
    # sql = 'select id, username, nickname from weibo_user'
    # count_sql = 'select count(*) from {}'.format(sql)
    #
    # page_size = 10
    # count_page = math.ceil(count / page_size)

    page = ''
    page_size = 10
    sql = 'select id, username, nickname from weibo_user'

    user_list = User.objects.raw(sql, )
    print(len(user_list))
    # 使用分页器进行查询(低版本的django无法直接使用分页器进行分页,当前版本django2.2.17)
    paginator = Paginator(user_list, page_size)
    try:
        page = int(request.GET.get('page', 1))
        # 获取总页数
        count_page = paginator.num_pages
        print(count_page)
        # if page > count_page:
            # raise PageNumError
        page_data = paginator.page(page)
        print(page_data.object_list)
    # except PageNumError as e:
    #     return HttpResponse('invalid page number')
    except EmptyPage as e:
        return HttpResponse('没有数据')
    except PageNotAnInteger as e:
        return HttpResponse('页码错误,必须是正整数')
    except ValueError as e:
        return HttpResponse('页码错误,必须是正整数')
    except InvalidPage as e:
        return HttpResponse('Invalid Page')
    # 使用切片进行分页[不推荐]
    # print(user_list[50: 60])

    return HttpResponse('ok')


def page_paginator_sql(request):
    """ 自定义SQL分页器 """
    # 1、准备sql
    try:
        # page = 1    # 表示页码,当前第几页
        page = int(request.GET.get('page', 1))
    except:
        return HttpResponse('no valid page')
    page_size = 10  # 每一页的数据大小
    offset = (page - 1) * page_size     # 偏移量
    sql = 'select id, username, nickname from weibo_user limit %s offset %s'
    # 2、获取数据库连接
    from django.db import connection
    # 3、根据连接获取游标
    cursor = connection.cursor()
    # 4、根据游标来执行SQL
    cursor.execute(sql, (page_size, offset, ))
    # 5、获取查询结果
    rows = cursor.fetchall()
    for row in rows:
        print(row)

    return HttpResponse('ok')


def page_paginator_sql2(request):
    # 封装分页类
    page = ''
    try:
        page = int(request.GET.get('page', 1))  # 表示页码,当前第几页
    except:
        print('no valid page')
    sql = 'select id, username, nickname from weibo_user where id > %s'
    sql_params = [5]    # id大于5
    page_size = 10
    try:
        paginator = SqlPaginator(sql, sql_params, page_size)
        page_data = paginator.page(page)
        for row in page_data:
            print(row)

        # 记录总数
        count = paginator.count
        print('总记录数:', count)
        page_count = paginator.page_count
        print('总页数:', page_count)
    except PageNumError as e:
        return HttpResponse('invalid page number')
    return HttpResponse('ok')


def page_enhance_sql(request):
    """ SQL 优化练习 """
    user_list = User.objects.all()
    return render(request, 'users.html', {
        'user_list': user_list,
    })


def page_comments(request):
    """ 评论查询优化 """
    comment_list = Comment.objects.all().select_related('weibo', 'user')
    return render(request, 'comments.html', {
        'comment_list': comment_list,
    })


def page_form_first(request):
    """ 第一个表单 """
    # 如果是POST,要接收的参数,然后处理里面的逻辑
    if request.method == 'POST':
        form = LoginForm(request.POST)
        # 验证表单里面数据的合法性
        print('POST:', form.is_bound)
        if form.is_valid():
            # 取数据
            data = form.cleaned_data
            print(data)
            # 其他的业务逻辑
    else:
        # 如果GET请求,展示我们的页面
        form = LoginForm(initial={'username': '用户名'})
        print(form)
        print('GET:', form.is_bound)
    return render(request, 'page_form_first.html', {
        'form': form
    })


def user_login(request):
    """ 用户登录 """
    # 第一次访问URL,GET展示表单,供用户输入
    # 第二次访问URL,POST
    if request.method == 'POST':
        form = UserLogin(request.POST)
        # 表单是否验证
        if form.is_valid():
            # 执行登录
            print('验证通过')
            pass
        else:
            print(form.errors)
    else:
        form = UserLogin()
    return render(request, 'user_login.html', {
        'form': form,
    })


def user_register(request):
    """ 用户注册 """
    form = UserRegister()
    return render(request, 'user_register.html', {
        'form': form,
    })

weiibo/templates/user_login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<form action="." method="post">
{#    {{ form.as_p }}#}
    <!-- 表单内容的展示 -->
    <div>
        {{ form.username.label }}:
        {{ form.username }}
        {{ form.username.errors.as_text }}
    </div>
    <div>
        {{ form.password.label }}:
        {{ form.password }}
        {{ form.password.errors }}
    </div>
    <div>
        {{ form.verify_code.label }}:
        {{ form.verify_code }}
        {{ form.verify_code.errors }}
    </div>
    <div><input type="submit" value="登录"></div>
    <div>
        {{ form.non_field_errors.as_text }}
    </div>
</form>
</body>
</html>

 

31、模型表单

P.S.11:https://docs.djangoproject.com/zh-hans/2.2/topics/forms/modelforms/#field-types

ORM字段            表单字段

CharField            CharField

DateField            DateField

ImageField            ImageField

IntegerField           IntegerField

FloatField            FloatField

BooleanField            BooleanField

ForeignKey            ModelChoiceField

ManyToManyField         ModelMultipleChoiceField

TextField             CharField

PositiveIntegerField         IntegerField

 

_Meta配置选项

_1.widgets——修改展现样式(文本输入、数字输入)

_2.help_texts——设置帮助文字

_3.labels——设置表单输入前的文字提示

_4.error_messages——设置表单的错误提示

 

weibo/forms.py

from weibo.models import WeiboUser


class UserForm(forms.ModelForm):
    """ 从模型创建表单 """
    class Meta:
        model = WeiboUser
        fields = ['username', 'password', 'nickname', 'remark']
        # 界面显示表单输入修改
        widgets = {
            'password': forms.PasswordInput(attrs={
                'class': 'text-error'
            }),
            'remark': forms.Textarea
        }
        labels = {
            'username': '手机号码'
        }
        error_messages = {
            'username': {
                'required': '请输入手机号码',
                'max_length': '最长不超过64位',
            }
        }

weibo/urls.py

# 从模型创建表单
    path('user/edit/', views.user_edit, name='user_edit'),

weibo/views.py

def user_edit(request):
    """从模型创建表单 """
    form = UserForm()

    return render(request, 'user_edit.html', {
        'form': form
    })

weibo/templates/user_edit.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>从模型创建表单,用户信息编辑</title>
    <style type="text/css">
        .text-error {
            color: #F32;
        }
    </style>
</head>
<body>
<h3>从ORM模型创建的表单</h3>
<form action="." method="post">
    {{ form.as_p }}
    <input type="submit" value="更新用户数据">
</form>
</body>
</html>

 

32、文件上传

_1、原始方法:

表单设置enctype="multipart/form-data"

在视图中通过request.FILES来获取文件对象

将文件保存到磁盘

def uploaded_file(f):
    """ 保存文件 """
    with open('media/file/name.jpg', 'wb+') as dest:
        for chunk in f.chunks():
            dest.write(chunk)

 

weibo/urls.py

# 文件上传
    path('file/upload/origin/', views.file_upload_origin, name='file_upload_origin')

weibo/views.py

def file_upload_origin(request):
    """ 原始的文件上传 """
    if request.method == 'POST':
        # 获取上传的文件对象,二进制
        file = request.FILES.get('avatar', None)
        filename = os.path.join(settings.MEDIA_ROOT, 'test.jpg')
        with open(filename, 'wb+') as f:
            for chunk in file.chunks():
                f.write(chunk)
        print('上传成功')
    return render(request, 'file_upload_origin.html')

weibo/templates/file_upload_origin.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>原始的文件上传</title>
</head>
<body>
<form action="." method="post" enctype="multipart/form-data">
    <input type="file" name="avatar">
    <input type="submit" value="开始上传">
</form>
</body>
</html>

 

_2、结合ORM模型方法:

weibo/urls.py

# ORM模型文件上传
    path('file/upload/form/', views.file_upload_forms, name='file_upload_forms')

weibo/forms.py

class AvatarUploadForm(forms.Form):
    """ 头像上传Form """
    remark = forms.CharField(label='备注', max_length=32)
    avatar = forms.ImageField(label='头像')

weibo/views.py

def file_upload_forms(request):
    """ ORM模型文件上传 """
    if request.method == 'POST':
        form = AvatarUploadForm(request.POST, request.FILES)
        if form.is_valid():
            # 保存备注
            # 保存图片
            file = request.FILES.get('avatar', None)
            filename = os.path.join(settings.MEDIA_ROOT, 'test.jpg')
            with open(filename, 'wb+') as f:
                for chunk in file.chunks():
                    f.write(chunk)
            print('上传成功')
    else:
        form = AvatarUploadForm()
    return render(request, 'file_upload_forms.html', {
        'form': form
    })

weibo/templates/file_upload_forms.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ORM模型文件上传</title>
</head>
<body>
<form action="." method="post" enctype="multipart/form-data">
    {{ form.as_p }}
    <input type="submit" value="上传">
</form>
</body>
</html>

 

_3、结合ORM模型上传

表单设置enctype="multipart/form-data"

在模型中使用models.ImageField

在表单中使用forms.ImageField

weibo/forms.py

class WeiboImageForm(forms.ModelForm):
    """ 微博图片上传 """
    content = forms.CharField(label='微博内容', max_length=256, widget=forms.Textarea(attrs={
        'placeholder': '请输入微博内容',
    }))

    class Meta:
        model = WeiboImage
        fields = ['image']

    # def clean_content(self):
    #     pass

    def save(self, user, commit=False):
        # 先不保存到数据库,先得到对象,接下来对这个对象进行修改,然后再保存
        obj = super().save(commit)
        data = self.cleaned_data
        content = data['content']
        # 1、创建微博的记录
        weibo = Weibo.objects.create(user=user, content=content)
        # 2、修改微博关联关系
        obj.weibo = weibo
        obj.save()
        return obj

weibo/urls.py

path('file/upload/weibo/', views.file_upload_weibo, name='file_upload_weibo'),

weibo/views.py

def file_upload_weibo(request):
    """ 发表带图片的微博 """
    # 因为没有登录,默认用户来进行微博的发布
    user = WeiboUser.objects.get(pk=1)

    if request.method == 'POST':
        form = WeiboImageForm(request.POST, request.FILES)
        if form.is_valid():
            obj = form.save(user,False)
            print('保存成功', obj.pk)
    else:
        form = WeiboImageForm()
    return render(request, 'file_upload_weibo.html', {
        'form': form
    })

weibo/templates/file_upload_weibo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>结合ORM模型上传——发布带图片的微博</title>
</head>
<body>
<h3>微博发布</h3>
<form action="." method="post" enctype="multipart/form-data">
    {{ form.as_p }}
    <input type="submit" value="发布微博">
</form>
</body>
</html>

 

33、用户登录及权限验证

_1、用户认证授权系统

_1_1、用户:记录用户的登录账号密码等信息

_1_2、权限:用户是否有权限访问某一资源(执行操作)

_1_3、组:对多个用户进行权限管理

_1_4、登录密码:加密后的密码存储

_1_5、自带后台管理

 

_2、安装及配置

_2_1、第一步:INSTALLED_APPS安装应用(django.contrib.auth、django.contrib.contenttypes)

_2_2、第二步:MIDDLEWARE中间件配置(SessionMiddleware、AuthenticationMiddleware)

_2_3、第三步:migrate同步模型到数据库

 

_3、用户模型

_3_1、User

_3_2、AnonymousUser

_3_3、常用属性:{username: 用户名, password: 登录密码,email: 电子邮箱, istaff: 是否为内部员工, is_active: 是否为激活用户, is_superuser: 是否为超级管理员, is_authenticated: 用户是否已登录的只读属性, is_anonymous: 用户登录是否已失效的只读属性, last_login: 最后登录的时间, date_joined: 注册时间, groups: 用户组多对多关系, user_permissions: 用户权限多对多关系}

 

_4、登录及退出

_4_1:用户登录(P.S.12:登录forms.py不能使用forms.ModelForm来创建,必须使用forms.Form,不然无法登录,报错提示:A user with that username already exists.)

_4_1_1:认证用户:

user = authenticate(username='Harold', password='123456')

_4_1_2:判断认证后的用户是否为None

用户不为None则表示用户认证通过

_4_1_3:调用login函数登录用户

login(request, user, backend=None)

_4_2:用户管理

_4_2_1:在视图中获取当前用户

request.user

_4_2_2:需要登录才可访问的视图

@login_required

def my_view(request):

_4_2_3:创建普通用户

from django.contrib.auth.models import User

user = User.objects.create_user('Harold', '[email protected]', 'Haroldpassword')

_4_2_4:使用命令行创建超级管理员

python manage.py createsuperuser

>Username:admin

>Email address:[email protected]

>Password:123456

>Password(again):123456

_4_3:用户退出

_4_3_1:使用logout函数退出当前登录的用户

form django.contrib.auth import logout

def logout_view(request):

  logount(request)

 

_5、权限验证

_5_1:权限管理

_5_1_1:判断用户是否具备某权限

request.user.has_perm('foo.add_bar')

_5_1_2:强制权限验证

@permission_required('polls.can_vote')

def my_view(request):

  pass

_5_2:密码管理

_5_2_1:设置、修改用户的密码

from django.contrib.auth.models import User

u = User.objects.get(username='harold')

u.set_password('new password')

u.save()

_5_2_2:检查用户的密码是否正确

u = User.objects.get(username='Harold')

u.check_password('my password')

_5_2_3:多种密码加密方式可供选择

 

_6、对用户进行扩展(Django内置的用户模型满足不了需求、要用户模型添加积分账户)

_6_1:方式一、使用OneToOneField对用户进行扩展

_6_2:方式二、替换现有的用户模型

_6_2_1:配置用户模型(setting.py)【使用自定义的用户模型】

AUTH_USER_MODEL = 'accounts.User'

_6_2_2:继承自AbstractUser抽象模型(accounts/models.py)

from django.contrib.auth.models import AbstractUser

class User(AbstractUser)

_6_2_3:添加字段,同步模型到数据库

python manage.py makemigrations

python manage.py migrate

_6_2_4:配置admin(accounts/admin.py)

from django.contrib import admin

from django.contrib.auth.admin import UserAdimin

from accounts.models import User

admin.site.register(User, UserAdmin)

 

34、将模型提添加到后台管理

_1、框架部分的配置(默认自动生成配置)

_1_1、INSTALLED_APPS配置应用及依赖

'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',

_1_2、模型选项配置TEMPLATES中的OPTIONS

_1_2_1、添加上下文处理器:context_processors

'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',

_1_3、添加中间件MIDDLEWARE

'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',

_1_4、配置URL

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

urlpatterns = [
    path('admin/', admin.site.urls),
]

_1_5、访问后台管理:http://127.0.0.1:8000/admin/

_1_6、设置后台中文显示和时区:

LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'

 

_2、ORM模型中的配置

_2_1、在模块中新建admin.py文件

_2_2、封装模型自定义管理后台功能和选项

_2_2_1、每个模型创建一个ModelAdmin的子类

_2_2_1_1、fields/exclude——需要/不需要编辑的字段列表

_2_2_1_2、form——自定义表单

_2_2_1_3、list_display——在列表中显示的字段

_2_2_1_4、ordering——指定排序规则

_2_3、INSTALLED_APPS配置应用

_2_3_1、需要管理ORM模型自动搜索添加至后台管理——INSTALLED_APPS

'mall.apps.MallConfig',     # 商品模块
'accounts.apps.AccountsConfig',     # 用户账户模块
'mine.apps.MineConfig',     # 个人中心模块
'system.apps.SystemConfig',     # 系统模块

from django.contrib import admin

# Register your models here.
from mall.models import Product


# 方式二:装饰器注册
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    """ 商品信息管理 """
    list_display = ('name', 'types', 'price', 'status', 'is_valid')

# 方式一:注册管理
# admin.site.register(Product, ProductAdmin)

 

35、使用admin的一些优化配置

_1、查询速度优化

_1_1、减少每页的数据大小

_1_2、减少SQL查询的次数

list_per_page——分页大小,默认每页100条

list_select_related——外键关联查询优化

paginator——分页器对象

_2、字段格式化字段优化

_3、快捷搜索支持:list_filter

_4、实现用户搜索:search_fields

accounts/admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from accounts.models import User


@admin.register(User)
class UserAdmin(UserAdmin):
    """ 用户管理 """
    list_display = ('format_username', 'nickname', 'integral', 'level')
    # 修改分页数据的大小
    list_per_page = 9
    # 按字段(用户名、暱称)搜索
    search_fields = ('username', 'nickname', )

    def format_username(self, obj):
        """ 用户名脱敏处理 """
        if obj.username.isdigit():
            return obj.username[0:3] + '****' + obj.username[-4:]
        return obj.username

    # 修改列名显示
    format_username.short_description = '用户名'

mall/admin.py

from django.contrib import admin

# Register your models here.
from mall.models import Product


# 方式二:装饰器注册
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    """ 商品信息管理 """
    list_display = ('name', 'types', 'price', 'status', 'is_valid')
    # 修改分页数据的大小
    list_per_page = 5
    # 快捷字段搜索
    list_filter = ('status', )
# 方式一:注册管理
# admin.site.register(Product, ProductAdmin)

accounts/apps.py

from django.apps import AppConfig


class AccountsConfig(AppConfig):
    name = 'accounts'
    verbose_name = '用户模块'

accounts/models.py

class User(AbstractUser):
    """ 用户的基础信息表 """
    # username = models.CharField('用户名', max_length=64)
    # password = models.CharField('密码', max_length=256)
    avatar = models.ImageField('用户头像', upload_to='avatar', null=True, blank=True)
    integral = models.IntegerField('用户的积分', default=0)
    nickname = models.CharField('暱称', max_length=32)
    level = models.SmallIntegerField('用户级别', default=0)

    class Meta:
        db_table = 'account_user'
        verbose_name = '用户基础信息'
        verbose_name_plural = '用户基础信息'

 

36、admin添加自定义方法

场景:批量禁用用户、批量下架商品

_1、(两种方式)定义操作函数

def action_name(modeladmin, request, queryset):
    pass

_2、命名界面显示的操作名称

action_name.short_description = '将选中的商品下架'

_3、配置ModelAdmin

class ProductAdmin(admin.ModelAdmin):
    ....
    actions = ['action_name']

方式一:

accounts/admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from accounts.models import User


@admin.register(User)
class UserAdmin(UserAdmin):
    """ 用户管理 """
    list_display = ('format_username', 'nickname', 'integral', 'level', 'is_active')
    # 修改分页数据的大小
    list_per_page = 9
    # 按字段(用户名、暱称)搜索
    search_fields = ('username', 'nickname', )
    # 添加自定义方法
    actions = ['disable_user', 'enable_user']

    def format_username(self, obj):
        """ 用户名脱敏处理 """
        if obj.username.isdigit():
            return obj.username[0:3] + '****' + obj.username[-4:]
        return obj.username

    # 修改列名显示
    format_username.short_description = '用户名'

    def disable_user(self, request, queryset):
        """ 批量禁用选中用户 """
        queryset.update(is_active=False)
    disable_user.short_description = '批量禁用用户'

    def enable_user(self, request, queryset):
        """ 批量启用用户 """
        queryset.update(is_active=True)
    enable_user.short_description = '批量启用用户'

方式二:

utils/admin_actions.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:djangoShop
# File:admin_actions.py
# Data:2021/1/7 22:21
# Author:LGSP_Harold
from django.contrib import messages


def set_invalid(modeladmin, request, queryset):
    """ 批量禁用 is_valid=False """
    queryset.update(is_valid=False)
    messages.success(request, '操作成功')


set_invalid.short_description = '禁用所选对象'


def set_valid(modeladmin, request, queryset):
    """ 批量启用 is_valid=True """
    queryset.update(is_valid=True)
    messages.success(request, '操作成功')


set_valid.short_description = '启用所选对象'

mall/admin.py

from django.contrib import admin

# Register your models here.
from mall.models import Product


# 方式二:装饰器注册
from utils.admin_actions import set_invalid, set_valid


@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    """ 商品信息管理 """
    list_display = ('name', 'types', 'price', 'status', 'is_valid')
    # 修改分页数据的大小
    list_per_page = 5
    # 快捷字段搜索
    list_filter = ('status', )

    actions = [set_invalid, set_valid]
    set_invalid.short_description = '禁用所选商品'
    set_valid.short_description = '启用所选商品'

# 方式一:注册管理
# admin.site.register(Product, ProductAdmin)

system/admin.py

from django.contrib import admin

from system.models import News, Slider
from utils.admin_actions import set_invalid, set_valid


@admin.register(News)
class NewsAdmin(admin.ModelAdmin):
    """ 新闻管理 """
    list_display = ['title', 'types', 'is_valid']
    actions = [set_valid, set_invalid]
    list_per_page = 18


@admin.register(Slider)
class SliderAdmin(admin.ModelAdmin):
    """ 轮播图管理 """
    list_display = ['name', 'types', 'start_time', 'end_time', 'is_valid']
    actions = [set_valid, set_invalid]

 

37、自定义模型配置

 场景:商品UID唯一标识,创建后不允许修改;商品销售价格保证输入的是正整数、商品的类型是否可以改为radio单选

 _1、【编辑页面不可见】在models.py下不允许修改的字段添加editable=False;【编辑页面不可见】在admin.py下设置exclude=['字段名']排除该字段;【编辑页面可见】在admin.py下设置readonly_fields=['字段名']让该字段只读。

_2、自定义后台模型表单验证

_3、自定义后台模型的界面显示

mall/forms.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:djangoShop
# File:forms.py
# Data:2021/1/8 20:12
# Author:LGSP_Harold
from django import forms

from mall.models import Product


class ProductAdminForm(forms.ModelForm):
    """ 商品编辑  """

    class Meta:
        model = Product
        exclude = ['created_at', 'updated_at']
        widgets = {
            # 修改表单的输入界面(下拉,radio)
            'types': forms.RadioSelect,
        }

    def clean_price(self):
        price = self.cleaned_data['price']
        if int(price) <= 0:
            raise forms.ValidationError('销售价格不能小于等于0')
        return price

mall/admin.py

from django.contrib import admin

# Register your models here.
from mall.forms import ProductAdminForm
from mall.models import Product


# 方式二:装饰器注册
from utils.admin_actions import set_invalid, set_valid


@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    """ 商品信息管理 """
    list_display = ('name', 'types', 'price', 'status', 'is_valid')
    # 修改分页数据的大小
    list_per_page = 5
    # 快捷字段搜索
    list_filter = ('status', )
    # 排除掉某些字段,使其不能编辑,在编辑页面不可见
    # exclude = ['remain_count']
    # 不可编辑,只读模式,在编辑页面可见
    readonly_fields = ['remain_count']

    actions = [set_invalid, set_valid]
    set_invalid.short_description = '禁用所选商品'
    set_valid.short_description = '启用所选商品'

    # 自定义表单
    form = ProductAdminForm

# 方式一:注册管理
# admin.site.register(Product, ProductAdmin)

 

38、Xadmin安装配置

P.S.14:Xadmin:https://xadmin.readthedocs.io/en/latest/index.html

P.S.15:https://github.com/sshwsfc/xadmin

安装:pip install git+git://github.com/sshwsfc/xadmin.git@django2

配置:

_1、INSTALLED_APPS配置应用及依赖

setting.py

INSTALLED_APPS = (
    ...
    'xadmin',
    'crispy_forms',
    'reversion',
    ...
)

_2、同步模型到数据库

python manage.py check

P.S.16:可能报ImportError: cannot import name 'SKIP_ADMIN_LOG'错误

ERROR
from
import_export.admin import DEFAULT_FORMATS, SKIP_ADMIN_LOG, TMP_STORAGE_CLASS ImportError: cannot import name 'SKIP_ADMIN_LOG'

请重新安装django-import-export,建议使用低版本,如django-import-export v1.2.0

python manage.py check运行无误后执行:python manage.py migrate即可。

_3、配置URL

import xadmin
xadmin.autodiscover()

# version模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()

urlpatterns = patterns('',
    path('xadmin/', xadmin.site.urls),
)

 

39、xadmin定制及优化

_1、添加adminx.py

_2、配置及注册

mall/adminx.py

import xadmin

from mall.models import Product


class ProductAdmin(object):
    """ 商品管理 """
    # 配置列表中默认显示的列
    list_display = ['name', 'type', 'price']
    # 快捷搜索
    list_filter = ['types', 'status']
    # 按关键字搜索
    search_fields = ['name']

# 注册到xadmin后台管理
xadmin.site.register(Product, ProductAdmin)

 

40、添加富文本插件django-ckeditor V6.0.0

安装:pip install django-ckeditor

setting.py

INSTALLED_APPS = [
    # 富文本编辑器
    'ckeditor'
    # 富文本编辑器上传管理
    'ckeditor_uploader'
]

# 富文本编辑器ckeditor配置
CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': 'full',  # 工具条功能
        'height': 600,  # 编辑器高度
        # 'width': 300,  # 编辑器宽
    },
}

CKEDITOR_UPLOAD_PATH = os.path.join(MEDIA_ROOT, 'uploads')

mall/models.py

from ckeditor.fields import RichTextField


class Product(models.Model):
    # content = models.TextField('商品描述')
    # 使用富文本编辑器
    content = RichTextField('商品描述')

 

添加上传(图片)功能(django项目中使用ckeditor富文本编辑器并实现图片上传功能)

setting.py

INSTALLED_APPS = [
    # 富文本编辑器
    'ckeditor'
    # 富文本编辑器上传管理
    'ckeditor_uploader'
]

# 富文本编辑器ckeditor配置
CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': 'full',  # 工具条功能
        'height': 600,  # 编辑器高度
        # 'width': 300,  # 编辑器宽
    },
}

CKEDITOR_UPLOAD_PATH = os.path.join(MEDIA_ROOT, 'uploads')
CKEDITOR_UPLOAD_SLUGIFY_FILENAME = False
CKEDITOR_RESTRICT_BY_USER = True
CKEDITOR_BROWSE_SHOW_DIRS = True
CKEDITOR_RESTRICT_BY_DATE = True

urls.py

urlpatterns = [
    # ckeditor上传
    path('ckeditor/', include('ckeditor_uploader.urls')),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

mall/models.py

from ckeditor_uploader.fields import RichTextUploadingField


class Product(models.Model):
    # content = models.TextField('商品描述')
# 使用富文本编辑器
# content = RichTextField('商品描述')
# 使用能上传文件的富文本编辑器
content = RichTextUploadingField('商品描述')

 

41、django内置命令行

创建django项目
django-admin startproject name

在项目中创建一个应用(app)
python manage.py startapp name

启动一个开发服务器
python manage.py runserver 0.0.0.0:8080

创建超级管理员
python manage.py createsuperuser

更改用户密码
python manage.py changepassword

检查djangoORM模型
python manage.py check

生成同步原语
python manage.py makemigrations

模型同步
python manage.py migrate

收集依赖中的静态文件
settings.py{
STATIC_URL = os.path.join(BASE_DIR, 'static')
}
python manage.py collectstatic

django控制台
python manage.py shell

清除过期会话
python manage.py clearsession

 

42、自定义django命令行工具

_1、创建指定的目录及文件结构

mine/
——management/
————__init__.py
————commands/
——————__init__.py
——————update.py

_2、实现自定义命令

实现django.core.management.base.BaseCommand的子类Command

添加命令参数add_arguments

P.S.17:https://docs.python.org/3/library/argparse.html

处理命令逻辑handle

显示处理过程(self.stdout.write/self.stderr.write)

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:djangoShop
# File:update.py
# Data:2021/1/18 16:42
# Author:LGSP_Harold
"""
更新订单状态
订单超过半小时不支付,取消订单,释放库存
"""
from django.core.management.base import BaseCommand


class Command(BaseCommand):
    help = """
        更新订单状态
        回收订单
    """

    def add_arguments(self, parser):
        """
        添加命令的参数
        argparse:https://docs.python.org/3/library/argparse.html
        1.回收所有超时未支付的订单
        python manage.py update --all
        2.指定回收某一个订单
        python manage.py update --one 200001
        :param parser:
        :return:
        """
        parser.add_argument(
            '--all',
            action='store_true',
            dest='all',
            default=False,
            help='回收所有超时未支付的订单',
        )

        parser.add_argument(
            '--one',
            action='store',
            dest='one',
            default=False,
            help='指定回收某一个订单',
        )

    def handle(self, *args, **options):
        if options['all']:
            self.stdout.write('开始回收订单')
            # 逻辑处理
            self.stdout.write('----')
            self.stdout.write('处理完成')
        elif options['one']:
            self.stdout.write('开始回收订单{}'.format(options['one']))
            # 逻辑处理
            self.stdout.write('----')
            self.stdout.write('处理完成')
        else:
            self.stderr.write('指令异常')

 

43、Django中间件的开发和使用之请求上下文

_1、上下文处理器context_processors

_1_1、渲染上下文Context

_1_2、请求上下文RequestContext

_1_3、连接views(视图)和templates(模板)

mine/context_processors.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:djangoShop
# File:context_processors.py
# Data:2021/1/18 18:08
# Author:LGSP_Harold
from django.db.models import Sum

from utils import constants


def shop_cart(request):
    """ 当前用户的购物车信息 """
    user = request.user
    cart_list = []
    cart_total = None
    cart_count = 0
    if user.is_authenticated:
        # 我的购物车商品列表
        cart_list = user.cart.filter(
            status=constants.ORDER_STATUS_INIT
        )
        cart_total = cart_list.aggregate(sum_amount=Sum('amount'), sum_count=Sum('count'))
        cart_count = cart_list.count()
    return {
        'cart_list': cart_list,
        'cart_count': cart_count,   # 购物车中的商品数量
        'cart_total': cart_total,
    }

settings.py

TEMPLATES = [
    {
        ....
        'OPTIONS': {
            'context_processors': [
                # 自定义请求上下文处理
                'mine.context_processors.shop_cart',
            ],
        },
    },
]

 

_2、中间件middleware(自定义中间件)

_2_1、实现中间件,处理业务逻辑

方式1:函数式自定义中间件

方式2:OOP(面向对象)自定义中间件

_2_2、激活中间件,添加到setting.py配置

_2_3、场景1:用户非法请求拦截(添加IP地址黑名单,在名单内的用户限制访问)

utils/middleware.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:djangoShop
# File:middleware.py
# Data:2021/1/18 21:07
# Author:LGSP_Harold
from django.http import HttpResponse


# 拦截黑名单IP
def ip_middleware(get_response):

    def middleware(request):
        # 请求到达前的业务逻辑
        print('请求到达前的业务逻辑')
        # 请求不满足业务规则,IP被限制
        ip = request.META.get('REMOTE_ADDR', None)
        ip_disable_list = [
            '127.0.0.1'
        ]
        print(ip)
        if ip in ip_disable_list:
            return HttpResponse('not allowed', status=403)
        response = get_response(request)
        # 在视图函数调用之后的业务逻辑
        print('在视图函数调用之后的业务逻辑')
        return response
    return middleware

settings.py

MIDDLEWARE = [
  ....
# 自定义中间件 'utils.middleware.ip_middleware', ]

 

_2_4、场景2:模拟用户登录(实现简单的用户登录中间件)

utils/middleware.py

#!/usr/bin/env python3
# coding=utf-8
# Version:python3.6.1
# Project:djangoShop
# File:middleware.py
# Data:2021/1/18 21:07
# Author:LGSP_Harold
from django.http import HttpResponse
from accounts.models import User



class MallAuthMiddleware(object):
    """ 自定义登录验证中间件 """
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request, *args, **kwargs):
        # 请求到达前的业务逻辑
        print('MallAuthMiddleware请求到达前的业务逻辑')

        user_id = request.session.get('user_id', None)
        if user_id:
            user = User.objects.get(pk=user_id)
        else:
            user = None

        request.my_user = user
        print('_____________', user)
        response = self.get_response(request)
        # 在视图函数调用之后的业务逻辑
        print('MallAuthMiddleware在视图函数调用之后的业务逻辑')
        return response

settings.py

MIDDLEWARE = [
  ....
    # 自定义中间件
    'utils.middleware.ip_middleware',
]

accounts/views.py

from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.shortcuts import render, redirect, get_object_or_404

# from django.contrib.auth.models import User as AuthUser

from accounts.forms import UserLoginForm, UserRegisterForm, UserAddressForm
from accounts.models import User, UserAddress
from utils import constants
from utils.verify import VerifyCode


def user_login(request):
    """ 用户登录 """
    # 如果登录是从其他页面跳转过来的,会带next参数,如果有next参数,登录完成后,需跳转到next所对应的地址,否则跳转到首页
    next_url = request.GET.get('next', 'index')
    # 第一次访问URL GET展示表单,供用户输入
    # 第二次访问URL POST
    if request.method == 'POST':
        form = UserLoginForm(request=request, data=request.POST)
        print(request.POST)
        client = VerifyCode(request)
        code = request.POST.get('verify_code', None)
        rest = client.validate_code(code)
        print('验证码验证结果:', rest)
        # 表单是否通过验证
        if form.is_valid():
            # 执行登录
            data = form.cleaned_data
            print(data)
            # # 使用自定义的方式实现登录
            # # 查询用户信息 [MD5]加密算法,不可逆的加密算法
            # user = User.objects.get(username=data['username'], password=data['password'])
            # # 将用户ID设置到session
            # request.session[constants.LOGIN_SESSION_ID] = user.id
            # # 登录后的跳转
            # return redirect('index')

            # 使用django-auth来实现登录
            user = authenticate(request, username=data['username'], password=data['password'])
            if user is not None:
                # login
                login(request, user)
                # 新增下面这行
                request.session['user_id'] = user.id
                # 登录后的跳转
                return redirect(next_url)
        else:
            print(form.errors)
    else:
        form = UserLoginForm(request=request)
    return render(request, 'login.html', {
        'form': form,
        'next_url': next_url
    })

views.py

def index(request):
    """ 首页 """
    print('request.my_user:', request.my_user)

 

P.S.18:上下文处理器vs中间件

相同点:都用来做全局处理

不同点:上下文处理器=》为模板添加全局变量;中间件=》过滤请求,拦截请求。

 

44、django扩展

_1、DRF(Django REST Framework)REST接口框架:www.django-rest-framework.org

_2、django-cms内容管理系统:https://github.com/django-cms/django-cms

_3、django-debug-toolbar调试工具

 _4、django-channels Websocket通信

....

使用步骤:

查:pypi、github、版本兼容情况

验:用户量、更新频次、文档、社区

装:pip安装,源码安装,whl安装

用:对照文档使用(官方文档=》官方社区=》其他渠道)

 

45、Django的安全及维护

_1、表单防重复提交

【同步请求】:

_1_1、使用CSRF保护

_1_1_1、在MIDDLEWARE设置中激活CSRF中间件(settings.py:django.middleware.csrf.CsrfViewMiddleware)

_1_1_2、<form>元素中使用csrf_token标签(POST)({% csrf_token %})

_1_1_3、选择合适的渲染方式(视图函数中,确保使用RequestContext来渲染或render()函数)

 【异步请求】:

P.S.19:https://docs.djangoproject.com/zh-hans/2.2/ref/csrf/#ajax

_1_2、Ajax请求使用CSRF保护

_1_2_1、方式一:从cookie获取scrftoken

settings.py

CSRF_USE_SESSIONS = False
CSRF_COOKIE_HTTPONLY = False

mall/templates/product_detail.html

<script>
....
     // 方式一:从cookie获取scrftoken
function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { let cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { let cookie = cookies[i].trim(); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } let csrftoken = getCookie('csrftoken'); // 添加到购物车 $('#id_add_cart').click(() => { $.post("{% url 'mine:cart_add' prod_obj.uid %}", { count: 1,
          csrfmiddlewaretoken:'{{ csrf_token }}'    /** 方式一,需将csrf_token一并提交到后台,否则被拦截403 */ }, (rest) => { }) }) </script>

_1_2_2、方式二:从DOM获取scrftoken(settings.py:CSRF_USE_SESSIONS=True)

settings.py

CSRF_USE_SESSIONS = True
CSRF_COOKIE_HTTPONLY = True

mall/templates/product_detail.html

{% csrf_token %}
<script>
....
    // 方式二:从DOM获取scrftoken
         var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();

        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }

        $.ajaxSetup({
            beforeSend: function (xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });

        // 添加到购物车
        $('#id_add_cart').click(() => {
            $.post("{% url 'mine:cart_add' prod_obj.uid %}", {
                count: 1
            }, (rest) => {
            })
        })
</script>

 

_2、bug邮件通知(发送文字邮件【send_mail()】、发送HTML邮件、发送带附件邮件、发送多个邮件【send_mass_mail()】、打破连接限制,连接复用)

P.S.20:https://docs.djangoproject.com/zh-hans/2.2/topics/email/

_2_1、邮件配置

_2_2、准备邮件内容

_2_3、发送邮件

settings.py


# 发送给管理员,需关闭DEBUG
DEBUG = False
ALLOWED_HOSTS = ['*']


# 邮件发送配置 EMAIL_HOST
= 'smtp.qq.com' EMAIL_HOST_USER = '发件人邮箱' EMAIL_HOST_PASSWORD = '发件人邮箱密码' # send_mail('邮件主题', '邮件内容', '发件人邮箱', ['收件人邮箱'])

# 配置发送给管理员
SERVER_EMAIL = '发件邮箱'
ADMINS = [('admin', '管理员接收邮箱')

views.py

....

def index(request):
    """ 首页 """
    raise Exception

....

 

_3、日志记录配置

_3_1、Loggers日志记录入口_3_1_1、Logger的级别

_3_1_1_1、DEBUG:用于调试目的的底层系统信息

_3_1_1_2、INFO:普通的系统信息

_3_1_1_3、WARNING:警告,较小的问题,不影响执行顺序

_3_1_1_4、ERROR:错误,较大的问题

_3_1_1_5、CRITICAL:严重,致命的问题_3_2、Handlers决定处理logger中消息的方式

_3_2_1、记录SQL日志,分文件存储、控制台打印

_3_2_2、错误日志发送到邮件

_3_3、对日志进行条件控制_3_4、Formatters日志记录的文本顺序(格式)

 

 

 

P.S.4: python_django_views文件中return render("XXX.html") 显示 Template file not found:

问题描述:
在使用django框架进行web开发的时候,views文件中return到模板文件中的某个html文件,但是显示的是template file not found 。
而我在settings中的TEMPLATES的DIRS已经加入了template 路径。
原因分析: pycharm没有将你新建的templates文件夹当作存放模板文件的目录,也就是说render的第二个参数的寻找范围不包含templates
解决方案: 将templates文件夹设置称为解析器默认的模板文件目录即可 1.鼠标右键你的模板文件夹 2.选择Mark Directory as(我的pycharm是倒数第三个) 3.选择Template Floder 选择Template Floder后我的Templates颜色变成了紫色

 

 

P.S.1:官方文档:https://docs.djangoproject.com/zh-hans/2.2/

P.S.2:若教程出错,请自行GG或官方文档,不要一昧的相信教程....

P.S.10:django.debug.toolbar:https://django-debug-toolbar.readthedocs.io/en/latest/index.html

P.S.13:从模型创建表单:https://docs.djangoproject.com/zh-hans/2.2/topics/forms/modelforms/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章