MTV 設計模式中,模型(M)就是對數據庫的操做,在 Web 開發中,使用最頻繁的也是對數據庫的操做,那麼該怎麼樣去實現呢?javascript
咱們不可能本身手動去寫大量的 SQL 語句,由於咱們也不是專業 DBA 人員,那麼咱們就只能奢求有一種可以使用 Python 程序對數據庫操做的方法了。這就是 ORM(Object Relation Mapping)對象關係映射,以面向對象的方式去操做數據庫。html
其實現模式大體是這樣的:java
Django 自己給咱們提供了強大的 ORM 系統,不須要再額外的
安裝,固然還有一些其餘的 ORM ,如:SQLAlch 等。python
Model 中的字段 fileds
即數據表中的列,用來存儲數據(記錄),字段類型對應列的數據類型。mysql
Django 內置了不少字段類型,都位於 django.db.models
中,如下爲經常使用字段類型:jquery
AutoField(Field) - int自增列,必須填入參數 primary_key=True BigAutoField(AutoField) - bigint自增列,必須填入參數 primary_key=True 注:當model中若是沒有自增列,則自動會建立一個列名爲id的列 from django.db import models class UserInfo(models.Model): # 自動建立一個列名爲id的且爲自增的整數列 username = models.CharField(max_length=32) class Group(models.Model): # 自定義自增列 nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) SmallIntegerField(IntegerField): - 小整數 -32768 ~ 32767 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正小整數 0 ~ 32767 IntegerField(Field) - 整數列(有符號的) -2147483648 ~ 2147483647 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正整數 0 ~ 2147483647 BigIntegerField(IntegerField): - 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807 BooleanField(Field) - 布爾值類型 NullBooleanField(Field): - 能夠爲空的布爾值 CharField(Field) - 字符類型 - 必須提供max_length參數, max_length表示字符長度 TextField(Field) - 文本類型 EmailField(CharField): - 字符串類型,Django Admin以及ModelForm中提供驗證機制 IPAddressField(Field) - 字符串類型,Django Admin以及ModelForm中提供驗證 IPV4 機制 GenericIPAddressField(Field) - 字符串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6 - 參數: protocol,用於指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 若是指定爲True,則輸入::ffff:192.0.2.1時候,可解析爲192.0.2.1,開啓刺功能,須要protocol="both" URLField(CharField) - 字符串類型,Django Admin以及ModelForm中提供驗證 URL SlugField(CharField) - 字符串類型,Django Admin以及ModelForm中提供驗證支持 字母、數字、下劃線、鏈接符(減號) CommaSeparatedIntegerField(CharField) - 字符串類型,格式必須爲逗號分割的數字 UUIDField(Field) - 字符串類型,Django Admin以及ModelForm中提供對UUID格式的驗證 FilePathField(Field) - 字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能 - 參數: path, 文件夾路徑 match=None, 正則匹配 recursive=False, 遞歸下面的文件夾 allow_files=True, 容許文件 allow_folders=False, 容許文件夾 FileField(Field) - 字符串,路徑保存在數據庫,文件上傳到指定目錄 - 參數: upload_to = "" 上傳文件的保存路徑 storage = None 存儲組件,默認django.core.files.storage.FileSystemStorage ImageField(FileField) - 字符串,路徑保存在數據庫,文件上傳到指定目錄 - 參數: upload_to = "" 上傳文件的保存路徑 storage = None 存儲組件,默認django.core.files.storage.FileSystemStorage width_field=None, 上傳圖片的高度保存的數據庫字段名(字符串) height_field=None 上傳圖片的寬度保存的數據庫字段名(字符串) DateTimeField(DateField) - 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field) - 日期格式 YYYY-MM-DD - auto_now_add=True 當在首次建立對象時自動將字段設置爲如今。用於建立時間戳 - auto_add=True 每次保存對象時,自動將字段設置爲如今。用於「最後修改」的時間戳 TimeField(DateTimeCheckMixin, Field) - 時間格式 HH:MM[:ss[.uuuuuu]] DurationField(Field) - 長整數,時間間隔,數據庫中按照bigint存儲,ORM中獲取的值爲datetime.timedelta類型 FloatField(Field) - 浮點型,精確的數不能用 FloatField DecimalField(Field) - 10進制小數,對於比較精確的數能夠用 DecimalField - 參數: max_digits,小數總長度 decimal_places,小數位長度 BinaryField(Field) - 二進制類型
null 數據庫中字段是否能夠爲空 db_column 數據庫中字段的列名 default 數據庫中字段的默認值 primary_key 數據庫中字段是否爲主鍵 db_index 數據庫中字段是否能夠創建索引 unique 數據庫中字段是否能夠創建惟一索引 unique_for_date 數據庫中字段【日期】部分是否能夠創建惟一索引 unique_for_month 數據庫中字段【月】部分是否能夠創建惟一索引 unique_for_year 數據庫中字段【年】部分是否能夠創建惟一索引 verbose_name Admin中顯示的字段名稱 blank Admin中是否容許用戶輸入爲空 editable Admin中是否能夠編輯 help_text Admin中該字段的提示信息 choices Admin中顯示選擇框的內容,用不變更的數據放在內存中從而避免跨表操做 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) error_messages 自定義錯誤信息(字典類型),從而定製想要顯示的錯誤信息; 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date 如:{'null': "不能爲空.", 'invalid': '格式錯誤'} validators 自定義錯誤驗證(列表類型),從而定製想要的驗證規則 from django.core.validators import RegexValidator from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\ MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator 如: test = models.CharField( max_length=32, error_messages={ 'c1': '優先錯信息1', 'c2': '優先錯信息2', 'c3': '優先錯信息3', }, validators=[ RegexValidator(regex='root_\d+', message='錯誤了', code='c1'), RegexValidator(regex='root_112233\d+', message='又錯誤了', code='c2'), EmailValidator(message='又錯誤了', code='c3'), ] )
1. null 用來保存空值,默認值 false。對於保存字符串的字段,儘可能避免將其設置爲 true,不然可能會致使出現是 null 或空字符串 2. blank 表示字段能夠爲空,默認 false。經常使用語表單輸入驗證是否爲空,與數據庫無關。 3. choices 頁面上選擇框標籤,二維元組格式。兩個參數,第一個爲存儲在數據庫中,第二個顯示在頁面上內容。 數據庫存儲的是0 、1 、2,而頁面上顯示的是籃球、足球、羽毛球 hobby_choices = [(0, '籃球'), (1, '足球'), (2, '羽毛球'),] hobby = models.IntegerField( choices=hobby_choices ) 4. primary_key 主鍵,若是沒有指定主鍵,那麼 Django會自動建立一個 AutoField的自增字段,名爲 id,並將其設置爲主鍵。 primary_key 至關於 null=False和unique=True,即惟一且不能爲空,你也能夠本身將其餘字段設置爲空,如有必要。
示例git
from django.db import models class UserInfo(models.Model): username = models.CharField( null=True, db_column='user', max_length=32, db_index=True, verbose_name='用戶名', help_text='幫助信息', default='', ) hobby_choices = [(0, '籃球'), (1, '足球'), (2, '羽毛球'),] hobby = models.IntegerField( choices=hobby_choices ) def __str__(self): return self.username
模型中的元類是指除了字段外的其餘非必須內容,如修改數據表的名字,設置代理等。ajax
使用方式sql
class User(models.Model): ... class Meta: verbose_name = '用戶'
1. abstract 爲 true 時,模型會被認爲是一個抽象類,長用來做爲其餘模型的父類被繼承。 2. app_label 若是沒有在 settings 中註冊 app,那麼必須在元類中聲名是屬於哪一個 app app_label = 'polls' 3. base_manager_name 自定義模型的 _base_manager 管理器的名字,模型管理器是 Django 爲模型提供的 API 4. db_table 指定模型生成數據表時,數據表的表名 db_table = 'user' 5. db_tablespace 自定義數據庫表空間的名字。默認值是工程的DEFAULT_TABLESPACE設置 6. default_manager_name 自定義模型的_default_manager管理器的名字 7. default_related_name 反向查詢時,默認咱們使用的 `<model_name>_set` 也就是源模型名字+下劃線+set 方法查詢,當咱們定義了 default_related_name時,就可使用它來反向查詢。 8. ordering 指定該模型生成的全部對象的排序方式,接收一個字段名組成的元組或列表。默認按升序排列,若是在字段名前加上字符「-」則表示按降序排列,若是使用字符問號「?」表示隨機排列。 ordering = ['pub_date'] # 表示按'pub_date'字段進行升序排列 ordering = ['-pub_date'] # 表示按'pub_date'字段進行降序排列 ordering = ['-pub_date', 'author'] # 表示先按'pub_date'字段進行降序排列,再按`author`字段進行升序排列。 9. permissions 該元數據用於當建立對象時增長額外的權限。它接收一個全部元素都是二元元組的列表或元組,每一個元素都是(權限代碼, 直觀的權限名稱)的格式。好比下面的例子: permissions = (("can_deliver_pizzas", "能夠送披薩"),) 10. default_permissions Django默認給全部的模型設置('add', 'change', 'delete')的權限,也就是增刪改。你能夠自定義這個選項,好比設置爲一個空列表,表示你不須要默認的權限,可是這一操做必須在執行migrate命令以前 11. proxy 若爲 true,表示使用代理模式的模型繼承方式。 12. required_db_vendor 聲明模型支持的數據庫。Django默認支持sqlite, postgresql, mysql, oracle 13. indexes 接收一個應用在當前模型上的索引列表 from django.db import models class Customer(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) class Meta: indexes = [ models.Index(fields=['last_name', 'first_name']), models.Index(fields=['first_name'], name='first_name_idx'), ] 14. unique_together 等同於數據庫的聯合約束,沒法應用多對多字段。 好比一張用戶表,保存用戶姓名、出生日期、地址等,如今有兩個叫張偉的人,那麼就可使用聯合惟一。 # 表示 name、birth_day、address 聯合惟一,即不能在同一地方、同一時刻出生且都叫張偉 unique_together = (('name', 'birth_day', 'address'),) 14. verbose_name 給 Admin 提供人性化的名稱,支持中文,如: verbose_name = '用戶' # 那麼 Admin 中顯示的就是 用戶 15. label 只讀元數據,不可修改,至關於 polls.Question
一對多模型,如:員工部門表,一個部門能夠有多個員工。那麼 Django 怎麼創建這種關係呢?數據庫
其實就是經過外鍵 ForeignKey
進行關聯,在多的一方,字段指定外鍵便可。
ForeignKey 字段參數
ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要進行關聯的表名 to_field=None, # 要關聯的表中的字段名稱 on_delete=None, # 當刪除關聯表中的數據時,當前表與其關聯的行的行爲 - models.CASCADE,刪除關聯數據,與之關聯也刪除 - models.DO_NOTHING,刪除關聯數據,引起錯誤IntegrityError - models.PROTECT,刪除關聯數據,引起錯誤ProtectedError - models.SET_NULL,刪除關聯數據,與之關聯的值設置爲null(前提FK字段須要設置爲可空) - models.SET_DEFAULT,刪除關聯數據,與之關聯的值設置爲默認值(前提FK字段須要設置默認值) - models.SET,刪除關聯數據, a. 與之關聯的值設置爲指定值,設置:models.SET(值) b. 與之關聯的值設置爲可執行對象的返回值,設置:models.SET(可執行對象) def func(): return 10 class MyModel(models.Model): user = models.ForeignKey( to="User", # 關聯 User 表 to_field="id" # 關聯 User 表 的 id 字段 on_delete=models.SET(func),) related_name=None, # 反向操做時,使用的字段名,用於代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操做時,使用的鏈接前綴,用於替換【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中顯示關聯數據時,提供的條件: # 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') db_constraint=True # 是否在數據庫中建立外鍵約束 parent_link=False # 在Admin中是否顯示關聯數據
示例:
class Department(models.Model): """部門表""" name = models.CharField(max_length=32) create_data = models.DateField(auto_now_add=True) # 建立時間 id_delete = models.BooleanField(default=False) # 是否可刪除 class Meta: db_table = 'department' class Employee(models.Model): """員工表""" name = models.CharField(max_length=32) age = models.IntegerField() gender = models.IntegerField(default=0) salary = models.DecimalField(max_digits=8, decimal_places=2) # max_digits 表示八個數字,包括兩位小數,decimal_places 表示保留兩位小數 # null=True 表示能夠爲空, blank=True 表示 Django admin 後臺能夠輸入空 comment = models.CharField(max_length=300, null=True, blank=True) hire_data = models.DateField(auto_now_add=True) department = models.ForeignKey('Department') # 外鍵字段 class Meta: db_table = 'employee' # 數據表名字
Tips
在設置外鍵時,須要給子表(有外鍵的表)指定當主表(被鏈接的表)刪除數據時,從表該如何處理。Django 經過設置 on_delete
屬性來控制,它有三種值:
DO_NOTHING/PROTECT
CASCADE
SET_NULL/SET_DEFAULT
,set_null
僅在字段能夠是 null 時才能使用OneToOneField(ForeignKey) to, # 要進行關聯的表名 to_field=None # 要關聯的表中的字段名稱 on_delete=None, # 當刪除關聯表中的數據時,當前表與其關聯的行的行爲
###### 對於一對一 ###### # 1. 一對一其實就是 一對多 + 惟一索引 # 2.當兩個類之間有繼承關係時,默認會建立一個一對一字段 # 以下會在A表中額外增長一個c_ptr_id列且惟一: class C(models.Model): nid = models.AutoField(primary_key=True) part = models.CharField(max_length=12) class A(C): id = models.AutoField(primary_key=True) code = models.CharField(max_length=1) # 使用 OneToOneField 字段建立一對一模型 class Person(models.Model): name = models.CharField(max_length=32) o2o = models.OneToOneField(to='Person_detail', to_field='id', on_delete=models.CASCADE) class Person_detal(models.Model): age = models.IntegerField() gender_choices = [(0, '男'), (1, '女')] gender = models.IntegerField( choices=gender_choices, default=0, ) height = models.PositiveIntegerField() # 正整數 email = models.EmailField(max_length=64)
多對多其實就是兩個一對多,經過兩個外鍵將三張表相連,其中第三張表存儲了前面兩張表的對應關係。例如:名字和愛好表,一我的能夠有多個愛好,一個愛好也能夠是多我的有。
如何建立三張表:
ManyToMangField
字段自動建立,不能新增列Meta
,能夠新增列Meta
+ ManyToMangField
多對多 ManyToManyField
字段參數:
ManyToManyField(RelatedField) to, # 要進行關聯的表名 related_name=None, # 反向操做時,使用的字段名,用於代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操做時,使用的鏈接前綴,用於替換【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中顯示關聯數據時,提供的條件: # 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') symmetrical=None, # 僅用於多對多自關聯時,symmetrical用於指定內部是否建立反向操做的字段 # 作以下操做時,不一樣的symmetrical會有不一樣的可選字段 models.BB.objects.filter(...) # 可選字段有:code, id, m1 class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=True) # 可選字段有: bb, code, id, m1 class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=False) through=None, # 自定義第三張表時,使用字段用於指定關係表 through_fields=None, # 自定義第三張表時,使用字段用於指定關係表中那些字段作多對多關係表 from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', through_fields=('group', 'person'), ) class Membership(models.Model): group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64) db_constraint=True, # 是否在數據庫中建立外鍵約束 db_table=None, # 默認建立第三張表時,數據庫中表的名稱
自動建立
class UserInfo(models.Model): username = models.CharField(max_length=32, verbose_name='用戶名') m = models.ManyToManyField( to = 'Tag', related_name = 'bb', ) class Tag(models.Model): title = models.CharField(max_length=32)
手動建立
class UserInfo(models.Model): username = models.CharField(max_length=32, verbose_name='用戶名') class Tag(models.Model): title = models.CharField(max_length=32) # 手動建立第三張表 class UserToTag(models.Model): tid = models.AutoField(primary_key=True) u = models.ForeignKey('UserInfo', on_delete=models.CASCADE) # 經過外鍵相連 t = models.ForeignKey('Tag', on_delete=models.CASCADE) # 聯合惟一索引 class Meta: unique_together = [ ('u', 't'), ]
手動加自動
class UserInfo(models.Model): username = models.CharField(max_length=32, verbose_name='用戶名') m = models.ManyToManyField( through = 'UserToTag', # 指定第三張表,不自動建立 through_fields = ['u', 't'] # 指定第三張表的字段 ) class Tag(models.Model): title = models.CharField(max_length=32) # 手動建立第三張表 class UserToTag(models.Model): tid = models.AutoField(primary_key=True) u = models.ForeignKey('UserInfo', on_delete=models.CASCADE) # 經過外鍵相連 t = models.ForeignKey('Tag', on_delete=models.CASCADE) # 聯合惟一索引 class Meta: unique_together = [ ('u', 't'), ]
自關聯模型,即表中的某列關聯表中另外一列。最典型的自關聯模型就是地區表(省市縣三級聯動)省的 pid 爲 null,市的 pid 爲省的 id,縣的 pid 爲市的 id。具體以下:
自關聯表現形式
parent_id
爲 nullparent_id
爲對應省的 id
parent_id
爲對應市的 id
models.py
class Area(models.Model): name = models.CharField(max_length=32, verbose_name='名稱') parent = models.ForeignKey('self', # 自關聯字段的外鍵指向自身,也能夠是 Area verbose_name='上級行政區劃', on_delete=models.SET_NULL, related_name='subs', null=True, blank=True ) class Meta: db_table = 'tb_areas' # 自定義表名 verbose_name = '行政區劃' # admin 中顯示的名稱 verbose_name_plural = '行政區劃' def __str__(self): return self.name
自關聯模型,最核心的地方就是本身關聯本身 self/Area
,用一張表作兩張表才能作的事。
app/urls.py
from django.urls import path from app import views urlpatterns = [ path('area/', views.area, name='area'), path('getPro/', views.getPro, name='getArea'), path('getCity/', views.getCity, name='getCity'), path('getDis/', views.getDis, name='getDis'), ]
views.py
from django.shortcuts import render, HttpResponse from app import models from django.http import JsonResponse # 訪問 http://127.0.0.1:8000/app/area/,返回 area.html def area(request): return render(request, 'area.html') def getPro(request): """獲取省份信息""" pro_list = models.Area.objects.filter(parent_id=None) # 省份的 parent_id 爲 None res = [] for i in pro_list: res.append([i.id, i.name]) print(res) # [[1, '廣東省'], [7, '湖南省']] return JsonResponse({'pro_list': res}) # JsonResponse 打包成 json 格式字符串 def getCity(request): """獲取市信息""" city_id = request.GET.get('city_id') city_list = models.Area.objects.filter(parent_id=int(city_id)) res = [] for i in city_list: res.append([i.id, i.name]) print('city', res) # [[2, '深圳市'], [3, '廣州市'], [6, '湛江市']] return JsonResponse({'city_list': res}) def getDis(request): """獲取區信息""" dis_id = request.GET.get('dis_id') dis_list = models.Area.objects.filter(parent_id=int(dis_id)) res = [] for i in dis_list: res.append([i.id, i.name]) return JsonResponse({'dis_list': res})
area.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <select id="pro"> <option value="">請選擇省份</option> </select> <select id="city"> <option value="">請選擇城市</option> </select> <select id="dis"> <option value="">請選擇區</option> </select> <script src="{% static 'jquery-3.1.1.js' %}"></script> <script> $(function () { var pro = $('#pro'); var city = $('#city'); var dis = $('#dis'); // 查詢省信息,$.get() 爲 $.ajax 的簡化版 // 向 /app/getPro/ 發送 ajax 請求,arg 爲請求成功返回的信息 $.get('/app/getPro/', function (arg) { console.log(arg); // {'pro_list': [[1, '廣東省'],[7, '湖南省']]} $.each(arg.pro_list, function (index, item) { console.log(item); // [1, '廣東省'] 、[7, '湖南省'] console.log(index); // 0、1 // 將從數據庫中取出的數據添加到 option 標籤中,其中 value 爲 id,文本值爲 name var $new = $("<option value="+ item[0] +">" + item[1] + "</option>"); pro.append($new); }); {# 或者使用 JavaScript for 循環#} {# console.log(arg.pro_list);#} {# for (var i = 0, len=arg.pro_list.length; i<len; i++){#} {# var $new = $('<option value='+ arg.pro_list[i][0] +'>' + arg.pro_list[i][1] + '</option>')#} {# pro.append($new);#} {# }#} }); // 根據省的變化查詢市,改變省時,清空市和區 pro.change(function () { city.empty().append('<option value="">請選擇市</option>'); dis.empty().append('<option value="">請選擇市</option>'); $.ajax({ url: '/app/getCity/', type: 'get', data: {'city_id': $(this).val()}, success: function (arg) { $.each(arg.city_list, function (index, item) { var $new = $('<option value='+ item[0]+'>' + item[1] + '</value>'); city.append($new); }) } }) }); // 根據市變化查詢區,改變市,清空區 city.change(function () { dis.empty().append('<option value="">請選擇市</option>'); $.ajax({ url: '/app/getDis/', type: 'get', data: {'dis_id': $(this).val()}, success: function (arg) { console.log('區', arg.dis_list); // [[4, '寶安區'], ] $.each(arg.dis_list, function (index, item) { console.log(item[1]); var $new = $('<option value='+ item[0] +'>' + item[1] + '</value>'); dis.append($new); }) } }) }) }) </script> </body> </html>
當訪問 http://127.0.0.1:8000/app/area/
時,會向 /app/getPro/
發送 ajax 請求,請求成功得到包含省份 id、name 的信息 {'pro_list': [[1, '廣東省'],[7, '湖南省']]}
。取出數據遍歷循環,添加到 option
標籤中,從而得到省份信息。
當咱們改變省份時,將上一次省份的市、區信息清空,避免添加劇復。獲取當前省份的 id,並向 /app/getDis/
發送 ajax 請求,從而得到相應市的信息,同理區也是同樣。
views.py
添加數據,能夠用 pycharm 自帶的數據庫添加,也能夠手動添加:
def add(request): models.Area.objects.create(id=7, name='湖南省', parent_id=None) models.Area.objects.create(id=8, name='長沙市', parent_id=7) models.Area.objects.create(id=9, name='雨花區', parent_id=8) return HttpResponse('添加成功')
參考博客:
Ajax 的 get() 方法
,它能夠向後臺發送 ajax 請求,請求成功可調用回調函數。若是須要在出錯時執行函數,請使用 $.ajax
,它是 $.ajax
簡化版本。
語法
$(selector).get(url, data, success(response, status, xhr), dataType); // 除了 url 必需,其餘均可選,status 爲請求狀態,xhr - 包含 XMLHttpRequest 對象
$('button').click(function(){ get('/app/getdata/', function(arg){}); });
總共四張表,分別是班級、學生、學生我的信息以及老師表,之間的關係以下:
class Class(models.Model): """班級表""" class_name = models.CharField(max_length=32) create_data = models.DateField() def __str__(self): return self.class_name class Meta: verbose_name = '班級' class Student(models.Model): """ 學生表 一每一個學生都有對應的我的信息(一對一),一個班級能夠有多個學生(一對多) """ student_name = models.CharField(max_length=32) # 一對多,與班級關聯 sc = models.ForeignKey(to='Class', to_field='id', related_name='stu', on_delete=models.CASCADE) # 一對一,與學生我的信息關聯 detail = models.OneToOneField('StudentDetail', to_field='id', on_delete=models.CASCADE) class Meta: verbose_name = '學生' def __str__(self): return self.student_name class StudentDetail(models.Model): """學生我的信息表""" age = models.IntegerField() gender_choices = [(0, '男'), (1, '女')] gender = models.IntegerField( choices=gender_choices, default=0, ) height = models.PositiveIntegerField() # 正整數 email = models.EmailField(max_length=64) class Teacher(models.Model): """ 老師表 一個老師能夠教多個班,一個班也能夠有多個老師 """ teacher_name = models.CharField(max_length=32) tc = models.ManyToManyField(to='Class', related_name='b') class Meta: verbose_name = '老師' def __str__(self): return self.teacher_name
app_teacher
:app_student_detail
:app_student
:app_class
:app_teacher_tc
:def query(request): ########### 一對一 ##################### # 正向查詢(根據外鍵字段) # 根據學生名字查詢其我的信息 # obj = models.Student.objects.filter(student_name='rose')[0] # print(obj) # print(obj.detail.age, obj.detail.gender, obj.detail.height, obj.detail.email) # # 17 1 170 456@qq.com # 反向查詢(根據要查詢的數據表名) # 根據郵箱查詢學生名字 # obj2 = models.StudentDetail.objects.filter(email='456@qq.com')[0] # print(obj2) # <QuerySet [<StudentDetail: StudentDetail object (2)>]> # # print(obj2.student.student_name) # rose ############## 一對多(班級表和學生表) ################## # 正向查詢 # 根據學生名查詢所屬班級 # obj3 = models.Student.objects.get(student_name='rose') # print(obj3.sc_id) # 1 # print(type(obj3.sc)) # <class 'app.models.Class'> # print(obj3.sc.class_name) # 一班 # 反向查詢 # 二班有哪些學生 # obj4 = models.Class.objects.filter(class_name='二班')[0] # print(obj4) # # res = obj4.student_set.all() # 若是外鍵字段沒有設置 related_name 就用 表名_set # res = obj4.stu.all() # print(res) # <QuerySet [<Student: john>, <Student: lila>]> # for i in res: # print(i.student_name) # john、lila # 方法二 # ret = models.Student.objects.filter(sc=obj4).values('student_name') # 字典形式 # print(ret) # <QuerySet [{'student_name': 'john'}, {'student_name': 'lila'}]> # for i in ret: # print(i['student_name']) # john、lila # # 雙下劃線 # 正向 obj4 = models.Class.objects.filter(class_name='二班').values('stu__student_name') # 反向 obj5 = models.Student.objects.filter(sc__class_name='二班').values('student_name') ############################### 多對多(老師表與班級表) ############################################ # 查看 孫老師教哪幾個班 # 正向查詢(經過多對多字段) # obj5 = models.Teacher.objects.filter(teacher_name='孫老師')[0] # ret = obj5.tc.all() # print(ret) # <QuerySet [<Class: 一班>, <Class: 二班>]> # for i in ret: # print(i.class_name) # 查看一班有哪幾個老師 # 反向查詢 # obj5 = models.Class.objects.filter(class_name='一班')[0] # ret = obj5.b.all() # print(ret) # < QuerySet[ < Teacher: 孫老師 >, < Teacher: 劉老師 >] > # for i in ret: # print(i.teacher_name) # 孫老師、劉老師 # # 雙下劃線 # # 若是沒有設置 related_name = b,那麼就是 values('teacher__name`) 即要查的表的表名__要查詢的字段 # obj6 = models.Class.objects.filter(class_name='一班').values('b__teacher_name') # print(obj6) # <QuerySet [{'b__teacher_name': '孫老師'}, {'b__teacher_name': '劉老師'}]> # # 正向 tc = models.ManyToManyField(to='Class', related_name='b') # obj6 = models.Teacher.objects.filter(tc__class_name='一班').values('teacher_name') return HttpResponse('查詢成功!')
一對1、一對多、多對多,最好都設置好 related_name
參數,沒有設置時,反向查找用 **要查詢的命名.查詢字段 或 _set.all()**。
查詢分爲:
models.Models.objects.filter(過濾字段)
models.Model.objects.filter(過濾字段).values(要顯示的字段)
一對一
OneToOneField
字段查詢# detail = models.OneToOneField('StudentDetail', to_field='id', on_delete=models.CASCADE) # 正向 obj = models.Student.objects.filter(student_name='rose')[0] print(obj.detail.age) # 反向 obj2 = models.StudentDetail.objects.filter(email='456@qq.com')[0] print(obj2.student.student_name) # 這裏沒設置 related_name 所以用表名
一對多
related_name
,就用這個名字查詢filter(條件).values(要查詢的表名__要查詢字段)
filter(外鍵字段__條件).values(要查詢字段)
# sc = models.ForeignKey(to='Class', to_field='id', related_name='stu', on_delete=models.CASCADE) # 正向 obj3 = models.Student.objects.get(student_name='rose') print(obj3.sc.class_name) # 根據外鍵字段 sc 查詢 # 反向 obj4 = models.Class.objects.filter(class_name='二班')[0] # res = obj4.student_set.all() # 沒設置 related_name,用表名_set res = obj4.stu.all() # 用 related_name 名字 # 雙下劃線 # 正向 obj5 = models.Class.objects.filter(class_name='二班').values('stu__student_name') # 反向 obj6 = models.Student.objects.filter(sc__class_name='二班').values('student_name')
多對多
ManyToManyField
字段查詢related_name
名字查詢# tc = models.ManyToManyField(to='Class', related_name='b') # 正向 obj5 = models.Teacher.objects.filter(teacher_name='孫老師')[0] print(obj5.tc.all()) # 反向 obj5 = models.Class.objects.filter(class_name='一班')[0] print(obj5.b.all())
參考博客:django 一對1、一對多、多對多操做、經常使用方法
Django中全部的模型都必須繼承 django.db.models.Model
模型,一樣地咱們也能夠本身建立一個父模型用來保存公共部分,子模型繼承父模型。這樣的好處就是有時會省去不少重複代碼。
一樣地 Django 也支持繼承多個父類。
Django 中三種繼承方式:
Abstract base classes
,將子類共同的數據抽離,供子類複用,它不會建立實際的數據表Multi-table inheritance
,每個模型都有本身的數據庫表只需在模型中元類添加 abstract=True
,便可將模型轉換爲抽象基類。可是它不會建立實際數據庫表,只能用來被繼承。
from django.db import models class CommonInfo(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=64) class Meta: abstract = True class User(CommonInfo): email = models.EmailField(max_length=60)
模型 User 將會有 username、password、email
三個字段。
抽象基類元類
若是子類沒有元類,那麼它將繼承基類的元類,一樣地子類也能夠對基類的元類進行拓展:
class CommonInfo(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=64) class Meta: abstract = True ordering = ['username'] class User(CommonInfo): email = models.EmailField(max_length=60) # 繼承基類的元類,並進行拓展 class Meta(CommonInfo.Meta): db_table = 'username'
abstract=true
不會被繼承db_table
元數據無效,由於抽象基類不會建立數據表related_name 和 related_query_name
若抽象基類中存在 Foreignk
和 ManyToManyField
字段,且設置了 related_name
或 related_query_name
參數,其子類也將繼承這兩個參數。可是在查詢時就會出現錯誤。
例如,對於 app01/models.py
中:
class Base(models.Model): m2m = models.ManyToManyField(User, related_name="%(app_label)s_%(class)s_related", related_query_name="%(app_label)s_%(class)ss") class Meta: abstract = True class ChildA(Base): pass class ChildB(Base): pass
app01_childa_related
;反向查詢名(reverse query name)爲 app01_childas
app01_childb_related
和 app01_childbs
父類和子類都是可正常使用的模型,且都有本身的數據表,其本質爲 一對一 關係。
class UserInfo(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() class PersonalDetail(UserInfo): email = models.EmailField() description = models.CharField(max_length=4000)
PersonalDetail
繼承 UserInfo
,它會繼承父類全部的字段,兩個模型內部是一對一關係,以下所示:
>>> from polls.models import UserInfo, PersonalDetail >>> q = PersonalDetail.objects.all() >>> for i in q: ... i.name ... 'rose' 'lina'
多表繼承之元類
多表繼承中子類不會繼承父類的元類,可是有兩個元類數據除外: ordering、get_latest_by
,所以若但願不繼承父類的元類的全部元數據,就須要指定或重寫這兩個元數據:
class PersonalDetail(UserInfo): email = models.EmailField() description = models.CharField(max_length=4000) class Meta: # 重寫 ordering,移除父類元類的影響 ordering = []
與 Python 同樣,模型也能夠繼承多個父類模型,當有多個父類都含有 Meta 類時,那麼只有第一個父類的會被繼承,其他將被忽略掉。
Tips
AutoField
字段。class Base1(models.Model): base1_id = models.AutoField(primary_key=True) pass class Base2(models.Model): base2_id = models.AutoField(primary_key=True) pass class Child(Base1, Base2): pass
代理模型就是對源模型的一種代理,能夠建立、刪除、更新代理模型的實例,與源模型用的是同一張數據表,可是 它對源模型數據沒有影響。
要想將一個模型設置爲代理模型,只需在 Meta 類中設置 proxy=True
:
class A(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() class B(A): # 代理模型 class Meta: proxy = True ordering = ['age'] def do_something(self): pass
只會建立一張數據表,同時代理模型也能夠操做數據表:
from .models import A, B >>> A.objects.create(name='rose', age=18) >>> q = B.objects.get(name='rose') >>> q.age 18 # 對代理模型排序,按照年齡從小到大排列,被代理的模型,查詢不會排序 obj_list = B.objects.all() for obj in obj_list: print(obj.age) # 1七、18 、20 print(obj.name) # lila、rose、tom
Tips
abstract=True
便可設置爲抽象基類ordering 和 get_latest_by
# A 中的 name 將會覆蓋 B 中 的 name,除非 A 是抽象基類 class A(models.Model): name = models.CharField(max_length=32) # class Meta: # abstract = True class B(A): name = models.CharField(max_length=32)
組織模型
當模型有不少時,咱們最好將模型分割開來,分類存儲,這樣有利於組織。咱們能夠利用包來組織模型。
在應用中建立一個名爲 models
的包(pycharm 能夠直接建立包,不須要手動建立 init.py文件),而後將模型分類到各個 .py 文件中,最後將其導入 __init__.py
文件中:
# app/models/__init__.py from polls.models.modelone import User
aggregate()
返回一個字典,鍵爲聚合值標識符,值爲計算處理的聚合值。
使用時須要先導入內置函數:
from django.db.models import Avg, Sum, Max, Min, Count
示例:
from django.db.models import Avg, Sum, Max, Min, Count >>> models.UserInfo.objects.all.aggregate(avg_age=Avg('age')) # 指定鍵爲 avg_age,也能夠用默認的 {'avg_age': 18}
多個聚合:
from django.db.models import Avg, Sum, Max, Min, Count >>> models.UserInfo.objects.all.aggregate(avg_age=Avg('age'), max_length=Max('height')) {'avg_age': 18, 'max_length': 182}
ORM 中 values()
或 values_list()
選字段,就至關於原生 SQL 中 select
什麼字段:
ret = models.Employee.objects.all() # 至關於 select * from employee """ SELECT `employee`.`id`, `employee`.`name`, `employee`.`age`, `employee`.`salary`, `employee`.`province`, `employee`.`dept` FROM `employee` LIMIT 21; args=() """ ret = models.Employee.objects.all().values("dept", "age") """ SELECT `employee`.`dept`, `employee`.`age` FROM `employee` LIMIT 21; args=() """
select
選中什麼字段,group by
就只能是什麼字段:
mysql> select name, Avg(salary) from app_employee group by name; +------+-------------+ | name | Avg(salary) | +------+-------------+ | rose | 2000.1 | | lila | 3000.45 | | john | 4000.56 | | tom | 5005.78 | +------+-------------+
連表分組查詢
按照部門分組,查詢每一個部門的平均薪水:
# ORM 操做 # 前一個 values() 表示按照什麼分組,後一個表示要顯示的字段 >>> employee_list = models.Employee.objects.values('dept__name').annotate(avg=Avg('salary')).values('dept__name', 'avg') >>> print(employee_list) <QuerySet [{'dept__name': '財務部', 'avg': 2000.1}, {'dept__name': '事業部', 'avg': 4003.115}, {'dept__name': '人事部', 'avg': 4000.56}]>
# 對應 SQL 語句 SELECT `app_department`.`name`, AVG(`app_employee`.`salary`) AS `avg` FROM `app_employee` INNER JOIN `app_department` ON (`app_employee`.`dept_id` = `app_department`.`id`) GROUP BY `app_employee`.`name`, `app_department`.`name` ORDER BY NULL LIMIT 21; args=()
按照國家分組,查詢每一個國家的平均薪水:
>>> employee_list = models.Employee.objects.values('addr').annotate(avg=Avg('salary')).values('addr', 'avg') >>> print(employee_list) <QuerySet [{'addr': '美國', 'avg': 3502.9399999999996}, {'addr': '英國', 'avg': 3000.45}, {'addr': '德國', 'avg': 4000.56}]>
SELECT `app_employee`.`addr`, AVG(`app_employee`.`salary`) AS `avg` FROM `app_employee` GROUP BY `app_employee`.`addr` ORDER BY NULL LIMIT 21; args=()
Tips
inner john
)查詢values(fields)
爲分組字段,第二個 values(fileds1, fields2
) 爲要顯示的字段select
選中什麼字段,group by
就只能是什麼字段上面咱們都是在比較字段與某個常亮,要想兩個字段進行比較,那就要用到 F 查詢
from django.db.models import F models.Book.objects.filter(price__gt=F('comment_count'))
models.Book.objects.update(price=F('price')+5)
models.Book.objects.update(price=F('price')*5)
Q 構建搜索條件(在這裏解決了或、與的問題)
obj1 = models.StudentDetail.objects.filter(Q(age=18)) # 構建過濾條件
obj2 = models.StudentDetail.objects.filter(Q(age=18) | Q(age=19)) # 解決了 Django API 中沒有或的問題 obj3 = models.StudentDetail.objects.filter(Q(age=18) & Q(age=19)) # 解決了 Django API 中沒有與的問題 obj3 = models.StudentDetail.objects.filter(~ Q(age=19)) # 解決了 Django API 中沒有非的問題
q1 = Q() # 建立 Q 對象 q1.connector = 'OR' # q1 內部爲 or 關係,即 age=18 或 age =19 q1.children.append(('age', 18)) q1.children.append(('age', 19)) q2 = Q() # 建立 Q 對象 q2.connector = 'AND' # q2 內部爲 and 關係 q2.children.append(('gender', 0)) q3 = Q() # 建立 Q 對象 q3.add(q1, 'AND') # 將 q一、q2 都添加到 q3 中,建立總的過濾條件 q3.add(q2, 'AND') obj = models.StudentDetail.objects.filter(q3) print(obj) # <QuerySet [<StudentDetail: StudentDetail object (1)>]> # 原生 SQL SELECT "app_studentdetail"."id", "app_studentdetail"."age", "app_studentdetail"."gender", "app_studentdetail"."height", "app_studentdetail"."email" FROM "app_studentdetail" WHERE (("app_studentdetail"."age" = 18 OR "app_studentdetail"."age" = 19) AND "app_studentdetail"."gender" = 0) LIMIT 21;
models.StudentDetail.filter(Q(age=18), email__startswith='123')
import datetime obj = models.Class.objects.filter(Q(create_data=datetime.date(2019,2,15))) print(obj)