一、Django QuerySet API |
Django模型中我們學習了一些基本的創建和查詢。這里專門講以下數據庫接口相關的接口(QuerySet API),當然你也可以選擇暫時跳過這節。如果以后用到數據庫相關的時候也可以在看看。
從數據庫中查詢出來的結果一般是一個集合,這個集合叫做QuerySet。
文中的例子大部分是基於這個blog/models.py
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __unicode__(self): # __str__ on Python 3 return self.name class Author(models.Model): name = models.CharField(max_length=50) email = models.EmailField() def __unicode__(self): # __str__ on Python 3 return self.name class Entry(models.Model): blog = models.ForeignKey(Blog) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __unicode__(self): # __str__ on Python 3 return self.headline
1、QuerySet 創建對象的方法
>>> from blog.models import Blog >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') >>> b.save() 總之,一共有四種方法 # 方法 1 Author.objects.create(name="wulaoer", email="wulaoer@163.com") # 方法 2 twz = Author(name="wulaoer", email="wulaoer@163.com") twz.save() # 方法 3 twz = Author() twz.name="wulaoer" twz.email="wulaoer@163.com" # 方法 4,首先嘗試獲取,不存在就創建,可以防止重復 Author.objects.get_or_create(name="wulaoer", email="wulaoer@163.com") # 返回值(object, True/False)
備注:前三種方法返回的都是對應的object,最后一種方法返回的是一個元組,(object,True/False),創建時返回True,已經存在時返回False
當有一對多,多對一,或者多對多的關系的時候,先把相關的對象查詢出來
>>> from blog.models import Entry >>> entry = Entry.objects.get(pk=1) >>> cheese_blog = Blog.objects.get(name="Cheddar Talk") >>> entry.blog = cheese_blog >>> entry.save()
2、獲取對象的方法(上一篇的部分代碼)
Person.objects.all() # 查詢所有 Person.objects.all()[:10] 切片操作,獲取10個人,不支持負索引,切片可以節約內存,不支持負索引,后面有相應解決辦法,第7條 Person.objects.get(name="wulaoer") # 名稱為 wulaoer 的一條,多條會報錯 get是用來獲取一個對象的,如果需要獲取滿足條件的一些人,就要用到filter Person.objects.filter(name="abc") # 等於Person.objects.filter(name__exact="abc") 名稱嚴格等於 "abc" 的人 Person.objects.filter(name__iexact="abc") # 名稱為 abc 但是不區分大小寫,可以找到 ABC, Abc, aBC,這些都符合條件 Person.objects.filter(name__contains="abc") # 名稱中包含 "abc"的人 Person.objects.filter(name__icontains="abc") #名稱中包含 "abc",且abc不區分大小寫 Person.objects.filter(name__regex="^abc") # 正則表達式查詢 Person.objects.filter(name__iregex="^abc")# 正則表達式不區分大小寫 # filter是找出滿足條件的,當然也有排除符合某條件的 Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person對象 Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名稱含有abc, 但是排除年齡是23歲的
3、QuerySet是可迭代的,比如:
es = Entry.objects.all() for e in es: print(e.headline)
Entry.objects.all()或者es就是QuerySet是查詢所有的Entry條目。
注意事項:
(1)、如果只是檢查Entry中是否有對象,應該用Entry.objects.all().exists()
(2)、QuerySet支持切片Entry.objects.all()[:10]取出10條,可以節省內存
(3)、用len(es)可以得到Entry的數量,但是推薦用Entry.objects.count()來查詢數量,后者用的是SQL: SELECT COUNT(*)
(4)、list(es)可以強行將QuerySet變成列表
4、QuerySet是可以用pickel序列化到硬盤讀取出來的
>>> import pickle >>> query = pickle.loads(s) # Assuming 's' is the pickled string. >>> qs = MyModel.objects.all() >>> qs.query = query # Restore the original 'query'.
5、QuerySet查詢結果排序
作者按照名稱排序
Author.objects.all().order_by('name') Author.objects.all().order_by('-name') # 在 column name 前加一個負號,可以實現倒序
6、QuerySet支持鏈式查詢
Author.objects.filter(name__contains="wulaoer").filter(email="wulaoer@163.com") Author.objects.filter(name__contains="wu").exclude(email="wulaoer@163.com") # 找出名稱含有abc, 但是排除年齡是23歲的 Person.objects.filter(name__contains="abc").exclude(age=23)
7、QuerySet不支持負索引
Person.objects.all()[:10] 切片操作,前10條 Person.objects.all()[-10:] 會報錯!!! # 1. 使用 reverse() 解決 Person.objects.all().reverse()[:2] # 最后兩條 Person.objects.all().reverse()[0] # 最后一條 # 2. 使用 order_by,在欄目名(column name)前加一個負號 Author.objects.order_by('-id')[:20] # id最大的20條
8、QuerySet重復的問題,使用.distinct()去重
一般的情況下,QuerySet中不會出來重復的,重復是很罕見的,但是當跨越多張表進行檢索后,結果並到一起,可以回出來重復的值(我最近就遇到過這樣的問題)
qs1 = Pathway.objects.filter(label__name='x') qs2 = Pathway.objects.filter(reaction__name='A + B >> C') qs3 = Pathway.objects.filter(inputer__name='wulaoer') # 合並到一起 qs = qs1 | qs2 | qs3 這個時候就有可能出現重復的 # 去重方法 qs = qs.distinct()
Django 后台 |
django的后台我們只要加少些代碼,就可以實現強大的功能。
與后台相關文件:每個app中的admn.py文件與后台相關。
下面示例是做一個后台添加博客文章的例子:
一、新建一個名稱問wlr_admin的例子:
django-admin.py startproject wlr_admin
二、新建一個叫做blog的app
# 進入 wlr_admin 文件夾 cd wlr_admin # 創建 blog 這個 app python manage.py startapp blog
注意:不同版本的Django創建project和app出來的文件會有一些不同
三、修改blog文件夾中的models.py
# coding:utf-8 from django.db import models class Article(models.Model): title = models.CharField(u'標題', max_length=256) content = models.TextField(u'內容') pub_date = models.DateTimeField(u'發表時間', auto_now_add=True, editable = True) update_time = models.DateTimeField(u'更新時間',auto_now=True, null=True)
四、把blog加入到settings.py中的INSTALLED_APPS中
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', )
提示:INSTALLED_APPS是一個元組,每次加入新的app的時候,在后面都加一個逗號,這是一個好習慣。
五、同步所有的數據表
# 進入包含有 manage.py 的文件夾 python manage.py syncdb 注意:Django 1.7及以上的版本需要用以下命令 python manage.py makemigrations python manage.py migrate
可以看到:
E:\wlr_admin>python manage.py syncdb Creating tables ... Creating table django_admin_log Creating table auth_permission Creating table auth_group_permissions Creating table auth_group Creating table auth_user_groups Creating table auth_user_user_permissions Creating table auth_user Creating table django_content_type Creating table django_session Creating table blog_article You just installed Django's auth system, which means you don't have any superusers defined. Would you like to create one now? (yes/no): yes Username (leave blank to use 'administrator'): wu Email address: Password: Password (again): Superuser created successfully. Installing custom SQL ... Installing indexes ... Installed 0 object(s) from 0 fixture(s)
如果是Django不主動提示創建管理員(Django 1.9不提示)用下面的命令創建一個帳號
python manage.py createsuperuser
六、修改admin.py
進入blog文件夾,修改admin.py文件(如果沒有新建一個),內容如下:
from django.contrib import admin from .models import Article admin.site.register(Article)
只需要這三行代碼,我們就可以擁有一個強大的后台!
提示:urls.py中關於admin的已經默認開啟。
七、打開開發服務器
python manage.py runserver # 如果提示 8000 端口已經被占用,可以用 python manage.py runserver 8001 以此類推
訪問http://127.0.0.1:8000/admin/輸入設定的帳號和密碼,就可以看到:
點擊Articles,動手輸入添加幾篇文章,就可以看到:
添加文章
根據需要選擇保存方式。
創建兩個文章看看。發現所有的文章都是叫Article object,這樣肯定不好,如果我們要是修改,肯定不知道要修改哪個。
我們修改以下blog中的models.py
# coding:utf-8 from django.db import models # Create your models here. class Article(models.Model): title = models.CharField(u'標題', max_length=256) content = models.TextField(u'內容') pub_date = models.DateTimeField(u'發表時間', auto_now_add=True, editable = True) update_time = models.DateTimeField(u'更新時間',auto_now=True, null=True) def __unicode__(self):# 在Python3中用 __str__ 代替 __unicode__ return self.title
加了一個__unicode__函數,刷新后台網頁,會看到:
所以推薦定義model的時候寫一個__unicdoe__函數(或__str__函數)
技能提升:如何兼容python 2.x和python 3.x呢?
示例如下:
# coding:utf-8 from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Article(models.Model): title = models.CharField('標題', max_length=256) content = models.TextField('內容') pub_date = models.DateTimeField('發表時間', auto_now_add=True, editable = True) update_time = models.DateTimeField('更新時間',auto_now=True, null=True) def __str__(self): return self.title
python_2_unicode_compatible會自動做一些處理去適應python不同版本,本例中的unicode_literals可以讓python 2.x也像python 3那個處理unicode字符,以便有更好地兼容性。
八、在列表顯示與字段相關的其他內容
后台已經基本上做出來了,可是如果我們還需要顯示一些其他的fields,如何做呢?
from django.contrib import admin from .models import Article class ArticleAdmin(admin.ModelAdmin): list_display = ('title','pub_date','update_time',) admin.site.register(Article,ArticleAdmin)
list_display就是來配置要顯示的字段的,當然也可以顯示非字段內容,或者字段相關的內容,比如:
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) def my_property(self): return self.first_name + ' ' + self.last_name my_property.short_description = "Full name of the person" full_name = property(my_property)
在admin.py中
from django.contrib import admin from .models import Article, Person class ArticleAdmin(admin.ModelAdmin): list_display = ('title', 'pub_date', 'update_time',) class PersonAdmin(admin.ModelAdmin): list_display = ('full_name',) admin.site.register(Article, ArticleAdmin) admin.site.register(Person, PersonAdmin)
到這里我們發現我們又有新的需求,比如要改models.py中的字段,添加一個文章的狀態。這時候我們就需要改表,django1.7以前的都不會自動更改表,我們需要用第三方插件South,參見Django遷移數據。
Django 1.7及以上用以下命令來同步數據庫表的更改
python manage.py makemigrations python manage.py migrate
本節代碼下載:
zqxt_admin (Django 1.6).zip (基於Django 1.6,后台帳號 tu 密碼 zqxt)
zqxt_admin (Django 1.9).zip (基於 Django 1.9 后台帳號 tu 密碼 ziqiangxuetang)
其他一些常用的功能:
搜索功能:search_fields = ('title', 'content',) 這樣就可以按照 標題或內容搜索了
篩選功能:list_filter = ('status',) 這樣就可以根據文章的狀態去篩選,比如找出是草稿的文章
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
新增或修改時的布局順序:https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets
有時候我們需要對django admin site進行修改以滿足自己的需求,那么我們可以從那些地方入手呢?
以下舉例說明:
1、定制加載的列表,根據不同的人顯示不同的內容列表,比如輸入員只能看見自己輸入的,審核員能看到所有的草稿,這時候就需要重寫get_queryset方法
class MyModelAdmin(admin.ModelAdmin): def get_queryset(self, request): qs = super(MyModelAdmin, self).get_queryset(request) if request.user.is_superuser: return qs else: return qs.filter(author=request.user)
該類實現的功能是如果是超級管理員就列出所有的,如果不是,就僅列出訪問者自己相關的
2、定制搜索功能(django 1.6及以上才有)
class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'age') search_fields = ('name',) def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super(PersonAdmin, self).get_search_results(request, queryset, search_term) try: search_term_as_int = int(search_term) queryset |= self.model.objects.filter(age=search_term_as_int) except: pass return queryset, use_distinct
queryset是默認的結果,search_term是在后台搜索的關鍵詞
3、修改保存時的一些操作,可以檢查用戶,保存的內容等,比如保存時加上添加人
from django.contrib import admin class ArticleAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): obj.user = request.user obj.save()
其中obj是修改后的對象,form是返回的表單(修改后的),當新建一個對象時change = False,當修改一個對象時change = True
如果需要獲取修改前的對象的內容可以用
from django.contrib import admin class ArticleAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): obj_original = self.model.objects.get(pk=obj.pk) obj.user = request.user obj.save()
那么又有問題了,這里如果原來的obj不存在,也就是如果我們是新建的一個怎么辦呢,這個時候可以用try,except的方法嘗試獲取,當然更好的方法是判斷以下這個對象是新建還是修改,是新建就沒有obj_original,是修改就有
from django.contrib import admin class ArticleAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): if change:# 更改的時候 obj_original = self.model.objects.get(pk=obj.pk) else:# 新增的時候 obj_original = None obj.user = request.user obj.save()
4、刪除時做一些處理。
from django.contrib import admin class ArticleAdmin(admin.ModelAdmin): def delete_model(self, request, obj): """ Given a model instance delete it from the database. """ # handle something here obj.delete()
Django 表單 |
有時候我們需要在前台用get或post方法提交一些數據,所以自己寫一個網頁,用到hetml表單的知識。
第一節:源碼下載zqxt_form_learn1.zip
比如寫一個計算a和b之和的簡單應用,網頁上這么寫
<!DOCTYPE html> <html> <body> <p>請輸入兩個數字</p> <form action="/add/" method="get"> a: <input type="text" name="a"> <br> b: <input type="text" name="b"> <br> <input type="submit" value="提交"> </form> </body> </html>
把這些代碼保存成一個index.html,放在templates文件夾中。
網頁的值傳到服務器是通過<input>或<textarea>標簽中的name屬性來傳遞的,在服務器端這么接收:
from django.http import HttpResponse from django.shortcuts import render def index(request): return render(request, 'index.html') def add(request): a = request.GET['a'] b = request.GET['b'] a = int(a) b = int(b) return HttpResponse(str(a+b))
request.GET可以看成一個字典,用GET方法傳遞的值都會保存到其中,可以用request.GET.get('key', None)來取值,沒有時不報錯。
在將函數和網址對應上,就可以訪問了,詳情參見源碼。
這樣就完成了基本的功能,基本上可以用了。
但是,比如用戶輸入的不是數字,而是字母,就出錯了,還有就是提交后再回來已經輸入的數據也會沒了。
當然如果我們手動將輸入之后的數據在views中都獲取到再傳遞到網頁,這樣是可行的,但是很不方便,如果Django提供了更方便簡易的forms來解決驗證等這一系列的問題。
第二節,使用Django的表單(forms)
例子足夠簡單,但是能說明問題
源碼下載:zqxt_forms.zip
新建一個 zqxt_form2 項目 django-admin.py startproject zqxt_form2 # 進入到 zqxt_form2 文件夾,新建一個 tools APP python manage.py startapp tools
在tools文件夾中新建一個forms.py文件
from django import forms class AddForm(forms.Form): a = forms.IntegerField() b = forms.IntegerField()
我們的視圖函數views.py中
# coding:utf-8 from django.shortcuts import render from django.http import HttpResponse # 引入我們創建的表單類 from .forms import AddForm def index(request): if request.method == 'POST':# 當提交表單時 form = AddForm(request.POST) # form 包含提交的數據 if form.is_valid():# 如果提交的數據合法 a = form.cleaned_data['a'] b = form.cleaned_data['b'] return HttpResponse(str(int(a) + int(b))) else:# 當正常訪問時 form = AddForm() return render(request, 'index.html', {'form': form})
對應的模版文件index.html
<form method='post'> {% csrf_token %} {{ form }} <input type="submit" value="提交"> </form>
再在urls.py中對應寫上這個函數
from django.conf.urls import patterns, include, url from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', # 注意下面這一行 url(r'^$', 'tools.views.index', name='home'), url(r'^admin/', include(admin.site.urls)), )
新手可能覺得這樣變得更麻煩了,有些情況是這樣的,但是Django的forms提供了:
1、模版中表單的渲染
2、數據的驗證工作,某一些輸入不合法也不會丟失已經輸入的數據。
3、還可以定制更復雜的驗證工作,如果提供了10個輸入框,必須要輸入其中兩個以上,在forms.py中都很容易實現