Category: Tech articles

  • MongoDB + Django

    \"mongodb\"
    mongoDB — документо-ориентированная система управления базами данных (СУБД) с открытым исходным кодом, не требующая описания схемы таблиц. Написана на языке C++ и распространяется в рамках лицензии Creative Commons.

    В последнее время становится довольно популярной и восстребованой. И вот возникла идея использовать ее в связке с фреймворком Django. Собственно о чем далее и пойдет речь.

    Для решения поставленной задачи мы будем использовать приложение mongodb-engine. Данное приложение тесно связано еще с несколькими приложениями, установкой которых мы и займемся вначале.

    Django-nonrel — используется для поддержки NoSQL в Django.

    pip install hg+https://bitbucket.org/wkornewald/django-nonrel

    djangotoolbox — набор инструментов для работы с нереляционными базами данных, лишним не будет.

    pip install hg+https://bitbucket.org/wkornewald/djangotoolbox

    А теперь уже ставим и mongodb-engine:

    pip install git+https://github.com/django-nonrel/mongodb-engine

    Указываем нашу базу данных в settings:

    DATABASES = {
       \'default\' : {
          \'ENGINE\' : \'django_mongodb_engine\',
          \'NAME\' : \'my_database\'
       }
    }

    При необходимости также можно указать host, port, user, password.

    Данное приложение предоставляет два типа полей для хранения произвольных данных, не входящих в стандартную django модель.

    ListField

    Списки и им подобные, представление массивов в формате BSON

    from djangotoolbox.fields import ListField
    
    class Post(models.Model):
        ...
        tags = ListField()
    >>> Post(tags=[\'django\', \'mongodb\'], ...).save()
    >>> Post.objecs.get(...).tags
    [\'django\', \'mongodb\']

    Вариант с указанием типа:

    class Post(models.Model):
        ...
        edited_on = ListField(models.DateTimeField())
    >>> post = Post(edited_on=[\'1010-10-10 10:10:10\'])
    >>> post.save()
    >>> Post.objects.get(...).edited_on
    [datetime.datetime([1010, 10, 10, 10, 10, 10])]

    Данный тип поля удобно использовать для организации связи один-ко-многим:

    from djangotoolbox.fields import EmbeddedModelField, ListField
    
    class Post(models.Model):
        ...
        comments = ListField(EmbeddedModelField(\'Comment\'))
    
    class Comment(models.Model):
        ...
        text = models.TextField()

    EmbeddedModelField — используется для организации связей между моделями.

    DictField

    Второй тип поля DictField, который используется в BSON для обьектов.

    from djangotoolbox.fields import DictField
    
    class Image(models.Model):
        ...
        exif = DictField()
    >>> Image(exif=get_exif_data(...), ...).save()
    >>> Image.objects.get(...).exif
    {u\'camera_model\' : \'Spamcams 4242\', \'exposure_time\' : 0.3, ...}

    Вариант с указанием типа:

    class Poll(models.Model):
        ...
        votes = DictField(models.IntegerField())
    >>> Poll(votes={\'bob\' : 3.14, \'alice\' : \'42\'}, ...).save()
    >>> Poll.objects.get(...).votes
    {u\'bob\' : 3, u\'alice\' : 42}
    Обновление данных
    Post.objects.filter(...).update(title=\'Everything is the same\')

    Можно использовать для обновления оператор $set

    .update(..., {\'$set\': {\'title\': \'Everything is the same\'}})

    А также функцию F()

    Post.objects.filter(...).update(visits=F(\'visits\')+1)

    В результате получится что то такое:

    .update(..., {\'$inc\': {\'visits\': 1}})
    Использование низко-уровневых запросов

    Если Вам не хватает возможностей Django ORM, можно использовать запросы к MongoDB минуя стандартный механизм.

    raw_query() — принимает один аргумент, возвращает данные в виде стандартного Django queryset. Что хорошо для дальнейшей обработки данных.

    Пример с geo данными, модель:

    from djangotoolbox.fields import EmbeddedModelField
    from django_mongodb_engine.contrib import MongoDBManager
    
    class Point(models.Model):
        latitude = models.FloatField()
        longtitude = models.FloatField()
    
    class Place(models.Model):
        ...
        location = EmbeddedModelField(Point)
    
        objects = MongoDBManager()

    получим все точки рядом с конкретными координатами:

    >>> here = {\'latitude\' : 42, \'longtitude\' : 3.14}
    >>> Place.objects.raw_query({\'location\' : {\'$near\' : here}})

    raw_update() — используется если нам недостаточно стандартных средств для обновления данных.

    Модель:

    from django_mongodb_engine.contrib import MongoDBManager
    
    class FancyNumbers(models.Model):
        foo = models.IntegerField()
    
        objects = MongoDBManager()

    использование:

    FancyNumbers.objects.raw_update({}, {\'$bit\' : {\'foo\' : {\'or\' : 42}}})

    В данном примере выполняется побитовое or для каждого foo в базе.

    На этом возможности данной связки не заканчиваются, но если пречислять все, то статья не оправдано затянется. Полное описание и примеры можно будет посмотреть по представленным ниже ссылкам.

    Ссылки:
    MongoDB
    mongodb-engine
    GitHub