Вставка документов в MongoDB
Вставка одного и нескольких документов в MongoDB на Python с pymongo: ObjectId, обработка ошибок и массовая вставка.
MongoDB хранит данные в виде документов — гибких, JSON-подобных объектов, которые могут содержать вложенные поля и массивы. В этой главе рассказывается, как вставлять по одному документу с помощью insert_one(), вставлять несколько документов за один вызов с помощью insert_many(), разобраться с автоматически генерируемым полем _id, обрабатывать ошибки дублирующихся ключей и выбирать между упорядоченной и неупорядоченной массовой вставкой.
Предварительные требования
- Python 3.8 или выше.
- Запущенный сервер MongoDB (локальный или удалённый). Если вы ещё не настроили его, см. MongoDB Get Started.
- Готовые к использованию база данных и коллекция. При необходимости обратитесь к MongoDB Create Database и MongoDB Create Collection.
- Установленный драйвер
pymongo:
pip install pymongoПодключение к MongoDB
Импортируйте MongoClient и откройте соединение перед выполнением операций вставки. Если MongoDB запущен локально с настройками по умолчанию, можно вызвать MongoClient() без аргументов:
from pymongo import MongoClient
client = MongoClient() # connects to localhost:27017
db = client["bookstore"] # database (created on first write)
books = db["books"] # collection (created on first write)Для подключения к удалённому серверу или Atlas передайте URI соединения:
client = MongoClient("mongodb://username:password@hostname:27017/")MongoClient поддерживает пул соединений внутри, поэтому следует создавать один клиент на приложение и использовать его во всех операциях.
Вставка одного документа с помощью insert_one()
insert_one() добавляет один документ в коллекцию и возвращает объект InsertOneResult. Наиболее полезное свойство этого объекта — inserted_id, содержащее _id, присвоенный новому документу.
from pymongo import MongoClient
client = MongoClient()
books = client["bookstore"]["books"]
document = {
"title": "The Pragmatic Programmer",
"author": "David Thomas",
"year": 1999,
"in_stock": True,
}
result = books.insert_one(document)
print("Inserted _id:", result.inserted_id)Пример вывода:
Inserted _id: 64b3e2c1f0a1234567890abcКонкретное значение _id будет отличаться при каждом запуске — MongoDB генерирует уникальный ObjectId, если вы не указываете собственный _id.
Поле _id
Каждый документ MongoDB должен иметь поле _id. Если оно не указано, драйвер автоматически генерирует значение bson.ObjectId. ObjectId — это 12-байтовое значение, которое кодирует:
- 4-байтовую метку времени Unix (в секундах),
- 5-байтовое случайное значение, уникальное для машины и процесса,
- 3-байтовый инкрементируемый счётчик.
Это означает, что значения ObjectId примерно упорядочены хронологически и глобально уникальны без какой-либо координации между серверами.
Вы можете задать собственный _id, если у вас есть естественный уникальный ключ (например, ISBN):
result = books.insert_one({
"_id": "978-0-13-468599-1",
"title": "The Pragmatic Programmer",
"author": "David Thomas",
"year": 1999,
})
print("Inserted _id:", result.inserted_id)
# Inserted _id: 978-0-13-468599-1Если вставить второй документ с тем же _id, MongoDB выдаст ошибку DuplicateKeyError (см. Обработка ошибок ниже).
Вставка нескольких документов с помощью insert_many()
insert_many() принимает список документов и вставляет их все за один сетевой запрос. Метод возвращает объект InsertManyResult, атрибут inserted_ids которого содержит список присвоенных значений _id в порядке вставки.
from pymongo import MongoClient
client = MongoClient()
books = client["bookstore"]["books"]
new_books = [
{"title": "Clean Code", "author": "Robert C. Martin", "year": 2008},
{"title": "Refactoring", "author": "Martin Fowler", "year": 1999},
{"title": "Design Patterns", "author": "Gang of Four", "year": 1994},
]
result = books.insert_many(new_books)
print("Inserted IDs:", result.inserted_ids)Пример вывода:
Inserted IDs: [ObjectId('...'), ObjectId('...'), ObjectId('...')]Упорядоченная и неупорядоченная вставка
По умолчанию insert_many() работает в упорядоченном режиме: документы вставляются один за другим в порядке списка, и обработка останавливается на первой ошибке.
Передайте ordered=False для неупорядоченного режима: MongoDB обрабатывает каждый документ независимо и собирает все ошибки перед тем, как выбросить исключение. Это быстрее при больших пакетах, когда вы ожидаете дубликаты и хотите пропустить проблемные документы, а не прерывать весь пакет.
from pymongo import MongoClient
from pymongo.errors import BulkWriteError
client = MongoClient()
books = client["bookstore"]["books"]
# Two documents with duplicate _id values mixed in
docs = [
{"_id": 1, "title": "Book A"},
{"_id": 2, "title": "Book B"},
{"_id": 1, "title": "Duplicate — will fail"}, # duplicate _id
{"_id": 3, "title": "Book C"},
]
try:
result = books.insert_many(docs, ordered=False)
print("Inserted:", result.inserted_ids)
except BulkWriteError as e:
# inserted_ids still shows the documents that succeeded
print("Some inserts failed:", e.details["nInserted"], "succeeded")
for err in e.details["writeErrors"]:
print(" Error on index", err["index"], "—", err["errmsg"])При ordered=False документы Book A, Book B и Book C вставляются, даже если дубликат не проходит. При ordered=True (по умолчанию) обработка остановится на третьем документе, и Book C никогда не будет вставлен.
Обработка ошибок
Ошибка дублирующегося ключа
Вставка документа, у которого _id (или любое поле, покрытое уникальным индексом) уже существует, вызывает pymongo.errors.DuplicateKeyError:
from pymongo import MongoClient
from pymongo.errors import DuplicateKeyError
client = MongoClient()
books = client["bookstore"]["books"]
try:
books.insert_one({"_id": "isbn-001", "title": "First"})
books.insert_one({"_id": "isbn-001", "title": "Duplicate"}) # raises
except DuplicateKeyError as e:
print("Duplicate key:", e.details["keyValue"])Вывод:
Duplicate key: {'_id': 'isbn-001'}Ошибки подключения
MongoClient() завершается успешно, даже когда MongoDB не запущен — ошибка появляется только при выполнении реального запроса. Оборачивайте операции вставки в блок try/except для корректной обработки ошибок подключения:
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure, PyMongoError
client = MongoClient(serverSelectionTimeoutMS=3000)
try:
result = books.insert_one({"title": "Test"})
print("Inserted:", result.inserted_id)
except ConnectionFailure:
print("Could not reach MongoDB server.")
except PyMongoError as e:
print("MongoDB error:", e)Проверка результата
Оба метода — insert_one() и insert_many() — возвращают объекты результата с полезными свойствами:
| Объект результата | Ключевые свойства |
|---|---|
InsertOneResult | inserted_id, acknowledged |
InsertManyResult | inserted_ids (список), acknowledged |
acknowledged равно True, когда MongoDB подтвердила запись. Значение False возможно только при использовании неподтверждённого write concern (w=0), который пропускает подтверждение ради максимальной скорости, жертвуя гарантией успешности записи.
Полный рабочий пример
Следующий самодостаточный скрипт подключается к локальному серверу MongoDB, вставляет несколько документов и выводит результаты:
from pymongo import MongoClient
from pymongo.errors import DuplicateKeyError, BulkWriteError
DB_NAME = "demo_bookstore"
COL_NAME = "books"
def main():
client = MongoClient(serverSelectionTimeoutMS=3000)
# Verify connectivity
client.admin.command("ping")
print("Connected to MongoDB")
col = client[DB_NAME][COL_NAME]
col.drop() # start fresh for this demo
# --- insert_one ---
result = col.insert_one({
"_id": "isbn-001",
"title": "The Pragmatic Programmer",
"author": "David Thomas",
"year": 1999,
})
print("insert_one _id:", result.inserted_id)
# --- insert_many ---
result = col.insert_many([
{"title": "Clean Code", "author": "Robert C. Martin", "year": 2008},
{"title": "Refactoring", "author": "Martin Fowler", "year": 1999},
{"title": "Design Patterns", "author": "Gang of Four", "year": 1994},
])
print("insert_many IDs:", result.inserted_ids)
# --- duplicate key ---
try:
col.insert_one({"_id": "isbn-001", "title": "Duplicate"})
except DuplicateKeyError:
print("Caught DuplicateKeyError as expected")
# --- unordered bulk insert ---
docs = [
{"_id": "isbn-002", "title": "Book A"},
{"_id": "isbn-001", "title": "Dup — will fail"}, # duplicate
{"_id": "isbn-003", "title": "Book C"},
]
try:
col.insert_many(docs, ordered=False)
except BulkWriteError as e:
print("Bulk insert: succeeded =", e.details["nInserted"],
", failed =", len(e.details["writeErrors"]))
print("Total documents:", col.count_documents({}))
# Clean up
client.drop_database(DB_NAME)
if __name__ == "__main__":
main()Ожидаемый вывод:
Connected to MongoDB
insert_one _id: isbn-001
insert_many IDs: [ObjectId('...'), ObjectId('...'), ObjectId('...')]
Caught DuplicateKeyError as expected
Bulk insert: succeeded = 2 , failed = 1
Total documents: 6Типичные ошибки
PyMongo изменяет ваш документ
Когда вы передаёте обычный словарь в insert_one(), PyMongo добавляет ключ _id в исходный словарь:
doc = {"title": "My Book"}
col.insert_one(doc)
print(doc) # {'title': 'My Book', '_id': ObjectId('...')}Если вы планируете повторно использовать тот же словарь (например, в цикле), передавайте копию: col.insert_one(doc.copy()).
Массовые вставки: используйте insert_many(), а не цикл
Вставка 10 000 документов по одному создаёт 10 000 сетевых запросов. Используйте insert_many(), чтобы отправить их все за один раз — это на порядки быстрее при массовой загрузке.
Если список очень большой (миллионы документов), разбейте его на пакеты по несколько тысяч, чтобы не превысить ограничение в 48 МБ на размер BSON-документа для одного пакета.
Поля с датой требуют объектов datetime, а не строк
MongoDB хранит даты как BSON Date (миллисекунды с начала эпохи). Используйте datetime.datetime из Python для полей с датой, чтобы они корректно сохранялись и запрашивались:
from datetime import datetime
col.insert_one({"title": "New Book", "published": datetime(2024, 3, 15)})Следующие шаги
- MongoDB Find — запрос и фильтрация только что вставленных документов.
- MongoDB Update — изменение существующих документов.
- MongoDB Delete — удаление документов из коллекции.
- MongoDB Query — использование операторов сравнения и логических операторов для фильтрации результатов.