1、Django QuerySet API |
Django模型中咱們學習了一些基本的建立和查詢。這裏專門講如下數據庫接口相關的接口(QuerySet API),固然你也能夠選擇暫時跳過這節。若是之後用到數據庫相關的時候也能夠在看看。html
從數據庫中查詢出來的結果通常是一個集合,這個集合叫作QuerySet。python
文中的例子大部分是基於這個blog/models.pyreact
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
一、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數據庫
當有一對多,多對一,或者多對多的關係的時候,先把相關的對象查詢出來django
>>> 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()
二、獲取對象的方法(上一篇的部分代碼)服務器
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歲的
三、QuerySet是可迭代的,好比:session
es = Entry.objects.all() for e in es: print(e.headline)
Entry.objects.all()或者es就是QuerySet是查詢全部的Entry條目。app
注意事項:函數
(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變成列表
四、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'.
五、QuerySet查詢結果排序
做者按照名稱排序
Author.objects.all().order_by('name') Author.objects.all().order_by('-name') # 在 column name 前加一個負號,能夠實現倒序
六、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)
七、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條
八、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文件與後臺相關。
下面示例是作一個後臺添加博客文章的例子:
1、新建一個名稱問wlr_admin的例子:
django-admin.py startproject wlr_admin
2、新建一個叫作blog的app
# 進入 wlr_admin 文件夾 cd wlr_admin # 建立 blog 這個 app python manage.py startapp blog
注意:不一樣版本的Django建立project和app出來的文件會有一些不一樣
3、修改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)
4、把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的時候,在後面都加一個逗號,這是一個好習慣。
5、同步全部的數據表
# 進入包含有 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
6、修改admin.py
進入blog文件夾,修改admin.py文件(若是沒有新建一個),內容以下:
from django.contrib import admin from .models import Article admin.site.register(Article)
只須要這三行代碼,咱們就能夠擁有一個強大的後臺!
提示:urls.py中關於admin的已經默認開啓。
7、打開開發服務器
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字符,以便有更好地兼容性。
8、在列表顯示與字段相關的其餘內容
後臺已經基本上作出來了,但是若是咱們還須要顯示一些其餘的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進行修改以知足本身的需求,那麼咱們能夠從那些地方入手呢?
如下舉例說明:
一、定製加載的列表,根據不一樣的人顯示不一樣的內容列表,好比輸入員只能看見本身輸入的,審覈員能看到全部的草稿,這時候就須要重寫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)
該類實現的功能是若是是超級管理員就列出全部的,若是不是,就僅列出訪問者本身相關的
二、定製搜索功能(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是在後臺搜索的關鍵詞
三、修改保存時的一些操做,能夠檢查用戶,保存的內容等,好比保存時加上添加人
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()
四、刪除時作一些處理。
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提供了:
一、模版中表單的渲染
二、數據的驗證工做,某一些輸入不合法也不會丟失已經輸入的數據。
三、還能夠定製更復雜的驗證工做,若是提供了10個輸入框,必需要輸入其中兩個以上,在forms.py中都很容易實現