python Django教程 之 模型(數據庫)、自定義Field、數據表更改、QuerySet API


               python  Django教程  之 模型(數據庫)、自定義Field、數據表更改、QuerySet API

 

 

一、Django 模型(數據庫)

Django 模型是與數據庫相關的,與數據庫相關的代碼一般寫在 models.py 中,Django 支持 sqlite3, MySQL, PostgreSQL等數據庫,只需要在settings.py中配置即可,不用更改models.py中的代碼,豐富的API極大的方便了使用。

本節的代碼:(Django 1.6, Python 2.7 測試環境)

大家按照我步驟來開始做:

django-admin.py startproject learn_models # 新建一個項目
cd learn_models # 進入到該項目的文件夾
django-admin.py startapp people # 新建一個 people 應用(app)

補充:新建app也可以用 python manage.py startapp people, 需要指出的是,django-admin.py 是安裝Django后多出的一個命令,並不是指一個 django-admin.py 腳本在當前目錄下。

那么project和app什么關系呢,一個項目一般包含多個應用,一個應用也可以用在多個項目中。

 

將我們新建的應用(people)添加到 settings.py 中的 INSTALLED_APPS中,也就是告訴Django有這么一個應用。

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'people',
)

我們打開 people/models.py 文件,修改其中的代碼如下:

from django.db import models
 
class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

我們新建了一個Person類,繼承自models.Model, 一個人有姓名和年齡。這里用到了兩種Field,更多Field類型可以參考教程最后的鏈接。

 

我們來同步一下數據庫

python manage.py syncdb # 進入 manage.py 所在的那個文件夾下輸入這個命令
 
注意:Django 1.7 及以上的版本需要用以下命令
python manage.py makemigrations
python manage.py migrate

我們會看到,Django生成了一系列的表,也生成了我們新建的people_person這個表,那么如何使用這個表呢?

Django提供了豐富的API, 下面演示如何使用它。

$ python manage.py shell
 
>>> from people.models import Person
>>> Person.objects.create(name="WeizhongTu", age=24)
<Person: Person object>
>>>

我們新建了一個用戶WeizhongTu 那么如何從數據庫是查詢到它呢?

>>> Person.objects.get(name="WeizhongTu")
<Person: Person object>
>>>

我們用了一個 .objects.get() 方法查詢出來符合條件的對象,但是大家注意到了沒有,查詢結果中顯示<Person: Person object>,這里並沒有顯示出與WeizhongTu的相關信息,如果用戶多了就無法知道查詢出來的到底是誰,查詢結果是否正確,我們重新修改一下 people/models.py

name 和 age 等字段中不能有 __(雙下划線,因為在Django QuerySet API中有特殊含義(用於關系,包含,不區分大小寫,以什么開頭或結尾,日期的大於小於,正則等)

也不能有Python中的關鍵字,name 是合法的,student_name 也合法,但是student__name不合法,try, class, continue 也不合法,因為它是Python的關鍵字( import keyword; print(keyword.kwlist) 可以打出所有的關鍵字)

 

from django.db import models
 
class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()
     
    def __unicode__(self):
    # 在Python3中使用 def __str__(self)
        return self.name

按CTRL + C退出當前的Python shell, 重復上面的操作,我們就可以看到:

新建一個對象的方法有以下幾種:

  1. Person.objects.create(name=name,age=age)

  2. p = Person(name="WZ", age=23)

    p.save()

  3. p = Person(name="TWZ")

    p.age = 23

    p.save()

  4. Person.objects.get_or_create(name="WZT", age=23)

    這種方法是防止重復很好的方法,但是速度要相對慢些,返回一個元組,第一個為Person對象,第二個為True或False, 新建時返回的是True, 已經存在時返回False.

 

獲取對象有以下方法:

  1. Person.objects.all()

  2. Person.objects.all()[:10] 切片操作,獲取10個人,不支持負索引,切片可以節約內存

  3. Person.objects.get(name=name) 

    get是用來獲取一個對象的,如果需要獲取滿足條件的一些人,就要用到filter

  4. Person.objects.filter(name="abc") # 等於Person.objects.filter(name__exact="abc") 名稱嚴格等於 "abc" 的人

  5. Person.objects.filter(name__iexact="abc") # 名稱為 abc 但是不區分大小寫,可以找到 ABC, Abc, aBC,這些都符合條件 

  6. Person.objects.filter(name__contains="abc") # 名稱中包含 "abc"的人

  7. Person.objects.filter(name__icontains="abc") #名稱中包含 "abc",且abc不區分大小寫 

  8. Person.objects.filter(name__regex="^abc") # 正則表達式查詢

  9. Person.objects.filter(name__iregex="^abc")# 正則表達式不區分大小寫 

    filter是找出滿足條件的,當然也有排除符合某條件的

  10. Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person對象

  11. Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名稱含有abc, 但是排除年齡是23歲的

 

 數據字段含義

 

1、models.AutoField  自增列 = int(11)
  如果沒有的話,默認會生成一個名稱為 id 的列,如果要顯示的自定義一個自增列,必須將給列設置為主鍵 primary_key=True。
2、models.CharField  字符串字段
  必須 max_length 參數
3、models.BooleanField  布爾類型=tinyint(1)
  不能為空,Blank=True
4、models.ComaSeparatedIntegerField  用逗號分割的數字=varchar
  繼承CharField,所以必須 max_lenght 參數
5、models.DateField  日期類型 date
  對於參數,auto_now = True 則每次更新都會更新這個時間;auto_now_add 則只是第一次創建添加,之后的更新不再改變。
6、models.DateTimeField  日期類型 datetime
  同DateField的參數
7、models.Decimal  十進制小數類型 = decimal
  必須指定整數位max_digits和小數位decimal_places
8、models.EmailField  字符串類型(正則表達式郵箱) =varchar
  對字符串進行正則表達式
9、models.FloatField  浮點類型 = double
10、models.IntegerField  整形
11、models.BigIntegerField  長整形
  integer_field_ranges = {
    'SmallIntegerField': (-32768, 32767),
    'IntegerField': (-2147483648, 2147483647),
    'BigIntegerField': (-9223372036854775808, 9223372036854775807),
    'PositiveSmallIntegerField': (0, 32767),
    'PositiveIntegerField': (0, 2147483647),
  }
12、models.IPAddressField  字符串類型(ip4正則表達式)
13、models.GenericIPAddressField  字符串類型(ip4和ip6是可選的)
  參數protocol可以是:both、ipv4、ipv6
  驗證時,會根據設置報錯
14、models.NullBooleanField  允許為空的布爾類型
15、models.PositiveIntegerFiel  正Integer
16、models.PositiveSmallIntegerField  正smallInteger
17、models.SlugField  減號、下划線、字母、數字
18、models.SmallIntegerField  數字
  數據庫中的字段有:tinyint、smallint、int、bigint
19、models.TextField  字符串=longtext
20、models.TimeField  時間 HH:MM[:ss[.uuuuuu]]
21、models.URLField  字符串,地址正則表達式
22、models.BinaryField  二進制
23、models.ImageField   圖片
24、models.FilePathField 文件
字段含義

 

 

字段參數

 

1、null=True
  數據庫中字段是否可以為空
2、blank=True
  django的 Admin 中添加數據時是否可允許空值
3、primary_key = False
  主鍵,對AutoField設置主鍵后,就會代替原來的自增 id 列
4、auto_now 和 auto_now_add
  auto_now   自動創建---無論添加或修改,都是當前操作的時間
  auto_now_add  自動創建---永遠是創建時的時間
5、choices
GENDER_CHOICE = (
        (u'M', u'Male'),
        (u'F', u'Female'),
    )
gender = models.CharField(max_length=2,choices = GENDER_CHOICE)
6、max_length
7、default  默認值
8、verbose_name  Admin中字段的顯示名稱
9、name|db_column  數據庫中的字段名稱
10、unique=True  不允許重復
11、db_index = True  數據庫索引
12、editable=True  在Admin里是否可編輯
13、error_messages=None  錯誤提示
14、auto_created=False  自動創建
15、help_text  在Admin中提示幫助信息
16、validators=[]
17、upload-to
字段參數

 

 

數據庫基本操作

1.基本操作

    • 增加:創建實例,並調用save
    • 更新:a.獲取實例,再sava;b.update(指定列)
    • 刪除:a. filter().delete(); b.all().delete()
    • 獲取:a. 單個=get(id=1) ;b. 所有 = all()
    • 過濾:filter(name='xxx');filter(name__contains='');(id__in = [1,2,3]) ;
      icontains(大小寫無關的LIKE),startswith和endswith, 還有range(SQLBETWEEN查詢)'gt', 'in', 'isnull', 'endswith', 'contains', 'lt', 'startswith', 'iendswith', 'icontains','range', 'istartswith'
    • 排序:order_by("name") =asc ;order_by("-name")=desc
    • 返回第n-m條:第n條[0];前兩條[0:2]
    • 指定映射:values
    • 數量:count()
    • 聚合:from django.db.models import Min,Max,Sum objects.all().aggregate(Max('guest_id'))
    • 原始SQL
    • cursor = connection.cursor()
      cursor.execute('''SELECT DISTINCT first_name ROM people_person WHERE last_name = %s""", ['Lennon'])
      row = cursor.fetchone()

       

#
    #
    # models.Tb1.objects.create(c1='xx', c2='oo')  增加一條數據,可以接受字典類型數據 **kwargs

    # obj = models.Tb1(c1='xx', c2='oo')
    # obj.save()

    #
    #
    # models.Tb1.objects.get(id=123)         # 獲取單條數據,不存在則報錯(不建議)
    # models.Tb1.objects.all()               # 獲取全部
    # models.Tb1.objects.filter(name='seven') # 獲取指定條件的數據

    #
    #
    # models.Tb1.objects.filter(name='seven').delete() # 刪除指定條件的數據

    #
    # models.Tb1.objects.filter(name='seven').update(gender='0')  # 將指定條件的數據更新,均支持 **kwargs
    # obj = models.Tb1.objects.get(id=1)
    # obj.c1 = '111'
    # obj.save()                                                 # 修改單條數據
基本操作

2、進階操作(了不起的雙下划線)

利用雙下划線將字段和對應的操作連接起來

# 獲取個數
    #
    # models.Tb1.objects.filter(name='seven').count()

    # 大於,小於
    #
    # models.Tb1.objects.filter(id__gt=1)              # 獲取id大於1的值
    # models.Tb1.objects.filter(id__lt=10)             # 獲取id小於10的值
    # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 獲取id大於1 且 小於10的值

    # in
    #
    # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 獲取id等於11、22、33的數據
    # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

    # contains
    #
    # models.Tb1.objects.filter(name__contains="ven")
    # models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
    # models.Tb1.objects.exclude(name__icontains="ven")

    # range
    #
    # models.Tb1.objects.filter(id__range=[1, 2])   # 范圍bettwen and

    # 其他類似
    #
    # startswith,istartswith, endswith, iendswith,

    # order by
    #
    # models.Tb1.objects.filter(name='seven').order_by('id')    # asc
    # models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

    # limit 、offset
    #
    # models.Tb1.objects.all()[10:20]

    # group by
    from django.db.models import Count, Min, Max, Sum
    # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
    # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
進階操作

3、連表操作(了不起的雙下划線)

利用雙下划線和 _set 將表之間的操作連接起來

class UserProfile(models.Model):
    user_info = models.OneToOneField('UserInfo')
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=64)

    def __unicode__(self):
        return self.username


class UserInfo(models.Model):
    user_type_choice = (
        (0, u'普通用戶'),
        (1, u'高級用戶'),
    )
    user_type = models.IntegerField(choices=user_type_choice)
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    address = models.CharField(max_length=128)

    def __unicode__(self):
        return self.name


class UserGroup(models.Model):

    caption = models.CharField(max_length=64)

    user_info = models.ManyToManyField('UserInfo')

    def __unicode__(self):
        return self.caption


class Host(models.Model):
    hostname = models.CharField(max_length=64)
    ip = models.GenericIPAddressField()
    user_group = models.ForeignKey('UserGroup')

    def __unicode__(self):
        return self.hostname
表結構實例
user_info_obj = models.UserInfo.objects.filter(id=1).first()
print user_info_obj.user_type
print user_info_obj.get_user_type_display()
print user_info_obj.userprofile.password
 
user_info_obj = models.UserInfo.objects.filter(id=1).values('email', 'userprofile__username').first()
print user_info_obj.keys()
print user_info_obj.values()
一對一操作
類似一對一
1、搜索條件使用 __ 連接
2、獲取值時使用 .    連接
一對多
user_info_obj = models.UserInfo.objects.get(name=u'武沛齊')
user_info_objs = models.UserInfo.objects.all()
 
group_obj = models.UserGroup.objects.get(caption='CEO')
group_objs = models.UserGroup.objects.all()
 
# 添加數據
#group_obj.user_info.add(user_info_obj)
#group_obj.user_info.add(*user_info_objs)
 
# 刪除數據
#group_obj.user_info.remove(user_info_obj)
#group_obj.user_info.remove(*user_info_objs)
 
# 添加數據
#user_info_obj.usergroup_set.add(group_obj)
#user_info_obj.usergroup_set.add(*group_objs)
 
# 刪除數據
#user_info_obj.usergroup_set.remove(group_obj)
#user_info_obj.usergroup_set.remove(*group_objs)
 
# 獲取數據
#print group_obj.user_info.all()
#print group_obj.user_info.all().filter(id=1)
 
# 獲取數據
#print user_info_obj.usergroup_set.all()
#print user_info_obj.usergroup_set.all().filter(caption='CEO')
#print user_info_obj.usergroup_set.all().filter(caption='DBA')
多對多
# F 使用查詢條件的值
    #
    # from django.db.models import F
    # models.Tb1.objects.update(num=F('num')+1)

    # Q 構建搜索條件
    from django.db.models import Q
    # con = Q()
    #
    # q1 = Q()
    # q1.connector = 'OR'
    # q1.children.append(('id', 1))
    # q1.children.append(('id', 10))
    # q1.children.append(('id', 9))
    #
    # q2 = Q()
    # q2.connector = 'OR'
    # q2.children.append(('c1', 1))
    # q2.children.append(('c1', 10))
    # q2.children.append(('c1', 9))
    #
    # con.add(q1, 'AND')
    # con.add(q2, 'AND')
    #
    # models.Tb1.objects.filter(con)

    #
    # from django.db import connection
    # cursor = connection.cursor()
    # cursor.execute("""SELECT * from tb where name = %s""", ['Lennon'])
    # row = cursor.fetchone()
復制代碼
其他操作

注意:xx_set中的[_set]是多對多中的固定搭配

擴展:

a、自定義上傳

def upload_file(request):
    if request.method == "POST":
        obj = request.FILES.get('fafafa')
        f = open(obj.name, 'wb')
        for chunk in obj.chunks():
            f.write(chunk)
        f.close()
    return render(request, 'file.html')
自定義上傳文件

b、Form上傳文件實例

class FileForm(forms.Form):
    ExcelFile = forms.FileField()
From
from django.db import models

class UploadFile(models.Model):
    userid = models.CharField(max_length = 30)
    file = models.FileField(upload_to = './upload/')
    date = models.DateTimeField(auto_now_add=True)
module
def UploadFile(request):
    uf = AssetForm.FileForm(request.POST,request.FILES)
    if uf.is_valid():
            upload = models.UploadFile()
            upload.userid = 1
            upload.file = uf.cleaned_data['ExcelFile']
            upload.save()
            
            print upload.file
View

django中的Form一般有兩種功能:

  • 輸入html
  • 驗證用戶輸入
復制代碼
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
from django import forms
from django.core.exceptions import ValidationError


def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手機號碼格式錯誤')


class PublishForm(forms.Form):

    user_type_choice = (
        (0, u'普通用戶'),
        (1, u'高級用戶'),
    )

    user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,
                                                                  attrs={'class': "form-control"}))

    title = forms.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': u'標題不能為空',
                                            'min_length': u'標題最少為5個字符',
                                            'max_length': u'標題最多為20個字符'},
                            widget=forms.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'標題5-20個字符'}))

    memo = forms.CharField(required=False,
                           max_length=256,
                           widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'詳細描述', 'rows': 3}))

    phone = forms.CharField(validators=[mobile_validate, ],
                            error_messages={'required': u'手機不能為空'},
                            widget=forms.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手機號碼'}))

    email = forms.EmailField(required=False,
                            error_messages={'required': u'郵箱不能為空','invalid': u'郵箱格式錯誤'},
                            widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'郵箱'}))
From
def publish(request):
    ret = {'status': False, 'data': '', 'error': '', 'summary': ''}
    if request.method == 'POST':
        request_form = PublishForm(request.POST)
        if request_form.is_valid():
            request_dict = request_form.clean()
            print request_dict
            ret['status'] = True
        else:
            error_msg = request_form.errors.as_json()
            ret['error'] = json.loads(error_msg)
    return HttpResponse(json.dumps(ret))
View

 

 

 

二、Django 自定義Field

 

Django 的官方提供了很多的 Field,但是有時候還是不能滿足我們的需求,不過Django提供了自定義 Field 的方法:

提示:如果現在用不到可以跳過這一節,不影響后面的學習,等用到的時候再來學習不遲。

來一個簡單的例子吧。

1. 減少文本的長度,保存數據的時候壓縮,讀取的時候解壓縮,如果發現壓縮后更長,就用原文本直接存儲:

Django 1.7 以下

from django.db import models
 
class CompressedTextField(models.TextField):
    """    model Fields for storing text in a compressed format (bz2 by default)    """
    __metaclass__ = models.SubfieldBase
 
    def to_python(self, value):
        if not value:
            return value
 
        try:
            return value.decode('base64').decode('bz2').decode('utf-8')
        except Exception:
            return value
 
    def get_prep_value(self, value):
        if not value:
            return value
 
        try:
            value.decode('base64')
            return value
        except Exception:
            try:
                tmp = value.encode('utf-8').encode('bz2').encode('base64')
            except Exception:
                return value
            else:
                if len(tmp) > len(value):
                    return value
 
                return tmp

to_python 函數用於轉化數據庫中的字符到 Python的變量, get_prep_value 用於將Python變量處理后(此處為壓縮)保存到數據庫,使用和Django自帶的 Field 一樣。

Django 1.8 以上版本,可以用

#coding:utf-8
from django.db import models
 
 
class CompressedTextField(models.TextField):
    """
    model Fields for storing text in a compressed format (bz2 by default)
    """
 
    def from_db_value(self, value, expression, connection, context):
        if not value:
            return value
        try:
            return value.decode('base64').decode('bz2').decode('utf-8')
        except Exception:
            return value
 
    def to_python(self, value):
        if not value:
            return value
        try:
            return value.decode('base64').decode('bz2').decode('utf-8')
        except Exception:
            return value
 
    def get_prep_value(self, value):
        if not value:
            return value
        try:
            value.decode('base64')
            return value
        except Exception:
            try:
                return value.encode('utf-8').encode('bz2').encode('base64')
            except Exception:
                return value

Django 1.8及以上版本中,from_db_value 函數用於轉化數據庫中的字符到 Python的變量

 

2. 比如我們想保存一個 列表到數據庫中,在讀取用的時候要是 Python的列表的形式,我們來自己寫一個 ListField

這個ListField繼承自 TextField,代碼如下:

from django.db import models
import ast
 
class ListField(models.TextField):
    __metaclass__ = models.SubfieldBase
    description = "Stores a python list"
 
    def __init__(self, *args, **kwargs):
        super(ListField, self).__init__(*args, **kwargs)
 
    def to_python(self, value):
        if not value:
            value = []
 
        if isinstance(value, list):
            return value
 
        return ast.literal_eval(value)
 
    def get_prep_value(self, value):
        if value is None:
            return value
 
        return unicode(value) # use str(value) in Python 3
 
    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

使用它很簡單,首先導入 ListField,像自帶的 Field 一樣使用:

class Article(models.Model):
    labels = ListField()

在終端上嘗試(運行 python manage.py shell 進入):

>>> from app.models import Article
>>> d = Article()
>>> d.labels
[]
>>> d.labels = ["Python", "Django"]
>>> d.labels
["Python", "Django"]

 

三、Django 數據表更改

我們設計數據庫的時候,早期設計完后,后期會發現不完善,需要對數據表進行更改.

Django 1.7.x 及以后的版本集成了 South 的功能,在修改models.py了后運行:

python manage.py makemigrations
python manage.py migrate

這兩行命令就會對我們的models.py 進行檢測,自動發現需要更改的,應用到數據庫中去。

Django 1.6.x 及以前:

寫過Django項目的同學,必然會遇到這個問題:

在Django 1.6以及以前的版本中,我們測試,當發現model要改,怎么辦?

 

我們修改了 models.py 之后,我們運行:

python manage.py syncdb

這句話只會將我們在 models.py 中新加的類創建相應的表。

對於原來有的,現在刪除了的類,Django 會詢問是否要刪除數據庫中已經存在的相關數據表。

 

如果在原來的類上增加字段或者刪除字段,可以參考這個命令:

python manage.py sql appname

給出的SQL語句,然后自己手動到數據庫執行 SQL 。但是這樣非常容易出錯!

Django 的第三方 app South 就是專門做數據庫表結構自動遷移工作,Jacob Kaplan-Moss 曾做過一次調查,South 名列最受歡迎的第三方 app。事實上,它現在已經儼然成為 Django 事實上的數據庫表遷移標准,很多第三方 app 都會帶 South migrations 腳本,Django 1.7 中集成了 South 的功能。

 

1, 安裝South

(sudo) pip install South

2. 使用方法

一個好的程序使用起來必定是簡單的,South和它的宗旨一樣,使用簡單。只需要簡單幾步,針對已經建好model和創建完表的應用。

 

把south加入到settings.py中的INSTALL_APPS中

# Application definition
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
 
    'blog',
    'south',
)

修改好后運行一次 python manage.py syncdb,Django會新建一個 south_migrationhistory 表,用來記錄數據表更改(Migration)的歷史紀錄。

$ python manage.py syncdb
Syncing...
Creating tables ...
Creating table south_migrationhistory
Installing custom SQL ...
Installing indexes ...
No fixtures found.
 
Synced:
 > django.contrib.admin
 > django.contrib.auth
 > django.contrib.contenttypes
 > django.contrib.sessions
 > django.contrib.messages
 > django.contrib.staticfiles
 > blog
 > south
 
Not synced (use migrations):

如果要把之前建好的比如 blog 這個 app 使用 South 來管理:

$ python manage.py convert_to_south blog

 

你會發現blog文件夾中多了一個 migrations 目錄,里面有一個 0001_initial.py 文件。

 

注:如果 blog 這個 app 之前就創建過相關的表,可以用下面的來“假裝”用 South 創建(偽創建,在改動 models.py 之前運行這個)

python manage.py migrate blog --fake

意思是這個表我以前已經建好了,用 South 只是紀一下這個創建記錄,下次 migrate 的時候不必再創建了。

原理就是 south_migrationhistory 中記錄下了 models.py 的修改的歷史,下次再修改時會和最近一次記錄比較,發現改變了什么,然后生成相應的對應文件,最終執行相應的 SQL 更改原有的數據表。

 

接着,當你對 Blog.models 做任何修改后,只要執行:

$ python manage.py schemamigration blog --auto

South就會幫助我們找出哪些地方做了修改,如果你新增的數據表沒有給default值,並且沒有設置null=True, south會問你一些問題,因為新增的column對於原來的舊的數據不能為Null的話就得有一個值。順利的話,在migrations文件夾下會產生一個0002_add_mobile_column.py,但是這一步並沒有真正修改數據庫的表,我們需要執行 python manage.py migrate :

$ python manage.py migrate
Running migrations for blog:
 - Migrating forwards to 0002_add_mobile_column.
 > blog:0002_add_mobile_column
 - Loading initial data for blog.
No fixtures found.

這樣所做的更改就寫入到了數據庫中了。

 

恢復到以前

South好處就是可以隨時恢復到之前的一個版本,比如我們想要回到最開始的那個版本:

> python manage.py migrate blog 0001
 - Soft matched migration 0001 to 0001_initial.
Running migrations for blog:
 - Migrating backwards to just after 0001_initial.
 < blog:0002_add_mobile_column

這樣就搞定了,數據庫就恢復到以前了,比你手動更改要方便太多了。

 

 

四、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="WeizhongTu", email="tuweizhong@163.com")
 
# 方法 2
twz = Author(name="WeizhongTu", email="tuweizhong@163.com")
twz.save()
 
# 方法 3
twz = Author()
twz.name="WeizhongTu"
twz.email="tuweizhong@163.com"
 
# 方法 4,首先嘗試獲取,不存在就創建,可以防止重復
Author.objects.get_or_create(name="WeizhongTu", email="tuweizhong@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="WeizhongTu") # 名稱為 WeizhongTu 的一條,多條會報錯
 
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 是可以用pickle序列化到硬盤再讀取出來的

>>> 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="WeizhongTu").filter(email="tuweizhong@163.com")
Author.objects.filter(name__contains="Wei").exclude(email="tuweizhong@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='WeizhongTu')
 
# 合並到一起
qs = qs1 | qs2 | qs3
這個時候就有可能出現重復的
 
# 去重方法
qs = qs.distinct()

 

參考網址:

https://djangosnippets.org/snippets/2014/

https://docs.djangoproject.com/en/dev/howto/custom-model-fields/

參考文檔:

Django models 官方教程: https://docs.djangoproject.com/en/dev/topics/db/models/

Fields相關官方文檔:https://docs.djangoproject.com/en/dev/ref/models/fields/

Django數據庫操作官方文檔: QuerySet API: https://docs.djangoproject.com/en/dev/ref/models/querysets/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM