Django之ORM單表查詢13條+外鍵操作(一對多)


一、ORM介紹

1、ORM概念

對象關系映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向對象與關系數據庫存在的互不匹配的現象的技術。

簡單的說,ORM是通過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到關系數據庫中。

ORM在業務邏輯層和數據庫層之間充當了橋梁的作用。

2、ORM由來

讓我們從O/R開始。字母O起源於"對象"(Object),而R則來自於"關系"(Relational)。

幾乎所有的軟件開發過程中都會涉及到對象和關系數據庫。在用戶層面和業務邏輯層面,我們是面向對象的。當對象的信息發生變化的時候,我們就需要把對象的信息保存在關系數據庫中。

按照之前的方式來進行開發就會出現程序員會在自己的業務邏輯代碼中夾雜很多SQL語句用來增加、讀取、修改、刪除相關數據,而這些代碼通常都是重復的。

3、ORM優勢

ORM解決的主要問題是對象和關系的映射。它通常把一個類和一個表一一對應,類的每個實例對應表中的一條記錄,類的每個屬性對應表中的每個字段。 

ORM提供了對數據庫的映射,不用直接編寫SQL代碼,只需像操作對象一樣從數據庫操作數據。

讓軟件開發人員專注於業務邏輯的處理,提高了開發效率。

4、ORM劣勢

ORM的缺點是會在一定程度上犧牲程序的執行效率。

ORM用多了SQL語句就不會寫了,關系數據庫相關技能退化...

5、總結

ORM只是一種工具,工具確實能解決一些重復,簡單的勞動。這是不可否認的。

但我們不能指望某個工具能一勞永逸地解決所有問題,一些特殊問題還是需要特殊處理的。

但是在整個軟件開發過程中需要特殊處理的情況應該都是很少的,否則所謂的工具也就失去了它存在的意義。

二、Model

在Django中model是你數據的單一、明確的信息來源。它包含了你存儲的數據的重要字段和行為。通常,一個模型(model)映射到一個數據庫表,

基本情況:

  • 每個模型都是一個Python類,它是django.db.models.Model的子類。
  • 模型的每個屬性都代表一個數據庫字段。
  • 綜上所述,Django為您提供了一個自動生成的數據庫訪問API。

下面這個例子定義了一個 Person 模型,包含 first_name 和 last_name

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_name 和 last_name 是模型的字段。每個字段被指定為一個類屬性,每個屬性映射到一個數據庫列。

上面的 Person 模型將會像這樣創建一個數據庫表:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

一些說明:

  • 表myapp_person的名稱是自動生成的,如果你要自定義表名,需要在model的Meta類中指定 db_table 參數,強烈建議使用小寫表名,特別是使用MySQL作為后端數據庫時。
  • id字段是自動添加的,如果你想要指定自定義主鍵,只需在其中一個字段中指定 primary_key=True 即可。如果Django發現你已經明確地設置了Field.primary_key,它將不會添加自動ID列。
  • 本示例中的CREATE TABLE SQL使用PostgreSQL語法進行格式化,但值得注意的是,Django會根據配置文件中指定的數據庫后端類型來生成相應的SQL語句。
  • Django支持MySQL5.5及更高版本。

三、Django ORM 常用字段和參數

1、常用字段

1)AutoField

int自增列,必須填入參數 primary_key=True。當model中如果沒有自增列,則自動會創建一個列名為id的列。

2)IntegerField

一個整數類型,范圍在 -2147483648 to 2147483647。

3)CharField

字符類型,必須提供max_length參數, max_length表示字符長度。

4)DateField

日期字段,日期格式  YYYY-MM-DD,相當於Python中的datetime.date()實例。

5)DateTimeField

日期時間字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相當於Python中的datetime.datetime()實例。

6)字段集合

   AutoField(Field)
        - int自增列,必須填入參數 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必須填入參數 primary_key=True

        注:當model中如果沒有自增列,則自動會創建一個列名為id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自動創建一個列名為id的且為自增的整數列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定義自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整數 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整數 0 ~ 32767
    IntegerField(Field)
        - 整數列(有符號的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整數 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807

    BooleanField(Field)
        - 布爾值類型

    NullBooleanField(Field):
        - 可以為空的布爾值

    CharField(Field)
        - 字符類型
        - 必須提供max_length參數, max_length表示字符長度

    TextField(Field)
        - 文本類型

    EmailField(CharField):
        - 字符串類型,Django Admin以及ModelForm中提供驗證機制

    IPAddressField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 IPV4 機制

    GenericIPAddressField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
        - 參數:
            protocol,用於指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定為True,則輸入::ffff:192.0.2.1時候,可解析為192.0.2.1,開啟此功能,需要protocol="both"

    URLField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 URL

    SlugField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證支持 字母、數字、下划線、連接符(減號)

    CommaSeparatedIntegerField(CharField)
        - 字符串類型,格式必須為逗號分割的數字

    UUIDField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供對UUID格式的驗證

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能
        - 參數:
                path,                      文件夾路徑
                match=None,                正則匹配
                recursive=False,           遞歸下面的文件夾
                allow_files=True,          允許文件
                allow_folders=False,       允許文件夾

    FileField(Field)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage
            width_field=None,   上傳圖片的高度保存的數據庫字段名(字符串)
            height_field=None   上傳圖片的寬度保存的數據庫字段名(字符串)

    DateTimeField(DateField)
        - 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 時間格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 長整數,時間間隔,數據庫中按照bigint存儲,ORM中獲取的值為datetime.timedelta類型

    FloatField(Field)
        - 浮點型

    DecimalField(Field)
        - 10進制小數
        - 參數:
            max_digits,小數總長度
            decimal_places,小數位長度

    BinaryField(Field)
        - 二進制類型
字段集合

7)自定義字段

class UnsignedIntegerField(models.IntegerField):
    def db_type(self, connection):
        return 'integer UNSIGNED'

自定義char類型字段:

復制代碼
class FixedCharField(models.Field):
    """
    自定義的char類型的字段類
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)

    def db_type(self, connection):
        """
        限定生成數據庫表的字段類型為char,長度為max_length指定的值
        """
        return 'char(%s)' % self.max_length


class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定義的char類型的字段
    cname = FixedCharField(max_length=25)
復制代碼

創建的表結構:

8)ORM字段與數據庫實際字段的對應關系

對應關系:
    'AutoField': 'integer AUTO_INCREMENT',
    'BigAutoField': 'bigint AUTO_INCREMENT',
    'BinaryField': 'longblob',
    'BooleanField': 'bool',
    'CharField': 'varchar(%(max_length)s)',
    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    'DateField': 'date',
    'DateTimeField': 'datetime',
    'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
    'DurationField': 'bigint',
    'FileField': 'varchar(%(max_length)s)',
    'FilePathField': 'varchar(%(max_length)s)',
    'FloatField': 'double precision',
    'IntegerField': 'integer',
    'BigIntegerField': 'bigint',
    'IPAddressField': 'char(15)',
    'GenericIPAddressField': 'char(39)',
    'NullBooleanField': 'bool',
    'OneToOneField': 'integer',
    'PositiveIntegerField': 'integer UNSIGNED',
    'PositiveSmallIntegerField': 'smallint UNSIGNED',
    'SlugField': 'varchar(%(max_length)s)',
    'SmallIntegerField': 'smallint',
    'TextField': 'longtext',
    'TimeField': 'time',
    'UUIDField': 'char(32)',
對應關系

2、字段參數

1)null

用於表示某個字段可以為空。

2)unique

如果設置為unique=True 則該字段在此表中必須是唯一的 。

3)db_index

如果db_index=True 則代表着為此字段設置索引。增加查詢速度。

4)default

為該字段設置默認值。

5)DateField和DateTimeField

auto_now_add:配置auto_now_add=True,第一次創建數據記錄的時候會把當前時間添加到數據庫。

auto_now:配置上auto_now=True,每次更新數據記錄的時候會更新該字段。

 四、ORM必知必會單表查詢13條

1、必知必會13條

復制代碼
<1> all():                 查詢所有結果
 
<2> filter(**kwargs):      它包含了與所給篩選條件相匹配的對象
 
<3> get(**kwargs):         返回與所給篩選條件相匹配的對象,返回結果有且只有一個,如果符合篩選條件的對象超過一個或者沒有都會拋出錯誤。
 
<4> exclude(**kwargs):     它包含了與所給篩選條件不匹配的對象
 
<5> values(*field):        返回一個ValueQuerySet——一個特殊的QuerySet,運行后得到的並不是一系列model的實例化對象,而是一個可迭代的字典序列
 
<6> values_list(*field):   它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列
 
<7> order_by(*field):      對查詢結果排序
 
<8> reverse():             對查詢結果反向排序,請注意reverse()通常只能在具有已定義順序的QuerySet上調用(在model類的Meta中指定ordering或調用order_by()方法)。
 
<9> distinct():            從返回結果中剔除重復紀錄(如果你查詢跨越多個表,可能在計算QuerySet時得到重復的結果。此時可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
 
<10> count():              返回數據庫中匹配查詢(QuerySet)的對象數量。
 
<11> first():              返回第一條記錄
 
<12> last():               返回最后一條記錄
 
<13> exists():             如果QuerySet包含數據,就返回True,否則返回False
復制代碼

示例:

復制代碼
# 如何在一個py文件中 使用Django項目的相關配置或內容
import os

# 右鍵運行時執行此文件的代碼,寫腳本可以這樣寫
if __name__ == '__main__':
    # 找到當前Django環境的所有配置
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
    import django

    django.setup()

    from app01 import models

    # 查詢age=18的第一個人,返回一個對象
    # (通常根據唯一的值進行查詢 查詢結果多余一個時,會報錯,通常根據pk(主鍵)、unique來查詢)
    # 通常使用get時,可以使用try來捕獲異常,這樣就不會報錯了
    ret = models.Person.objects.get(age=18)
    print(ret)
    # 查詢age=18的所有人, 返回一個QuerySet列表,查詢不到結果返回null
    ret = models.Person.objects.filter(age=18)
    print(ret[0])  # 可以根據索引取值

    # 查詢age=18的所有人,返回QuerySet列表,元素為每個人包含age與phone字段的字典
    ret = models.Person.objects.filter(age=18).values("age", "phone")
    print(ret)

    # 查詢age=18的所有人,返回QuerySet列表,元素為每個人包含age與phone值的元組
    ret = models.Person.objects.filter(age=18).values_list("age", "phone")
    print(ret)

    # 按照年齡排序
    ret = models.Person.objects.all().order_by("age")
    print(ret)
復制代碼

分類:

復制代碼
分類:
1. 返回QuerySet列表的有哪一些?
    1. all()
    2. filter()
    3. exclude()
    4. order_by()
    5. reverse()
    6. distinct()
                        
    7. values('字段名', ...)     --> 查詢結果的列表里,都是字典
    8. values_list(字段名', ...) --> 查詢結果的列表里,都是元祖
                        
2. 返回具體對象的
    1. first()
    2. last()
    3. get()

3. 返回數字的
    1. count()

4. 返回布爾值
    1. exists()
復制代碼

2、單表查詢之神奇的雙下划線

復制代碼
# 如何在一個py文件中 使用Django項目的相關配置或內容
import os

# 右鍵運行時執行此文件的代碼,寫腳本可以這樣寫
if __name__ == '__main__':
    # 找到當前Django環境的所有配置
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
    import django

    django.setup()

    from app01 import models
    

        # 查詢年齡大於18歲,id大於1的(大於age__gt、小於age__lt、小於等於age__lte)
    ret = models.Person.objects.filter(age__gt=18, id__gt=1)
    print(ret)

    # 查詢id值在 [1, 2]的人
    ret = models.Person.objects.filter(id__in=[1, 2, 20000])
    print(ret)

    # 查詢id值不在[1,2]的人
    ret = models.Person.objects.exclude(id__in=[1,2])
    print(ret)

    # 查詢名字中包含 JJ 的那個人
    ret = models.Person.objects.filter(name__contains="JJ")
    print(ret)
    
    # 不區分大小寫查詢
    ret = models.Person.objects.filter(name__icontains="jj")
    print(ret)

    # 查詢id在1-3區間內的數據
    ret = models.Person.objects.filter(id__range=[1, 3])
    print(ret)

    # 查詢以JJ結尾的人
    ret = models.Person.objects.filter(name__endswith='JJ')
    print(ret)
    print(models.Person.objects.first().birthday)
    # 查詢 生日 是 2018年的所有人
    ret = models.Person.objects.filter(birthday__year=2018)
    print(ret)
復制代碼

 3、外鍵ForeignKey

1)示例

modles.py:

from django.db import models


# 出版社表
class Publisher(models.Model):
    name = models.CharField(max_length=12)


# 書籍表
class Book(models.Model):
    title = models.CharField(max_length=32)
    # on_delete=models.CASCADE 級聯刪除 當出版社刪除時,關聯的書籍也刪除
    # Django1.1 默認為級聯刪除  Django2.0需要明確此選項on_delete=models.CASCADE 否則運行時會報錯
    # 當orm創建數據庫表時, 會自動在外鍵設置的字段上加上publisher_id
    publisher = models.ForeignKey(to="Publisher", on_delete=models.CASCADE)
modles.py

urls.py:

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^book_list/$', views.book_list),
    url(r'^add_book/$', views.AddBook.as_view()),
    url(r'^delete_book/(?P<pk>\d+)/$', views.DeleteBook.as_view()),
    url(r'^edit_book/(?P<pk>\d+)/$', views.EditBook.as_view()),
]
urls.py

views.py:

from django.shortcuts import render, redirect, HttpResponse
from django.http import JsonResponse
from app01 import models
from django.urls import reverse
from django import views


def publisher_list(request):
    print(request.path_info)
    data = models.Publisher.objects.all()
    return render(request, "publisher_list.html", {"publisher_list": data})


def book_list(request):
    # 去數據庫查詢所有的書籍
    data = models.Book.objects.all()
    return render(request, "book_list.html", {"book_list": data})


class AddBook(views.View):

    def get(self, request):
        data = models.Publisher.objects.all()
        return render(request, "add_book.html", {"publisher_list": data})

    def post(self, request):
        book_name = request.POST.get("title")
        publisher_id = request.POST.get("publisher")
        # publisher_obj = models.Publisher.objects.get(id=publisher_id)
        # 創建書籍
        models.Book.objects.create(
            title=book_name,
            publisher_id=publisher_id
            # publisher=publisher_obj
        )
        return redirect("/book_list/")


class DeleteBook(views.View):
    def get(self, request, pk):
        models.Book.objects.filter(id=pk).delete()
        return redirect("/book_list/")


class EditBook(views.View):
    def get(self, request, pk):
        book_obj = models.Book.objects.get(id=pk)
        publisher_list = models.Publisher.objects.all()
        return render(request, "edit_book.html", {"book": book_obj, "publisher_list": publisher_list})

    def post(self, request, pk):
        book_obj = models.Book.objects.get(id=pk)
        new_title = request.POST.get("title")
        new_publisher_id = request.POST.get("publisher")
        # 更新
        book_obj.title = new_title
        book_obj.publisher_id = new_publisher_id
        # 同步到數據庫
        book_obj.save()
        return redirect("/book_list/")
Views.py

huml頁面代碼:

{#book_list.html#}

<table border="1">
    <thead>
    <tr>
        <th>#</th>
        <th>書名</th>
        <th>出版社名稱</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
        {% for book in book_list %}
            <tr>
            <td>{{ forloop.counter }}</td>
            <td>{{ book.title }}</td>
            <td>{{ book.publisher.name }}</td>
            <td>
                <a href="/delete_book/{{ book.id }}/">刪除</a>
                <a href="/edit_book/{{ book.id }}/">編輯</a>
            </td>
            </tr>
        {% endfor %}
    </tbody>
</table>

{#edit_book.html#}

<form action="" method="post">
{% csrf_token %}
    <input type="text" name="title" value="{{ book.title }}">
    <select name="publisher">
        {% for publisher in publisher_list %}

            {% if book.publisher == publisher %}
                <option selected value="{{ publisher.id }}">{{ publisher.name }}</option>
            {% else %}
                <option value="{{ publisher.id }}">{{ publisher.name }}</option>
            {% endif %}
        {% endfor %}
    </select>
    <input type="submit" value="提交">
</form>

{#add_book.html#}

<form action="" method="post">
{% csrf_token %}
    <input type="text" name="title">
    <select name="publisher">
        {% for publisher in publisher_list %}
            <option value="{{ publisher.id }}">{{ publisher.name }}</option>
        {% endfor %}
    </select>
    <input type="submit" value="提交">
</form>
html頁面代碼

2)正常查找

對象查找(跨表):

語法:對象.關聯字段.字段

字段查找(跨表):

語法:關聯字段__字段

3)反向查找

對象查找:

語法:obj.表名_set

字段查找:

語法:表名__字段

示例:

復制代碼
    # 查詢第一本書關聯的出版社的名字
    # 1. 基於對象的查詢
    book_obj = models.Book.objects.first()
    ret = book_obj.publisher.name
    print(ret)
    # 2. 基於queryset的雙下划線查詢,雙下划線表示跨表
    ret = models.Book.objects.all().values_list("publisher__name").distinct()
    print(ret)

    # 反向查詢
    # 1. 由出版社反向查詢書籍(基於對象的查詢)
    publisher_obj = models.Publisher.objects.get(id=2)  # 找到張江出版社
    # 張江出版社出版的所有書籍
    ret = publisher_obj.book_set.all()
    print(ret)

    # 2. 基於queryset的雙下划線
    # 江出版社出版的所有書籍的書名
    ret = models.Publisher.objects.filter(id=2).values_list("book__title")
    print(ret)
復制代碼

 


免責聲明!

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



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