使用 Django,只要很少的代碼,Python 的程序開發人員就可以輕松地完成一個正式網站所需要的大部分內容,並進一步開發出全功能的 Web 服務 Django 本身基於 MVC 模型,即 Model(模型)+ View(視圖)+ Controller(控制器)設計模式,MVC 模式使後續對程序的修改和擴展簡化,並且使程序某一部分的重復利用成為可能。
MVC 優勢:
MVC 以一種插件式的、松耦合的方式連接在一起
MTV 模式本質上和 MVC 是一樣的,也是為了各組件間保持松耦合關系,只是定義上有些許不同
復制系統python,不同項目使用不同的虛擬環境
創建虛擬環境
mkvirtualenv -p /usr/bin/python3 djangoApp
查看虛擬環境
workon
切換虛擬環境
workon djangoApp
移除虛擬環境
rmvirtualenv djangoApp
創建虛擬環境
mkvirtualenv -p /usr/bin/python3 djangoApp
安裝對應的庫
pip install django -i https://pypi.douban.com/simple
創建工作目錄
mkdir djangoProject # 創建存放Django的目錄
django-admin startproject CRM # 使用Django創建一個Django目錄
設置端口映射(22、3306、8000)
編寫代碼
cd CRM # 進入到工作目錄內部
python manage.py runserver 0:8000 # 開啟服務,在主機訪問使用127.0.0.1:1236
本地創建一個空項目
連接遠程解釋器(注意:一定要是項目的解釋器)
文件映射(注意:設置成默認)
設置自動同步文件
django-admin startproject CRM
則,創建項目後,其樹形結構為:
.
├── CRM
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── db.sqlite3
└── manage.py
在CRM文件夾裡面,創建一個views.py
在文件裡面添加:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "views.py"
__time__ = "2022/6/30 13:59"
__email__ = "[email protected]"
from django.http import HttpResponse
def index(request):
return HttpResponse("hello world")
在CRM.urls.py
裡面添加代碼
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path("index/", views.index), # 將index注冊到路由中
]
在CRM.settings.py
中,修改ALOWED_HOSTS
變量,這變量一般都要修改
ALLOWED_HOSTS = ["*"] # 允許所有的ip訪問這個網址
LANGUAGE_CODE = 'zh_hans' # 設置語言為中文簡體
TIME_ZONE = 'Asia/Shanghai' # 設置時區為上海時區
開啟服務,在浏覽器輸入127.0.0.1:1236/index/
既可以訪問剛才寫的網站
創建app
在終端中輸入,來創建app,一個項目可以擁有很多的app
python manage.py startapp student
touch student/urls.py
注冊app
在settings.py
文件中,在INSTALLED_APPS
中添加app
:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'student' # 注冊app
]
添加分路由
在student.urls.py
中添加
from django.urls import path
app_name = "student" # 配置app的名字為student
urlpatterns = [
# 裡面添加分路由
]
配置總路由
在CRM.urls.py
中添加
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path("student/", include('student.urls')), # 分發路由
]
url
是全球資源統一定位符,格式:
協議://域名(ip地址:端口)/路徑/?參數
schema://host[:port]/path/[?query-string][#anchor]
schema
:指定使用的協議(例如:http, https, ftp)
host
:Http服務器的IP地址或者域名
port
:端口號,http默認是80端口
path
:訪問資源的路徑
query-string
:發送給http服務器的數據
anchor
:錨點#
路徑表達式對視圖函數的映射
語法規則:
path(route, view, kwargs=None, name=None)
參數:
route
:字符串,url規則view
:視圖函數kwargs
:額外參數,此參數將傳遞給模板文件name
:url的別名注意:
- url規則要和設置一致才能正確訪問
- url規則指向是哪個視圖,訪問哪個視圖
<參數名>:捕獲到的值默認是字符串
使用路徑轉換器:
str # 匹配除了路徑分隔符(/)之外的非空字符串,這是默認的形式
int # 匹配正整數,包含0。
slug # 匹配字母、數字以及橫槓、下劃線組成的字符串。
uuid # 匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path # 匹配任何非空字符串,包含了路徑分隔符
如,視圖函數為:
def index(request, name):
return HttpResponse(f"hello {name}")
路由配置為:
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path("index/<str:name>/", views.index)
]
使用127.0.0.1:1236/index/lihua/
訪問
語法:
re_path(route, view, kwargs=None, name=None)
如,我們要接收一個參數,其為整數,並且大於0小於20,且命名為age
re_path("index/(?P<age>[0-9] | 1[0-9])", views.index)
當我們還有其他應用時,需要包含其他的路由配置
from django.urls import path, include
path("app1/", include("app1.urls"))
包含app1裡面的路由配置,其映射app1裡面的路由配置,即app1裡面的urls.py
path("index/", views.index, name="index"), # 這個name參數就是重定向的地址
重定向時,避免因為路徑的修改而導致需要修改所有引用了該路徑的地方
在對應app.urls.py
中,添加對應app_name = appName
,避免照成url命名的沖突
app_name = "student"
urlpatterns = [
path('admin/', admin.site.urls),
path("index/", views.index, name="index")
]
當我們使用重定向時:
def test(request):
return redirect("student:index") # 定向到student這個app裡面的index路由
在視圖函數中,添加一個test路由
from django.http import HttpResponse
from django.shortcuts import redirect, reverse
def test(request):
# 使用reverse可以解析出name對應的路徑,當在app中時,需要
url = reverse("index")
print(url)
return redirect("index") # 即為path裡面的name參數
# return redirect("/index") # 這樣也可以,其為硬編碼,如果路徑改變,就要修改,同時如果路徑過長,我們就需要使用name這個軟編碼
def index(request):
return HttpResponse(f"hello")
在路由配置中
from django.contrib import admin
from django.urls import path, re_path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path("index/", views.index, name="index"), # 這個name參數就是重定向的地址
path("test/", views.test)
]
這裡我們使用應用來演示
有兩種方法,第一種方法是自定義一個公共的templates
集中存放,第二種方法是放在app
目錄下
在項目的根目錄創建一個punlicTemplates
的文件夾
在settings.py
中,找到TEMPLATES
變量
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'publicTemplates'), ], # BASE_DIR 即為項目的路徑,這裡為導入模板文件
'APP_DIRS': True, # 這個為True的話,其會在app目錄下查找模板文件
'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',
],
},
},
]
在publicTemplates
中,根據應用名稱,來創建不同的文件夾,來區分,避免不同的app出現相同的模板文件
在視圖函數中渲染
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
# from django.template.loader import get_template
# html = get_template("student/index.html").render()
# return HttpResponse(html)
# 相當於
return render(request, "student/index.html")
目錄結構為:
publicTemplates -> student -> index.html
在student
目錄下創建一個templates
文件夾,同時這個文件夾的名字最好是固定的
直接在視圖函數中渲染
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, "index.html")
先查找項目根目錄,如果項目的公共目錄沒找到,同時APP_DIRS=True
,則會查找項目對應app下的目錄
一般選擇集中存放,每個app下面的模板文件,使用不同的文件夾分隔
如果分開存放,其模板文件的復用性更強
創建一個函數,將一些信息通過後台傳遞給前端頁面,我們使用context上下文管理來進行傳遞
在views.py
中,添加:同時注冊路由
def test(request):
from datetime import datetime
now = datetime.now()
lis = [1, 2, 3]
dic = {"name": "李華", "age": 23}
def func():
return "你好,我是函數!"
return render(
request,
"student/get_time.html", {
"now": now,
"lis": lis,
"dic": dic,
"fun": func
})
在模板文件中,創建test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>測試模板編程案例</title>
</head>
<body>
<p>現在的時間為:{{ now }}</p>
<p>傳入的列表為:{{ lis }}</p>
<p>我是列表的第一個值:{{ lis.0 }}</p>
<p>我是一個字典:{{ dic }}</p>
<p>我是一個字典裡面的一個值:{{ dic.name }}</p>
{% for i, j in dic.items %}
<p>{{ i }}: {{ j }}</p>
{% endfor %}
<p>函數調用結果為:{{ func }}</p>
</body>
</html>
作用: 對變量進行過濾。在真正渲染出來之前,過濾器會根據功能處理好變量,然後得出結果後再替換掉原來的變量展示出來。
管道符號進行鏈式調用,比如實現一個功能,先把所有字符變成小寫,把第一個字符轉換成大寫
語法:
{{fruits|lower|capfirst}}
使用參數:過濾器可以使用參數,在過濾器名稱後面使用冒號”:”再加上參數,比如要把一個字符串中所有的空格去掉,則可以使用cut過濾器
{{fruits|cut:" "}}
注意:使用參數的時候,冒號和參數之間不能有任何空格,一定要緊挨著。
時間過濾器格式
使用示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>測試模板編程案例</title>
</head>
<body>
<p>現在的時間是:{{ now|date:"Y年m月d日 H:i:s" }}</p>
<p>獲取兩個元素:{{ lis|slice:":2" }}</p>
<p>列表第一個元素相加的結果為:{{ lis.0|add:2 }}</p>
<p>列表第一個元素不能相加的結果為:{{ lis.0|add:"a"|default:"error" }}</p>
<p>列表的第一個元素:{{ lis|first }}</p>
<p>js腳本為:{{ js }}</p> <!--xss跨域腳本攻擊,其中js ='<script>alert("你好呀,我是xss攻擊")</script>',django默認會對代碼進行轉義 -->
<p>js腳本為:{{ js|safe }}</p> <!--如果代碼是安全的,則可以取消轉義-->
</body>
</html>
和模板文件類似,有兩種方法,第一種方法是自定義一個公共的static
集中存放,第二種方法是放在app
目錄下分別存放
這裡就使用公共的靜態文件來演示
創建步驟:
創建一個static
文件夾
注冊靜態文件,在settings.py
中
STATIC_URL = '/static/' # 用於拼接靜態文件的存儲路徑
# 創建一個列表,存放路徑
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
同時以app為名,去創建不同的靜態文件
在模板文件中導入static
文件
{% load static %} <!--導入靜態文件,一定要有這句話-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>學生管理系統</title>
<link rel="stylesheet" href="{% static 'student/index.css' %}">
<!-- <link rel="stylesheet" href="/static/student/index.css"> 這樣導入也可以,這屬於硬連接,不推薦,無法避免靜態文件前綴的修改-->
</head>
<body>
<p>歡迎來到學生管理系統</p>
</body>
</html>
如果要使用第一種方式導入,則要使用
{% load static %}
作用:可以在模板中進行各種邏輯操作,比如,循環、判斷等
使用模板標簽的語法:
{% load static %} <!--加載模板標簽-->
{% tag %} {% endTag %}
常用模板標簽:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>學生管理系統</title>
<link rel="stylesheet" href="{% static 'student/index.css' %}"> <!--導入我們的樣式表-->
</head>
<body>
<p>歡迎來到學生管理系統</p>
<table>
<thead>
<tr><th>序號</th><th>姓名</th><th>年齡</th></tr>
</thead>
<tbody>
{# 傳入的數據為:{"data": [{"name": "李華", "age": 12, "sex": "男"}, {"name": "Lucy", "age": 23, "sex": "女"}]}#}
{% for foo in data %} <!--循環拿到傳入前端頁面的數據-->
<tr {% if foo.age <= 13 %} {% endif %}>
{# 進行條件判斷,如果年齡小於13歲,則字體顏色改為紅色 #}
<td>
<!--路由為:path("detail/<str:name>", views.detail, name="detail"),-->
<a href="{% url 'student:detail' foo.name%}">
<!--foo.name是要傳入路由的參數-->
{{ forloop.counter }} <!--forloop為一個迭代對象,可以進行相應的操作-->
</a>
</td>
<td>{{ foo.name }}</td>
<td>{{ foo.age }}</td>
<td>{{ foo.sex }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
用來解決代碼冗余的問題
引用語法:{% include 'student/ad.html' %}
繼承語法:{% extends 'student/base.html' %}
{% extends 'student/base.html' %} <!--繼承base.html這個文件-->
語法:
{% block bolckname %}
<title>登錄界面<title>
{% endblock %}
比如,我們創建一個基類模板文件base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% block title %}
這裡面的內容不會展示在子類文件中
{% endblock %}
</head>
<body>
<div>頭部內容</div>
<div>
{% block content %}
{% endblock %}
</div>
<div>尾部內容</div>
</body>
</html>
我們繼承這個文件:
{% extends "student/base.html" %} <!--繼承父類的模板-->
{% block title %} <!--在代碼塊中添加內容-->
<title>繼承模板</title>
{% endblock %}
{% block content %}
{% include "student/login.html" %} <!--引入已經寫好的html頁面-->
{% endblock %}
自定義模板標簽和過濾器,官方文檔地址:https://docs.djangoproject.com/zh-hans/4.0/howto/custom-template-tags/
templatetags
的文件夾,創建一個__init__.py
,使得其變為一個包大概結構:
自定義的 tags 和 filters 會保存在模塊名為
templatetags
的目錄內。模塊文件的名字即稍候你用來加載 tags 的名字,所以小心不要采用一個可能與其它應用自定義的 tags 和 filters 沖突的名字。添加
templatetags
模塊後,你需要重啟服務器,這樣才能在模板中使用 tags 和 filters
原來我們的過濾器的使用方法:
{{ 模板變量|過濾器[:字符串參數] }}
<!--
過濾器是一個python函數
第一個參數:模板變量
第二個參數:可能有可能沒有,即字符串參數
-->
如,一般來說,我們使用0和1來表示男性和女性存儲在數據庫中,我們可以定義一個過濾器來將這個0和1轉換成漢字
定義函數
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "customer_filters.py" # 這個文件的文件名可以隨便,自定義
__time__ = "2022/7/31 14:05"
# 定義函數
def to_sex(value, arg):
"""
男是1,女是0
:param value: 模板變量
:param arg: 字符串參數
:return: 處理後的結果
"""
change = {
"zh": ("女", "男"),
"en": ("girl", "boy")
}
return change[arg][value] # 根據語言選擇返回對應的值
注冊函數
from django import template
register = template.Library() # register 其為固定值,一個字母都不能錯
register.filter(to_sex) # 第一個參數是這個過濾器的名字,第二個參數是函數名字,我們使用第一個參數作為調用過濾器的名字,如果函數名和過濾器的名字相同,可以不用傳入name參數
使用裝飾器
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "customer_filters.py" # 這個文件的文件名可以隨便,自定義
__time__ = "2022/7/31 14:05"
from django import template
register = template.Library() # register 其為固定值,一個字母都不能錯
# 定義函數,且過濾器的名字和函數名一樣
@register.filter()
def to_sex(value, arg='zh'):
"""
男是1,女是0
:param value: 模板變量
:param arg: 字符串參數,注意,這個參數可以有默認參數
:return: 處理後的結果
"""
change = {
"zh": ("女", "男"),
"en": ("girl", "boy")
}
return change[arg][value] # 根據語言選擇返回對應的值
調用過濾器
{% load customer_filters %} <!--將模塊導入-->
<!--加載頁面-->
{# 傳入的數據為:{"data": [{"name": "李華", "age": 12, "sex": "1"}, {"name": "Lucy", "age": 23, "sex": "0"}]}#}
{% for foo in data %} <!--循環拿到傳入前端頁面的數據-->
<p>{{ foo.name }}</p>
<p>{{ foo.age }}</p>
<p>{{ foo.sex | to_sex:'zh'}}</p>
{% endfor %}
模板標簽可以做任何事情
我們定義一個獲取當前格式化時間的標簽
定義函數
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "customer_tags.py" # 文件名可以自定義
__time__ = "2022/7/31 14:05"
from datetime import datetime
def current_time():
return datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")
注冊標簽
from django import template
register = template.Library()
register.simple_tag(current_time, name="current_time") # name參數和過濾器的name參數作用一樣,使用方法也是一樣的
結合裝飾器
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "customer_tags.py" # 文件名可以自定義
__time__ = "2022/7/31 14:05"
from datetime import datetime
from django import template
register = template.Library()
@register.simple_tag() # 注意,如果這個要自定義name的話,要指定 name='youName'
def current_time():
return datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")
使用標簽
{% load customer_tags %} <!--將標簽導入-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
當前時間為:{% current_time %} <!--直接使用標簽,如果該標簽要傳入參數的話,我們加個空格,然後傳入需要的參數就可以了-->
</body>
</html>
需要傳入參數的一個標簽:
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "customer_tags.py" # 文件名可以自定義
__time__ = "2022/7/31 14:05"
from datetime import datetime
from django import template
register = template.Library()
@register.simple_tag()
def current_time(format):
return datetime.now().strftime(format)
# {% current_time format %} format: "%Y年%m月%d日 %H:%M:%S"其是可以從視圖函數中傳入的
這個過程實現了:視圖函數 -》模板文件 -》標簽函數
那麼,我們是否可以直接將參數:視圖函數-》標簽函數 呢?
答案是可以的,這個方法是最通用的,實現方法:
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "customer_tags.py" # 文件名可以自定義
__time__ = "2022/7/31 14:05"
from datetime import datetime
from django import template
register = template.Library()
@register.simple_tag(takes_context=True) # 使得標簽函數可以從上下文管理中獲取變量
def current_time(context): # 第一個參數必須是context
return datetime.now().strftime(context["format"]) # 像字典一樣獲取值
有了上面的基礎,我們直接使用裝飾器來創建包含標簽
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "customer_tags.py" # 文件名可以自定義
__time__ = "2022/7/31 14:05"
from datetime import datetime
from django import template
register = template.Library()
@register.inclusion_tag(filename="student/show_list.html") # takes_context=True 也可以使用
def show_list(value, style): # 定義一個函數,接收模板變量
return {"lis": value, "style": style} # lis是傳遞給包含模板的參數
我們的包含模板的文件為,show_list.html
{#用於將列表數據展示為有序列表的模板文件,這裡面可以使用if判斷我們需要加載的樣式#}
{% if style == "ul" %}
<ul>
{% for foo in lis %}
<li>{{ foo }}</li>
{% endfor %}
</ul>
{% elif style == "ol" %}
<ol>
{% for foo in lis %}
<li>{{ foo }}</li>
{% endfor %}
</ol>
{% endif %}
在主文件中寫入主結構:
{% load customer_tags %} <!--將標簽導入-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
{% show_list data style %}
</body>
</html>
這個過程實現了:視圖函數 -》模板文件 -》標簽函數
那麼,我們可以直接將參數:視圖函數-》標簽函數
實現方法:
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "customer_tags.py" # 文件名可以自定義
__time__ = "2022/7/31 14:05"
from datetime import datetime
from django import template
register = template.Library()
@register.inclusion_tag(filename="student/show_list.html", takes_context=True)
def show_list(context): # 定義一個函數,接收模板變量
return {"lis": context["data"], "style": context["style"]}
我們調用的話:
{% load customer_tags %} <!--將標簽導入-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
{% show_list %}
</body>
</html>
自定義標簽特點:
模型層和SQLAchemy的操作類似:SQLAchemy語法
ORM:
環境配置:
安裝pymysql:pip install pymysql -i https://pypi.douban.com/simple
配置數據庫
-- 創建項目數據庫
CREATE DATABASE CRM charset=utf8; -- 注意charset要使用小寫
-- 創建一個管理員用戶crm賬號,密碼為 crm:
CREATE USER ‘crm'@'%'IDENTIFIED BY ‘crm';
-- 給這個用戶授予所有遠程訪問,這個用戶主要用於管理整個數據庫,備份,還原等操作,CRM這個數據庫
GRANT ALL ON CRM.* TO ‘crm'@'%';
-- 也可以一步完成
GRANT ALL PRIVILEGES ON CRM.* to 'crm'@'%' IDENTIFIED BY 'crm';
-- 使授權立即生效:
FLUSH PRIVILEGES;
修改配置文件
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
"NAME": "CRM", # 數據庫名字
"USER": "crm", # 數據庫登錄用戶
"PASSWORD": "crm", # 數據庫登錄木馬
"HOST": '127.0.0.1', # 指定訪問的主機
"PORT": '3306', # 指定訪問的端口
}
}
設置連接器
修改主目錄下的__init__.py文件,設置連接器為pymysql,在文件中添加下面代碼:
import pymysql
pymysql.install_as_MySQLdb() # 將pymysql設置為我們的數據庫連接器
在app下的models.py文件中創建類,來創建模型
from django.db import models
# Create your models here.
class Student(models.Model): # Student模型是models.Model的子類
name = models.CharField(max_length=20) # 創建一個name字段,其為char 20
age = models.SmallIntegerField() # 創建一個短整型的age字段
sex = models.SmallIntegerField(default=1) # 設置sex的默認值為1,短整型字段
qq = models.CharField(max_length=20) # 存儲qq號
c_time = models.DateTimeField(verbose_name="創建時間", auto_now_add=True) # 日期時間字段,同時自動添加當前時間,verbose_name參數的作用是創建具體描述
注意,一定要先注冊app
生成遷移文件:
python manage.py makemigrations # 創建全部App的遷移
python manage.py makemigrations student # 創建student App的遷移
# 作用:告訴django我對模型做了一些修改,希望把這些修改存儲起來
python manage.py sqlmigrate student 0001 # 查看遷移原生的SQL語句
python manage.py sqlmigrate 遷移序號 # 查看遷移sql語句
python manage.py migrate student 0001 # 回滾回指定遷移
python manage.py migrate # 讓全部app遷移生效
python manage.py migrate student # 讓 student app遷移生效
運行完成後,我們來看一下我們的數據庫:
遷移的步驟:
作用:實時升級數據庫,而不會丟失數據
首先,配置環境:
/* 打開python交互模式
第一種方法:
在目標環境下安裝 */
pip install ipython
python manage.py shell
/*方法二
使用python console
*/
每個django模型類都有一個默認的管理器,objects
# 第一種
s1 = Student(name='Dou', age=15, qq='123456') # 實例化
s1.save() # 將實例對象保存到數據庫
# 第二種
s2 = Student()
s2.name='Tea'
s2.age = 12
s2.save()
# 第三種
Student.objects.create(name='Guo') # 但是這個方法會一直創建name
# 第四種,如果有,進行get查詢,返回的元組的第二個元素就是False,如果沒有,進行create新增
In [12]: Student.objects.get_or_create(name="Hai")
Out[13]: (<Student: Student object (4)>, True)
In [14]: Student.objects.get_or_create(name="Hai")
Out[15]: (<Student: Student object (4)>, False)
In [16]: Student.objects.get(name='Hai')
Out[17]: <Student: Student object (4)>
s1 = Student.objects.get(pk=4)
Out[19]: <Student: Hai>
s1.delete()
Out[20]: (1, {'teacher.Student': 1})
Student.objects.filter(age=20).delete()
Out[21]: (2, {'teacher.Student': 2})
Student.objects.all().delete() # 全部刪除
Out[22]: (1, {'teacher.Student': 1})
# 1.改單條,通過屬性修改
s1 = Student.objects.get(pk=1) # pk即為主鍵名,獲取主鍵名為1的數據
s1.age=18
s1.save()
# 2.改多條,通過update
Student.objects.filter(age=18).update(age=20)
Out[17]: 2 # 修改數據的條數
1.查所有
res = Student.objects.all()
print(res.query) # 查看sql語句
2.查一條
Student.objects.get(pk=1) # pk即為主鍵名,獲取主鍵名為1的數據
3.帶條件查詢
Student.objects.filter(sex=1)
Out[8]: <QuerySet [<Student: Dou>, <Student: Tea>, <Student: Guo>, <Student: Hai>]>
總結:
1.get拿到是一個對象,集合對象
2.all,filter拿到的是查詢集,querySet[]
QuerySet集合對象:
- 可以用list轉成列表
- 可以迭代
- 可以切片(不支持負索引)
常用查詢方法:
1.查詢所有all
2.查詢單條get
3.條件查詢filter與排除exclude
4.獲取第一條first或最後一條last
5.指定字段查詢
values:提升查詢效率;僅能拿到指定字段的值,其他字段值無法獲取
+ User.objects.values("name", "age") # 相當於 SELECT `name`, `age` FROM `student`
only:提升查詢效率;能拿到指定字段的值,其他字段值也能獲取,主鍵必帶
defer:排除指定字段,進行查詢,主鍵必帶,作用與only相反
6.排序
res = Student.objects.order_by('age')
res = Student.objects.order_by('-age')
7.切片
不支持負索引,數據量大時不要使用步長
8.多條件查詢
and查詢
res = Student.objects.filter(sex=1, age=18)
or查詢
from django.db.models import Q
res = Student.objects.filter(Q(sex=1), Q(age=18) | Q(age=20)) values方法還可以指定字段查詢
常用的查詢條件:
官方文檔: https://docs.djangoproject.com/zh-hans/4.0/ref/models/fields/
常見字段:
1. IntegerField : 整型,映射到數據庫中的int類型。
2. CharField: 字符類型,映射到數據庫中的varchar類型,通過max_length指定最大長度。
3. TextField: 文本類型,映射到數據庫中的text類型。
4. BooleanField: 布爾類型,映射到數據庫中的tinyint類型,在使用的時候,傳遞True/False進去。如果要可以為空,則用NullBooleanField。
5. DateField: 日期類型,沒有時間。映射到數據庫中是date類型,在使用的時候,可以設置DateField.auto_now每次保存對象時,自動設置該字段為當前時間(修改時間)。設置DateField.auto_now_add當對象第一次被創建時自動設置當前時間。
6. DateTimeField: 日期時間類型。映射到數據庫中的是datetime類型,在使用的時候,傳遞datetime.datetime()進去。
其余的請到官方文章查閱
字段實例化常用參數:
primary_key: 指定是否為主鍵。
unique: 指定是否唯一。
null: 指定是否為空,默認為False。
blank: 等於True時form表單驗證時可以為空,默認為False。
default: 設置默認值。
DateField.auto_now: 每次修改都會將當前時間更新進去,只有調用,QuerySet.update方法將不會調用。這個參數只是Date和DateTime以及TimModel.save()方法才會調用e類才有的。
DateField.auto_now_add: 第一次添加進去,都會將當前時間設置進去。以後修改,不會修改這個值
一對一存放的字段可以是雙向的
from django.db import models
# Create your models here.
class Student(models.Model): # Student模型是models.Model的子類
name = models.CharField(verbose_name="學生姓名", max_length=20) # 創建一個name字段,其為char 20
age = models.SmallIntegerField(verbose_name="學生年齡", null=True) # 創建一個短整型的age字段
sex = models.SmallIntegerField(verbose_name="學生性別", default=1) # 設置sex的默認值為1,短整型字段
qq = models.CharField(verbose_name="學生qq", max_length=20, null=True) # 存儲qq號
phone = models.CharField(verbose_name="學生手機號", max_length=12, null=True) # 存儲學生手機號
c_time = models.DateTimeField(verbose_name="創建時間", auto_now_add=True) # 日期時間字段
# 這個表裡面還有一個id字段,是自動生成的,作為主鍵
detail = models.OneToOneField("StudentDetail",
on_delete=models.SET_NULL, null=True)
# 進行一對一映射,一名學生對應一個詳情頁數據;同時,如果這個詳情表刪除的話,就把這個字段置空,故要設置這個字段可以為空
class Meta:
db_table = "student" # 設置表的名字
def __str__(self):
# 設置print輸出的內容
return self.name
class StudentDetail(models.Model):
address = models.CharField(verbose_name="學生地址", max_length=20, null=True) # 學生信息
# student = models.OneToOneField("Student", on_delete=models.CASCADE) # 一對一是雙向的
# 這裡如果學生刪了,地址也沒必要存在了,設置為級聯刪除
# 這個表裡面還有一個id字段,是自動生成的,作為主鍵
class Meta:
db_table = "detail"
def __str__(self):
return self.address
一對多的表,最好將字段放在多的表中,這樣更好查詢
# Create your models here.
class Student(models.Model): # Student模型是models.Model的子類
name = models.CharField(verbose_name="學生姓名", max_length=20) # 創建一個name字段,其為char 20
age = models.SmallIntegerField(verbose_name="學生年齡", null=True) # 創建一個短整型的age字段
sex = models.SmallIntegerField(verbose_name="學生性別", default=1) # 設置sex的默認值為1,短整型字段
qq = models.CharField(verbose_name="學生qq", max_length=20, null=True) # 存儲qq號
phone = models.CharField(verbose_name="學生手機號", max_length=12, null=True) # 存儲學生手機號
c_time = models.DateTimeField(verbose_name="創建時間", auto_now_add=True) # 日期時間字段
# 這個表裡面還有一個id字段,是自動生成的,作為主鍵
detail = models.OneToOneField("StudentDetail",
on_delete=models.SET_NULL, null=True)
# 進行一對一映射,一名學生對應一個詳情頁數據;同時,如果這個詳情表刪除的話,就把這個字段置空,故要設置這個字段可以為空
grade = models.ForeignKey("Grade", on_delete=models.SET_NULL, null=True) # 我們使用外鍵約束來完成一對多,不進行級聯刪除,班級去除後,把這個字段置空
class Meta:
db_table = "student" # 設置表的名字
def __str__(self):
# 設置print輸出的內容
return self.name
class Grade(models.Model):
name = models.CharField(verbose_name="班級名稱", max_length=20) # 班級信息,這裡只設置班級的名稱
# 這個表裡面還有一個id字段,是自動生成的,作為主鍵
class Meta:
db_table = "grade" # 設置表名稱
def __str__(self):
return self.name
多對多也是雙向的,其會自動生成student和course的中間表
from django.db import models
# Create your models here.
class Student(models.Model): # Student模型是models.Model的子類
name = models.CharField(verbose_name="學生姓名", max_length=20) # 創建一個name字段,其為char 20
age = models.SmallIntegerField(verbose_name="學生年齡", null=True) # 創建一個短整型的age字段
sex = models.SmallIntegerField(verbose_name="學生性別", default=1) # 設置sex的默認值為1,短整型字段
qq = models.CharField(verbose_name="學生qq", max_length=20, null=True) # 存儲qq號
phone = models.CharField(verbose_name="學生手機號", max_length=12, null=True) # 存儲學生手機號
c_time = models.DateTimeField(verbose_name="創建時間", auto_now_add=True) # 日期時間字段
# 這個表裡面還有一個id字段,是自動生成的,作為主鍵
detail = models.OneToOneField("StudentDetail",
on_delete=models.SET_NULL, null=True)
# 進行一對一映射,一名學生對應一個詳情頁數據;同時,如果這個詳情表刪除的話,就把這個字段置空,故要設置這個字段可以為空
grade = models.ForeignKey("Grade", on_delete=models.SET_NULL, null=True) # 不進行級聯刪除,班級去除後,把這個字段置空
class Meta:
db_table = "student" # 設置表的名字
def __str__(self):
# 設置print輸出的內容
return self.name
class Enroll(models.Model):
"""多對多的中間表"""
student = models.ForeignKey("Student", on_delete=models.CASCADE)
course = models.ForeignKey("Course", on_delete=models.CASCADE)
c_time = models.DateTimeField(verbose_name="報名時間", auto_now_add=True)
class Meta:
db_table = "enroll"
class Course(models.Model):
name = models.CharField(verbose_name="課程名稱", max_length=20) # 設置學生對應的課程名稱,一個學生有多個課程,同時一個課程有多個學生
# 多對多映射,其也是雙向的
students = models.ManyToManyField("Student", through='Enroll') # 關聯Student這張表,同時指定中間表
class Meta:
db_table = "course"
def __str__(self):
return self.name
如果我們沒有指定中間表,Django會自動幫助我們創建一個中間表
一個模型如果有一個關聯字段(外鍵字段),通過這個模型對外鍵關聯的模型進行操作叫做正向。
g1 = Grade(name="數據庫")
g2 = Grade(name="框架")
# 增、改:無則增,有則改
# 1.通過屬性賦值的方式
s1 = Student(name='Arrogant', age=18, sex=0)
s1.grade = g1
s1.save()
# 2.通過主鍵的方式
s2 = Student(name='ljy', age=19)
s2.grade_id = g2.id
s2.save()
# 刪
s2.grade = None
s2.save()
# 查
s1.grade
s1.grade.name
一個模型如果被另一個模型關聯,通過這個模型對關聯它的模型進行操作就叫反向。
實際:如果一個模型(eg:Student
)有關聯字段(eg:ForeignKey
),那麼這個外鍵模型(grade
)的實例(eg:g1
)將可以訪問一個管理器(eg:管理Student所有實例的管理器)。默認情況下,這個管理器名為有關聯字段的模型小寫名_set(eg:student_set
)。
"""
In [27]: g[1].student_set
Out[27]: <django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager at 0x7faccb88feb8> # 關於多對一的方向管理器
"""
# 增
# 1.通過grade表創建student表數據
new_student = g2.student_set.create(name='哈哈', age=16)
# 2.增加多條數據
s3, s4, s5, s6 = Student.objects.filter(id__lte=8)
g1.student_set.add(s3,s4,s5,s6)
# 刪
# 1. 從相關對象中移除指定的模型對象
g1.student_set.remove(s1)
g1.student_set.remove(s2,s4)
# 2. 從相關對象中刪除所有的對象
g1.student_set.clear()
# 改,有則改,無則增
g1.student_set.set([s1, s2, s3, s4, s5])
s6, s7 = Student.objects.filter(grade_id = 2)
g1.student_set.set([s6, s7]) # 班裡面只有兩個學生
# 查
# 1. 查詢所有
g1.student_set.all()
# 2. 條件查詢
g1.student_set.filter(age=16)
# 自定義反向關系管理器student_set----->student grade = models.ForeignKey('Grade', on_delete=models.SET_NULL, null=True, related_name='student') g1 = Grade.objects.get(pk=1) g1.student # 這個即為我們自定義的方向關系管理器,默認為 student_set
總結:
正向:模型通過關聯字段的屬性名操作關聯模型
反向:被關聯模型使用關聯模型的小寫模型名_set去操作
# 使用中間表來設置兩個對象的關系
s1 = Student.objects.get(pk=6)
c1 = Course.objects.get(pk=1)
e = Enroll()
e.student = s1
e.course = c1
e.save()
# 正向查詢
c1.students.all() # 關聯字段屬性名
# 反向查詢
s1.course_set.all() # 使用的是源模型的小寫模型名_set
# 通過關聯字段中設置related_name修改反向管理器名
# 正向:通過關聯字段的屬性名
d2 = StudentDetail(address='湖南')
s2 = Student.objects.get(pk=7)
s2.detail = d2
s2.detail
# 反向:管理器對象不是一個對象的集合,而是一個單一的對象,名字叫源模型的小寫模型名,注意所有字母都是小寫哦,不是根據小駝峰命名法來的
d1 = StudentDetail(address='江西')
d1.student = s1
d1.save()
d1.student
跨模型的相關字段的字段名,以雙下劃線隔開,直到你想要達到的字段為止。
正向是相關字段的字段名;反向是模型的小寫名。
# 多對多(學生和課程)
Course.objects.filter(students__sex=0)
Student.objects.filter(course__name__contains='python')
# 一對多
Student.objects.filter(grade__name = 'django框架').distinct() # distinct() 方法有去重的作用
# 一對一
StudentDetail.objects.values('address', 'student__name', 'student__age')
Student.objects.values('name', 'age', 'studentdetail__address')