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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章