Django之路:QuerySet API,後臺和表單

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',) 這樣就能夠按照 標題或內容搜索了  

https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields

篩選功能: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中都很容易實現

相關文章
相關標籤/搜索