在這篇文章中,將向您展現如何使用Python連接目前主流的MongoDB(V3.4.0)數據庫,主要使用PyMongo(v3.4.0)和MongoEngine(V0.10.7)。同時比較SQL和NoSQL。html
英文原文:https://realpython.com/blog/python/introduction-to-mongodb-and-pythonpython
若是你不是很熟悉NoSQL這個概念,MongoDB就是一個NoSQL數據庫。近幾年來它愈來愈受到整個行業的歡迎。NoSQL數據庫提供了一個和關係型數據庫很是不一樣的檢索方式和存儲數據功能。正則表達式
在NoSQL出現的幾十年來,SQL數據庫是開發者尋求構建大型、可擴展系統的惟一選擇之一。然而,愈來愈多的需求要求存儲複雜數據結構的能力。這推進了NoSQL數據庫的誕生,它容許開發者存儲異構和無結構的數據。redis
當到數據庫方案選擇時,大多數人都問本身最後一個問題,「SQL或NoSQL的?」。不管是SQL和NoSQL都有本身的長處和弱點,你應該選擇適合您的應用需求中最好的之一。這裏是二者之間的一些區別:mongodb
模型是關係型的;數據庫
數據被存放在表中;apache
適用於每條記錄都是相同類型並具備相同屬性的狀況;api
存儲規範須要預約義結構;數組
添加新的屬性意味着你必須改變總體架構;服務器
ACID事務支持;
模型是非關係型的;
能夠存儲Json、鍵值對等(決定於NoSQL數據庫類型);
並非每條記錄都要有相同的結構;
添加帶有新屬性的數據時,不會影響其餘;
支持ACID事務,根據使用的NoSQL的數據庫而有所不一樣;
一致性能夠改變;
橫向擴展;
在兩種類型的數據庫之間還有許多其餘的區別,但上面提到的是一些更重要的區別。根據您的具體狀況,使用SQL數據庫多是首選,而在其餘狀況下,NoSQL的是更明顯的選擇。當選擇一個數據庫時,您應該謹慎考慮每一個數據庫的優點和劣勢。
NoSQL的一個好處是,有許多不一樣類型的數據庫可供選擇,而且每一個都有本身的用例:
還有不少,但這些是一些更常見的類型。近年來,SQL和NoSQL數據庫甚至已經開始合併。例如,PostgreSQL如今支持存儲和查詢JSON數據,很像MongoDB。有了這個,你能夠用Postgres實現MongoDB同樣的功能,但你仍然沒有MongoDB的其餘優點(如橫向擴容和簡單的界面,等等)。
如今,讓咱們將視線轉移到本文的重點,並闡明的MongoDB的具體的一些狀況。
MongoDB是一個面向文檔的,開源數據庫程序,它平臺無關。MongoDB像其餘一些NoSQL數據庫(但不是所有!)使用JSON結構的文檔存儲數據。這是使得數據很是靈活,不須要的Schema。
一些比較重要的特色是:
支持多種標準查詢類型,好比matching()、comparison (, )或者正則表達式;
能夠存儲幾乎任何類型的數據,不管是結構化,部分結構化,甚至是多態;
要擴展和處理更多查詢,只需添加更多的機器;
它是高度靈活和敏捷,讓您可以快速開發應用程序;
做爲基於文檔的數據庫意味着您能夠在單個文檔中存儲有關您的模型的全部信息;
您能夠隨時更改數據庫的Schema;
許多關係型數據庫的功能也能夠在MongoDB使用(如索引)。
在運行方面,MongoDB中有至關多的功能在其餘數據庫中是沒有的:
不管您須要獨立服務器仍是完整的獨立服務器集羣,MongoDB均可以根據須要進行擴展;
MongoDB還經過在各個分片上自動移動數據來提供負載均衡支持;
它具備自動故障轉移支持,若是主服務器Down掉,新的主服務器將自動啓動並運行;
MongoDB的管理服務(MMS)能夠用於監控和備份MongoDB的基礎設施服務;
不像關係數據庫,因爲內存映射文件,你將節省至關多的RAM。
雖然起初MongoDB彷佛是解決咱們許多問題的數據庫,但它不是沒有缺點的。MongoDB的一個常見缺點是缺乏對ACID事務的支持,MongoDB在特定場景下支持ACID事務,但不是在全部狀況。在單文檔級別,支持ACID事務(這是大多數事務發生的地方)。可是,因爲MongoDB的分佈式性質,不支持處理多個文檔的事務。
MongoDB還缺乏對天然join查詢支持。在MongoDB看來:文檔意在一應俱全,這意味着,通常來講,它們不須要參考其餘文檔。在現實世界中,這並不老是有效的,由於咱們使用的數據是關係性的。所以,許多人認爲MongoDB應該被用做一個SQL數據庫的補充數據庫,可是當你使用MongoDB是,你會發現這是錯誤的。
如今咱們已經描述了MongoDB的是什麼,讓咱們來看看如何在Python中實際使用它。由MongoDB開發者發佈的官方驅動程序PyMongo,這裏經過一些例子介紹,但你也應該查看完整的文檔,由於咱們沒法面面俱到。
固然第一件事就是安裝,最簡單的方式就是pip
:
pip install pymongo==3.4.0
注:有關更全面的指南,請查看文檔的安裝/升級頁面,並按照其中的步驟進行設置
完成設置後,啓動的Python控制檯並運行如下命令:
>>> import pymongo
若是沒有提出任何異常就說明安裝成功了
使用MongoClient
對象創建鏈接:
from pymongo import MongoClient client = MongoClient()
使用上面的代碼片斷,將創建鏈接到默認主機(localhost)和端口(27017)。您還能夠指定主機和/或使用端口:
client = MongoClient('localhost', 27017)
或者使用MongoURl格式:
client = MongoClient('mongodb://localhost:27017')
一旦你有一個鏈接的MongoClient
實例,你能夠在Mongo服務器中訪問任何數據庫。若是要訪問一個數據庫,你能夠看成屬性同樣訪問:
db = client.pymongo_test
或者你也可使用字典形式的訪問:
db = client['pymongo_test']
若是您的指定數據庫已建立,實際上並不重要。經過指定此數據庫名稱並將數據保存到其中,您將自動建立數據庫。
在數據庫中存儲數據,就如同調用只是兩行代碼同樣容易。第一行指定你將使用哪一個集合。在MongoDB中術語中,一個集合是在數據庫中存儲在一塊兒的一組文檔(至關於SQL的表)。集合和文檔相似於SQL表和行。第二行是使用集合插入數據insert_one()的方法:
posts = db.posts post_data = { 'title': 'Python and MongoDB', 'content': 'PyMongo is fun, you guys', 'author': 'Scott' } result = posts.insert_one(post_data) print('One post: {0}'.format(result.inserted_id))
咱們甚至可使用insert_one()同時插入不少文檔,若是你有不少的文檔添加到數據庫中,可使用方法insert_many()。此方法接受一個list參數:
post_1 = { 'title': 'Python and MongoDB', 'content': 'PyMongo is fun, you guys', 'author': 'Scott' } post_2 = { 'title': 'Virtual Environments', 'content': 'Use virtual environments, you guys', 'author': 'Scott' } post_3 = { 'title': 'Learning Python', 'content': 'Learn Python, it is easy', 'author': 'Bill' } new_result = posts.insert_many([post_1, post_2, post_3]) print('Multiple posts: {0}'.format(new_result.inserted_ids))
你應該看到相似輸出:
One post: 584d947dea542a13e9ec7ae6 Multiple posts: [ ObjectId('584d947dea542a13e9ec7ae7'), ObjectId('584d947dea542a13e9ec7ae8'), ObjectId('584d947dea542a13e9ec7ae9') ]
注意: 不要擔憂,你和上面顯示不同。它們是在插入數據時,由Unix的紀元,機器標識符和其餘惟一數據組成的動態標識。
檢索文檔可使用find_one()方法,好比要找到author爲Bill的記錄:
bills_post = posts.find_one({'author': 'Bill'}) print(bills_post) 運行結果: { 'author': 'Bill', 'title': 'Learning Python', 'content': 'Learn Python, it is easy', '_id': ObjectId('584c4afdea542a766d254241') }
您可能已經注意到,這篇文章的ObjectId是設置的_id,這是之後可使用惟一標識。若是須要查詢多條記錄可使用find()方法:
scotts_posts = posts.find({'author': 'Scott'}) print(scotts_posts) 結果: <pymongo.cursor.Cursor object at 0x109852f98>
他的主要區別在於文檔數據不是做爲數組直接返回給咱們。相反,咱們獲得一個遊標對象的實例。這Cursor是一個包含至關多的輔助方法,以幫助您處理數據的迭代對象。要得到每一個文檔,只需遍歷結果:
for post in scotts_posts: print(post)
雖然PyMongo是很是容易使用,整體上是一個偉大的輪子,可是許多項目使用它均可能過低水平。簡而言之,你必須編寫不少本身的代碼來持續地保存,檢索和刪除對象。PyMongo之上提供了一個更高的抽象一個庫是MongoEngine。MongoEngine是一個對象文檔映射器(ODM),它大體至關於一個基於SQL的對象關係映射器(ORM)。MongoEngine提供的抽象是基於類的,因此你建立的全部模型都是類。雖然有至關多的Python的庫能夠幫助您使用MongoDB,MongoEngine是一個更好的,由於它有一個很好的組合的功能,靈活性和社區支持。
使用pip安裝:
pip install mongoengine==0.10.7
鏈接:
from mongoengine import * connect('mongoengine_test', host='localhost', port=27017)
和pymongo不一樣。MongoEngine須要制定數據庫名稱。
創建文檔以前,須要定義文檔中要存放數據的字段。與許多其餘ORM相似,咱們將經過繼承Document類,並提供咱們想要的數據類型來作到這一點:
import datetime class Post(Document): title = StringField(required=True, max_length=200) content = StringField(required=True) author = StringField(required=True, max_length=50) published = DateTimeField(default=datetime.datetime.now)
在這個簡單的模型中,咱們已經告訴MongoEngine,咱們的Post實例有title、content、author、published。如今Document對象可使用該信息來驗證咱們提供它的數據。
所以,若是咱們試圖保存Post的中沒有title那麼它會拋出一個Exception,讓咱們知道。咱們甚至能夠進一步利用這個並添加更多的限制:
required:設置必須;
default:若是沒有其餘值給出使用指定的默認值
unique:確保集合中沒有其餘document有此字段的值相同
choices:確保該字段的值等於數組中的給定值之一
將文檔保存到數據庫中,咱們將使用save()的方法。若是文檔中的數據庫已經存在,則全部的更改將在原子水平上對現有的文檔進行。若是它不存在,可是,那麼它會被建立。
這裏是建立和保存一個文檔的例子:
post_1 = Post( title='Sample Post', content='Some engaging content', author='Scott' ) post_1.save() # This will perform an insert print(post_1.title) post_1.title = 'A Better Post Title' post_1.save() # This will perform an atomic edit on "title" print(post_1.title)
調用save()的時候須要注意幾點:
PyMongo將在您調用.save()時執行驗證,這意味着它將根據您在類中聲明的模式檢查要保存的數據,若是違反模式(或約束),則拋出異常而且不保存數據;
因爲Mongo不支持真正的事務,所以沒有辦法像在SQL數據庫中那樣「回滾」.save()調用。
當你保存的數據沒有title時:
post_2 = Post(content='Content goes here', author='Michael') post_2.save() raise ValidationError(message, errors=errors) mongoengine.errors.ValidationError: ValidationError (Post:None) (Field is required: ['title'])
使用MongoEngine是面向對象的,你也能夠添加方法到你的子類文檔。例以下面的示例,其中函數用於修改默認查詢集(返回集合的全部對象)。經過使用它,咱們能夠對類應用默認過濾器,並只獲取所需的對象
class Post(Document): title = StringField() published = BooleanField() @queryset_manager def live_posts(clazz, queryset): return queryset.filter(published=True)
您還可使用ReferenceField對象來建立從一個文檔到另外一個文檔的引用。MongoEngine在訪問時自動惰性處理引用。
class Author(Document): name = StringField() class Post(Document): author = ReferenceField(Author) Post.objects.first().author.name
在上面的代碼中,使用文檔」外鍵」,咱們能夠很容易地找到第一篇文章的做者。其實還有比這裏介紹的更多的字段類(和參數),因此必定要查看文檔字段更多信息。
從全部這些示例中,您應該可以看到,MongoEngine很是適合管理幾乎任何類型的應用程序的數據庫對象。這些功能使得建立一個高效可擴展程序變得很是容易。若是你正在尋找更多關於MongoEngine的幫助,請務必查閱他們的用戶指南。