Django的Model使用


創建模型

使用Django的模型主要注意兩個方面:字段的類型和方法的重寫。這里用一個例子來說明,其中包含了常用的字段類型和如何重寫方法。

from django.db import models


class School(models.Model):
    pass


class Message(models.Model):
    pass


class Teacher(models.Model):
    pass
   
   
class Student(models.Model):

    GENDER_CHOICES = (
        ('male', "男"),
        ('female', "女"),
        ('secret', "保密")
    )

    name = models.CharField(max_length=40, blank=True, verbose_name="姓名")
    gender = models.CharField(max_length=6, choices=GENDER_CHOICES, default="secret", verbose_name="性別")
    age = models.IntegerField(default=0, verbose_name="年齡")
    rank = models.PositiveIntegerField(default=1, verbose_name="排名", unique=True)
    discount = models.DecimalField(max_digits=3, decimal_places=2, verbose_name="折扣", default=1.0)
    school = models.ForeignKey(to=School, verbose_name="學校", on_delete=models.CASCADE)
    message = models.OneToOneField(to=Message, verbose_name="信息", on_delete=models.CASCADE)
    teacher = models.ManyToManyField(verbose_name="老師", to=Teacher, blank=True)
    introduce = models.TextField(blank=True, verbose_name="介紹")
    grade = models.FloatField(default=0.0, verbose_name="成績")
    url = models.URLField(verbose_name="個人主頁", max_length=100)
    email = models.EmailField(verbose_name="郵箱")
    image = models.ImageField(upload_to='img/%Y/%m/%d/', verbose_name='上傳圖片', null=True)
    file = models.FileField(upload_to="file/%Y/%m/%d/", verbose_name="上傳文件", blank=True)
    is_deleted = models.BooleanField(verbose_name="已刪除", default=False, blank=True)
    time_added = models.DateTimeField(verbose_name="添加時間", auto_now_add=True, blank=True)

    def delete(self, using=None, keep_parents=False):
        self.is_deleted = True
        # some actions
        self.save()

    def save(self, force_insert=False, force_update=False, using=None,
             update_fields=None):
        # some actions
        self.name = self.name.capitalize()	# 首字母大寫
        return super().save(force_insert=force_insert, force_update=force_update, using=using,
                            update_fields=update_fields)

    def __repr__(self):
        return "UserProfile:{}".format(self.name)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['-time_added']
        verbose_name = "用戶信息"
        verbose_name_plural = verbose_name
        db_table = "student_info"
        unique_together = ("name", "gender")

字段類型

這里對常見字段中值得注意的地方作一下說明。

CharField

字符串類型值得注意的是當該字段只能在是某些指定的值時,要使用choices參數來指向預先設定的值,如果要在templates中展示說明使用{{ get_gender_display }}。

IntergerField & PositiveIntegerField

整數類型和正整數類型。

DecimalField

十進制浮點數,其中,參數max_digits代表數字有多少位,decimal_places代表小數部分有多少位。

ForeignKey

使用to指向被關聯的模型,使用on_delete來規定被關聯對象刪除時該對象的處理方式。主要有兩種取值,models.CASCADE和models.SET_NULL。models.CASCADE表示當被關聯對象刪除時刪除該對象,models.SET_NULL表示當被關聯對象被刪除時將該對象設置為空,此設置的前提是該字段要允許為空。

需要注意的是,有時候需要實現自關聯,比如部門的上級還是部門,這時候使用self表示本身模型:

class Department(models.Model):  
      
    ''''' 
    some other filed 
    '''  
    super_department = models.ForeignKey('self')  

ImageField & FileField

使用upload_to參數來指定文件保存的路徑。注意,該路徑前面再加上 MEDIA_ROOT中設置的路徑就是上傳的文件真實保存路徑了,如 MEDIA_ROOT的路徑是'/home/media',那圖片上傳的路徑就類似/home/media/img/2018/03/06。

BooleanField

布爾類型,可以使用default指定默認值。

DateTimeField

在Django中,代表時間字段的有三種:DateTimeField、DateField、TimeField,三種類型分別對應datetime()、date()、time(),都有auto_now和auto_now_add參數。

  • auto_now
    默認值為False,設置為True時會在每次修改該對象時自動更新為當前時間,但是無法手動修改該字段的值。
  • auto_now_add
    默認值為False,設置為True時會在創建對象時自動設置為當前時間,之后都不再修改,也不能手動修改其值。
  • 默認當前時間,又能修改
    有時候我們需要在創建對象時設置字段的值為當前時間,在后續時又能修改,使用auto_now或者auto_now_add都無法實現這一點。此時,可以使用default參數來設置默認值,如下
from django.db import models
from django.utils import timezone
class Message(models.Model):
    add_date = models.DateTimeField(verbose_name='保存日期',default = timezone.now)
    mod_date = models.DateTimeField(verbose_name='最后修改日期', auto_now = True)

重寫方法

delete

Django默認的刪除是將數據從數據庫里刪除,有時候我們需要軟刪除,保存以前的數據,這時候我們可以使用一個布爾類型的字段標識該條數據是否刪除,這時需要重寫delete方法實現軟刪除。
在delete方法中將is_deleted的值設置為True,表示該條數據已刪除。此外還可以執行一些關聯的動作,比如對相關字段賦值等,最后保存對象。

save

重寫save方法可以讓我們在保存數據時做一些相關的操作,比如保存姓名時自動設置為首字母大寫,執行完之后需要調用父類的save方法進行保存。

repr & str

兩者的作用是將變量或者常量轉換為字符串對象,這里重寫該方法使得對象實例能被轉化為字符串。

class Meta

  • ordering:結果集按照何種方式排序,上面例子表示按添加時間的逆序排序
  • verbose_name:對象的名稱
  • verbose_name_plural:對象復數形式的名稱
  • db_table:在數據庫中的表名
  • unique_together: 聯合唯一

常用方法

在對常用方法介紹部分,由於上面的模型包含字段較多,所以不使用上面創建的模型。這里使用一些常見的模型,通過名字就可以知道代表的內容,因此就不列出模型了。

創建實例

create

使用create方法可以創建一個模型實例,將各字段在參數中設置各個字段的值。

student = Student.objects.create(name='zhangsan', gender='male')

get_or_create

get_or_create的作用是查詢一個實例,當實例不存在時則創建一個實例。

obj, created = Person.objects.get_or_create(
    first_name='John',
    last_name='Lennon',
    defaults={'birthday': date(1940, 10, 9)},
)

函數返回一個(object, created)的tuple,object是查詢或者創建的對象實例,created是個布爾類型的值,表示是否是新創建的實例。在查詢時使用defaults以外的參數進行查詢,當實例不存在時將包含default參數一起創建一個新的實例。功能類似於如下代碼:

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
    obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
    obj.save()

update_or_create

update_or_create的作用是更新一個實例,當實力不存在時則創建一個實例。

obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon',
    defaults={'first_name': 'Bob'},
)

函數返回一個(object, created)的tuple,object是更新或者創建的對象實例,created是個布爾類型的值,表示是否是新創建的實例。在查詢的對象實例存在時,使用default中的參數進行更新,當實例不存在時,創建新的對象實例,需要更新的字段的值設置為default中的值。功能類似:

defaults = {'first_name': 'Bob'}
try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
    for key, value in defaults.items():
        setattr(obj, key, value)
    obj.save()
except Person.DoesNotExist:
    new_values = {'first_name': 'John', 'last_name': 'Lennon'}
    new_values.update(defaults)
    obj = Person(**new_values)
    obj.save()

add

這里補充一下add方法,add用在多對多的關系模型上,表示添加該字段的指向對象。

>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)

上面的Author表示作者模型,entry表示書籍條目,一本書可以有多個作者,采用多對多關系。add可以為書的實例添加多個作者。

查詢

all

all方法查詢該對象的所有實例。

all_entries = Entry.objects.all()

get

使用get查詢實例,當實例不存在時會返回一個不存在異常。

>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")

filter

filter返回一個QuerySet,而不是對象實例,當查詢結果不存在時返回空的QuerySet,而不是返回異常,可以對結果集作切片操作來獲取實例內容。下面代碼功能在有對象實例時等同於上面get的操作。

>>> entry = Entry.objects.filter(pk=1)[0]
>>> cheese_blog = Blog.objects.filter(name="Cheddar Talk")[0]

filter結果可以鏈式地進行操作,也就是后面可以接多個過濾條件。

import datetime
time = datetime.datetime.now()
Entry.objects.filter(status=type).exclude(time_start__gte=time)

Entry.objects.filter(pub_date__year=2006)
Entry.objects.all().filter(pub_date__year=2006)

上面的pub_date__year表示取出DateField類型的pub_date的年,類似的可以取出月__month,日__day。在使用外鍵ForeignKey時可以使用雙下划線來表示被關聯對象的字段。
現在來看一下鏈式過濾的例子。

Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime.date(2005, 1, 30)
... )

首先使用__startswith過濾標題以'What'開始的數據。然后保留讓發布日期小於今天的數據,exclude表示排除條件內的那部分數據,條件使用了__gte表示大於當前日期,該部分過濾類似於filter后面使用__lte小於。最后也是使用__gte大於一個自定義的日期。
除了上面列舉的一些查詢條件外還有很多常用的:

__exact			精確等於
__iexact		精確等於 忽略大小寫
__contains		包含
__icontains		包含 忽略大小寫
__gt			大於
__gte			大於等於
__lt			小於
__lte			小於等於
__in			存在於一個list范圍內
__startswith	以...開頭
__istartswith	以...開頭 忽略大小寫
__endswith		以...結尾
__iendswith		以...結尾 忽略大小寫
__range			在...范圍內
__year			日期字段的年份
__month			日期字段的月份
__day			日期字段的日
__isnull=True/False	

Q

Q是Django自帶的內容,用於查詢。主要的用途是在頁面的搜索框中輸入內容后台查詢相應的數據集。

from django.db.models import Q

student = student.filter(Q(name__icontains=search) |
                         Q(teacher__name__icontains=search) |
                         Q(gender__icontains=search) |
                         Q(url__icontains=search) |
                         Q(id__in=lid_lists))

上述例子常用在一個搜索框可能搜索多個字段的內容時,各個過濾條件之間使用“|”進行或者運算。當多條件查詢時,各個條件是並運算,使用“&”代替“|”。
Q可以復制給個變量,有時候我們需要對Q后面的內容先做一些處理,如將日期拼湊出來等,這時候可以先把Q賦值給一個變量,然后對變量進行“|”或者“&”操作。

query = Q(name__icontains=search)
query = query | Q(teacher__name__icontains=search)
student = student.filter(query)

反向查找

反向查找主要是用在外鍵的情況下,通過被關聯對象查找關聯對象。

# post文章,comment評論
comment_list = post.comment_set.all()   # 獲取文章的所有評論
post.comment_set.count      # 文章的所有評論數

除了上面的用法,還可以使用related_name來進行反向查找,而且當一個模型中有兩個字段關聯同一個模型時,在外鍵中一定要用related_name。

class School(models.Model):
	name = models.CharField()
class Student(models.Model):
	school = models.ForeignKey(School, related_name='SS')
	code = models.CharField()

這樣可以根據Student來查School:

School.objects.filter(SS_code="123456")

也可以通過School的實例查找所有以該實例為外鍵的Student:

school.SS.all()

外鍵列表

students = Students.objects.all()
# 外鍵school的id列表
school_ids = students.values_list("school", flat=True)
# school列表
schools = School.objects.filter(id__in=school_ids)

當要使獲取的列表去重時,使用distinct

school_ids = students.values_list("school", flat=True).distinct()

values()和values_list()區別:

students.values_list("school_id", flat=True).distinct()
# 獲取一個list	[1, 2]
# 如果不加 flat=True, 返回一個tuple的list [(1,), (2,)]
students.values("school_id").distinct()
# 返回一個dict的list 
# [{'school_id': 1}, {'school_id': 2}]


免責聲明!

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



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