W3docs

Вставка документов в 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() — возвращают объекты результата с полезными свойствами:

Объект результатаКлючевые свойства
InsertOneResultinserted_id, acknowledged
InsertManyResultinserted_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 — использование операторов сравнения и логических операторов для фильтрации результатов.
Was this page helpful?