Публикации с меткой «orm»

Блог django на хабрахабре

Django и особенности использования транзакций в MySQL

Наверное всем известно, что Django является одним из самых популярных фреймворков для web-разработки на python-е. И даже если в основе web-проекта лежит сторонний код, то зачастую при разработке используют отдельные части этого фреймворка — например ORM. В данной статье я хотел бы рассказать об особенностях использования Django ORM при работе с базой данных MySQL, а именно про транзакции и подводные камни, связанные с ними. Так, например, если в какой-то момент вы осознаёте, что вместо ожидаемых данных, возвращается совершенно другой результат, то возможно, данная статья поможет разобраться что к чему.
Читать дальше →

Блог django на хабрахабре

Django Framework / Как не пересчитывать суммы и средние каждый раз

Представим, что у нас электронная платёжная система, а в ней в базе данных таблица операций. И мы хотим посчитать, например, какого размера средняя операция. Легко, вот запрос, только долго выполняется:

> SELECT avg(amount) FROM transfer;
65.125965782378
generated in 3850 seconds


А теперь представим, что показатель должен быть свежайшим, а записи в таблицу делаются каждую секунду, и за месяц их набираются миллионы. Или другие требования, но суть та же — агрегировать те же данные каждый раз очень затратно. Обычные базы данных не предлагают таких оптимизаций. Как быть?

Блог django на хабрахабре

Django Framework / Cacheops

Некоторое время назад я писал о системе кеширования. Помнится, я обещал продолжение, но сейчас решил, что строка кода лучше сотни комментариев, теорию оставим на потом. Поэтому сегодня у нас своего рода анонс с парой советов по использованию в одном флаконе. Встречайте, cacheops — система кеширования и автоматической инвалидации кеша для Django ORM.

Еще один блог о Django

Уменьшаем кол-во запросов, которые генерируются Django ORM

Думаю, для многих уже совсем не секрет, что делать, чтобы уменьшить кол-во запросов, которые генерируются при помощи Django ORM. Однако, столкнувшись в очередной раз с необходимостью сбавить обороты и ускорить генерацию страницы я захотел вынести все эти методы в один пост.

Лирическое отступление: Для того, чтоб лучше понять как эти методы работают, я задам легкую схему для моделей, упоминаемых в статье. Во-первых, это будет встроенная auth.User, затем это будет модель статьи, которая будет версионироваться при помощи django-reversion. Иными словами, наш models.py будет выглядеть как-то так:

from django.db import models
from django.utils.translation import ugettext_lazy as _

import reversion


class Article(models.Model):

    STATE_NEW, STATE_SUBMITTED, STATE_REJECTED, STATE_ACCEPTED = range(1, 5)
    STATE_CHOICES = (
        (STATE_NEW, _('New')),
        (STATE_SUBMITTED, _('Submitted')),
        (STATE_REJECTED, _('Rejected)),
        (STATE_ACCEPTED, _('Accepted)),
    )

    title = models.CharField(_('title'), max_length=64)
    content = models.TextField(_('content'))
    state = models.PositiveIntegerField(_('state'), choices=STATE_CHOICES,
        default=STATE_NEW)

    writer = models.ForeignKey('auth.User', related_name='articles_writer',
        verbose_name=_('writer'))
    editor = models.ForeignKey('auth.User', blank=True, null=True,
        related_name='articles_editor', verbose_name=_('editor'))

reversion.register(Article)

Метод 1. Использование select_related вместо all

Здесь все очень просто. Если у моделей есть не-нулевые FK поля Вы явно захотите получать данные в них без дополнительных запросов к БД. Потому если писатель статьи должен быть явно указан на сайте, то считывайте статьи перед паджинацией как,

articles = Article.objects.select_related()

а не .all(). На самом деле метод .all() хорош как по мне только для "нишевых" (с малым кол-вом или вообще без связей) моделей. В остальных случаях надо смотреть по ситуации, может где-то .defer() после .select_related(*fields) пригодится, может где и .annotate() будет использован.

В целом с .select_related() я думаю всем все давным давно ясно, не забывайте только, что для не-нулевых FK-полей этот метод ничего не даст. И следующий код сгенерит в худшем случае Articles.objects.count() + 1 запросов (если все поля editor заполнены), вместо 1:

articles = Article.objects.select_related()
for article in articles:
    print(article.title, article.state, article.writer, article.editor)

Метод 2. Использование model.fk_id вместо model.fk.id

На самом деле, с первого взгляда кажется, что метод несколько бесполезен. Ну что там можно узнать про объект только по его идентификатору? Однако, смею вас уверить, это кажется только на первый взгляд. На самом деле во всяких filter или map и прочих lambda-функциях без него не обойтись.

Простой пример, необходимо найти все версии, оставленные редактором статьи. Что сразу приходит в голову? Использовать простой запрос,

article = Article.objects.get(pk=XXX)
versions = Version.objects.get_for_object(article).\
                           exclude(revision__user=None)
versions = article.editor and versions.filter(revision__user=article.editor) \
                          or versions.none()

И вроде все просто и безоблачно. Но только до поры до времени, для начала это +1 запрос к БД, а потом код переместиться в цикл или надо будет расширить параметры поиска искать не только ревизии редактора, но и какого-то системного пользователя.

Выход прост, изначально помнить, что редактор может принимать нулевое значение, а значит обращение к .editor спровацирует +1 запрос к БД. А значит используем .editor_id и точно знаем, что здесь искать кита мы не будем.

article = Article.objects.get(pk=XXX)
versions = Version.objects.get_for_object(article).\
                           exclude(revision__user=None)
versions = \
    article.editor_id and versions.filter(revision__user__pk=article.editor_id) \
                      or versions.none()

Метод 3. Предопределение FK значения

Сразу предупреждаю, пользуйтесь этим методом осторожно и только если уверены, что знаете что делаете :)

Помните пример из первого метода? Когда каждый запрос к полю редактора статьи стоил нам одного запроса. Нехорошо это, ведь и .select_related() нам тут не помощник. Но, не все так печально, а иначе очень уж просто. Надо всего лишь считать всех пользователей, а потом подставить необходимое значение в аттрибут ._editor_cache (примечание: на месте editor может быть любое название вашего FK-поля). А теперь еще раз и в коде:

articles = Article.objects.select_related('writer')
users = User.objects.all()
users = dict([(user.pk, user) for user in users])

for article in articles:
    article._editor_cache = users.get(article.editor_id)
    print(article.title, article.state, article.writer, article.editor)

И мы в итоге получаем гарантировано два запроса к БД, вместо Article.objects.сount() + 2 в худшем случае (опять же при условии, что все поля editor заполнены). Просто и изящно.

Однако надо помнить, что установив ручками значение в ._{{ fk_field }}_cache именно вы в ответе за него, а никак не Django. И если вдруг редактором статьи вместо Васи Пупкина станет Джон Доу, то вы знаете что делать :)

Метод 4. Ручное исполнение запросов

Я думаю, многие обрадовались и подумали, ну наконец-то про .raw(), ну наконец-то про SQL, к черту тот ORM, он и медленный и вообще сложно с ним. Но нет, поспешу разочаровать. Я лишь про то, что во многих случаях может пригодится явное преобразование QuerySet объектов в что-то более питоновское, как-то list и в дальнейшем использование именно питоновских функций для выборки данных, например filter(), а не метода .filter(). Взвесьте все за и против и помните, что QuerySet преобразованный в list есть не попросит к базе уже не обратиться, а обыкновенный еще как сможет.

Ну и про .raw() и .extra() не забывайте. Как говорится, есть моменты когда ваше SQL кунг-фу будет сильнее SQL кунг-фу Django ORM.

Метод 5. Или прочее

Сразу оговорюсь, что я не пытался упомянуть о всех методах уменьшения кол-ва запросов, а лишь хотел преподнести вам методы наиболее используемые мною при работе с Django. Для дальнейшего просветления я рекомендую вам прочитать раздел про оптимизацию доступа к базе данных в документации Django (версия для 1.3 или 1.2), ну или напрямую обращаться к Google или StackOverflow с теми же ключевыми словами, Django database optimization.

На сим прощаюсь и до новых встреч. Если появились вопросы или я где-то сглупил - милости прошу в комменты!

Блог django на хабрахабре

Django Framework / Именованные кортежи из выборок

В Django для ускорения запросов, возвращающих большое количество данных, существуют методы QuerySetvalues() и values_list(). Первый вместо моделей возвращает словари, второй кортежи. Работать и с теми, и с другими не так удобно как с экземплярами моделей, дескать, платите ребята за скорость удобством. А я вот не хочу, и благодаря именнованым кортежам из стандартного модуля collections, не буду.

Блог python на хабрахабре

Python / odbm = объектная обёртка для key-value хранилищ

odbm



Оптимизация проекта => оптимизация хранилища => dbm => dict style => :-( => odmb => :-)

Ростислав Дзинько

Замена сигнала m2m_changed для Django 1.1.x


Вот столкнулся с необходимостью использовать в проекте сигнал на изменения связей многие ко многим. Для тех, кто с этим не сталкивался коротко объясню. Вот пример:

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

В Django 1.2.x существует замечательный сигнал m2m_changed для обработки таких событий, чего лишена версия 1.1.x, о которой я сейчас и пишу.

Первое, что приходит в голову - писать обработчик сигнала post_save для модели django.contrib.auth.User. Но, попробовав такое сделать, мы увидим, что в обработчике сигнала мы получили изменения в объекте-пользователе, но группы, в которых он состоит не поменялись. Проблема в том, что связь многие ко многим осуществляется через доп. таблицу, и на этом этапе мы не получаем изменения так как они еще не произошли.

Есть разные пути решения проблемы, которые подходят при разных требованиях к задачи.

1. Создаем свой собственный менеджер моделей (django.db.models.Manager)

Данный подход, наверное, получится самым универсальным для данной задачи. Создав свой менеджер, можно переопределить методы add, clear, добавив туда необходимый функционал. Таким образом указываем действия, которые можно выполнить, в одном месте.

2. Запоминаем измененные объекты, и делаем последующую обработку в планируемой задаче.

Этот вариант подойдет только тогда, когда влияние изменений некритичны для работы проекта, и действия могут быть выполнены через определенное время после внесения изменений в объекты.

3. Используем save_model класса ModelAdmin.

Метод save_model примечателен тем, что получает в качестве аргументов кроме модели еще и форму, что позволяет сравнить старое состояние объекта и новое через form.cleaned_data, на основе чего можно выполнять необходимые дополнительные действия. Использовать следует тогда, когда гарантируется изменения модели через админ. часть сайта.

4. Делаем действия в виде или форме при сохранении объекта.

Аналогично предыдущему методу, только такое надо делать во всех формах и видах, которые участвуют в изменении нужного атрибута объекта.

Заметочки

Подгрузка связанных моделей, ч.2

Собственно пылятся еще две функции, неизвестной полезности, для подгрузки связаных моделей: для GenericForeignKey и ForeignKey(null=True). Работало на Django 1.1. Думаю с небольшим "допилом" будет и на 1.2 работать. Все это делалось для комментариев. Первой ф-цией подгружали комментируемые обьекты, а второй - пользователей, которые комментировали.


Copy Source | Copy HTML
  1. def load_related_generic(object_list, field='content_object'):
  2.     if not object_list:
  3.         return object_list
  4.  
  5.     related_field = getattr(object_list.model, field)
  6.     ct_field = object_list.model._meta.get_field(related_field.ct_field).get_attname()
  7.     fk_field = object_list.model._meta.get_field(related_field.fk_field).get_attname()
  8.  
  9.     result = {}
  10.     to_retrive = {}
  11.     for item in object_list:
  12.         ct_id = getattr(item, ct_field)
  13.         fk_id = getattr(item, fk_field)
  14.         if ct_id not in to_retrive:
  15.             to_retrive[ct_id] = {'model': getattr(item, related_field.ct_field).model_class(), 'pks': set([])}
  16.         to_retrive[ct_id]['pks'].update([fk_id])
  17.  
  18.     for key in to_retrive:
  19.         objects = to_retrive[key]['model']._default_manager.filter(pk__in=list(to_retrive[key]['pks']))
  20.         result[key] = dict([[obj.pk, obj] for obj in objects])
  21.     for item in object_list:
  22.         setattr(item, field, result[getattr(item, ct_field)][int(getattr(item, fk_field))])
  23.  
  24. def load_related_fk(object_list, field):
  25.     if not object_list:
  26.         return object_list
  27.  
  28.     related_field = object_list.model._meta.get_field(field)
  29.     attname = related_field.get_attname()
  30.  
  31.     pks = list(set([getattr(obj, attname) for obj in object_list if getattr(obj, attname)]))
  32.     objects = related_field.rel.to._default_manager.filter(pk__in=pks)
  33.  
  34.     related_dict = {}
  35.     for obj in objects:
  36.         related_dict[obj.pk] = obj
  37.  
  38.     for obj in object_list:
  39.         try:
  40.             setattr(obj, field, related_dict[getattr(obj, attname)])
  41.         except KeyError:
  42.             pass
  43.  
  44.     return object_list

Copy Source | Copy HTML
  1. #views.py
  2. comments = Comment.objects.all()
  3. load_related_generic(comments)
  4. load_related_fk(comments, 'user')

Copy Source | Copy HTML
  1. {% for item in comments %}
  2.     {{ item.user.username|default: item.username }}
  3.     <a href="{{ item.content_object.get_absolute_url }}">
  4.         {{ item.content_object.name }}
  5.     </a>
  6.     {{ item.comment|striptags|truncatewords:10 }}
  7. {% endfor %}
  8.  

Собственно для подгрузки m2m. И то же для Django 1.2 от Анатолия Ларина.

Анатолий Ларин

Django 1.2.1 и load_related_m2m

Интро Думаю, многие из нас занимались оптимизацие загрузки данных из полей ManyToManyField. Задача для оптимизации проста: есть коллекция объектов с некоторым полем (полями) М2М. Необходимо выбрать данные для поля М2М одним запросом, т.е. не выполнять для каждого объекта из коллекции отдельный запрос, по загрузке однотипных данных. Думаю, многие для этого писали собственные функции, я же пользовался load_related_m2m, [...]

Изучаем Django

Smoothing The Curve

.article header { font-family:LeagueGothicRegular,Impact,sans-serif; font-weight:normal; height:100px; margin-bottom:20px; position:relative; text-transform:uppercase; display: block; } .article header h1 { color:#586C78; font-size:42px; font-weight:normal; height:60px; left:0; letter-spacing:1px; line-height:60px; position:absolute; top:10px; } .article header h2 { color:#CCCCCC; font-size:28px; font-weight:normal; height:40px; left:0; letter-spacing:1px; line-height:40px; position:absolute; top:0; } .article header h2 a { border:medium none; color:#ABB9C1; padding:0; text-decoration:none; } .article header h2 sup { font-size:0.7em; line-height:1em; text-decoration:underline; vertical-align:6px; } .article p, .article ul, .article ol, .article dl { font-size:18px; line-height:1.75em; margin-bottom:1em; font-family:Palatino,Georgia,"Times New Roman",Times,serif; } .article .content { width: 100% !important; } .article .editor { font-size: 80%; margin-top: 0; padding: 0 0 0 0; line-height: 100%; display: block; font-style: italic; } .article .footer p { font-size: 14px; line-height: 1.75em; } code { font-size: 13px; }

Smoothing The Curve

Sean O'Connor February 11th 2010

Практически для всех возможностей Django существуют способы модификации и расширения. Крайне редко может понадобиться переписывать существенную часть функционала Django только для того, чтобы изменить действие какого-то инструмента. Например, если вы хотите изменить внешний вид формы, вы можете отказаться от внешнего вида «по умолчанию» и создать собственное поле, или даже просто использовать собственный HTML. В обоих случаях вы выигрываете по спектру возможностей, сохраняя все остальные преимущества библиотеки форм.

Это расширение возможностей можно представить в виде кривой. На одном конце базовый вариант, который прост в написании, но ограничивает возможности контроля. На противоположном конце кривой будет специально созданный класс для форм со статическим у HTML-кодом, который обеспечивает больший уровень управления, но который и более сложен в создании. В примере с созданием форм эта кривая будет довольно гладкая, поскольку для всего спектра функций библиотеки форм существуют возможности постепенного замещения базовых опций на собственные пользовательские расширения.

До выхода в свет версии Django 1.2 для ORM такая кривая имела аналогичный вид, за одним исключением: существенный скачок в конце. Этот скачок появлялся в связи с тем, что при необходимости создания нестандартного SQL-запроса требовалось выйти за пределы ORM. Чтобы использовать определённый функционал ORM, пользователю пришлось бы создавать его заново самостоятельно, хоть это и не является само по себе катастрофой. В версии Django 1.2 добавлен метод Model.objects.raw (), который решает эту проблему и, таким образом, сглаживает эту кривую для ORM.

Старый способ

До выхода версии Django 1.2 при необходимости создать SQL запрос, нужно было написать нечто подобное:

from django.db import connection
from library.models import Author 

cursor = connection.cursor()
query = "SELECT * FROM library_author"
cursor.execute(query)
results = cursor.fetchall() 

authors = []
for result in results:
    author = Author(*result)
    authors.append(author)

Не то чтобы это было совсем ужасно, но здесь мы теряем доступ к функционалу ORM во всём, что не касается создания SQL запросов. В частности, недоступна автоматическая трансформация результатов запроса в экземпляр модели. В меру трудоёмкие способы восстановить утраченную функциональность, конечно, существуют, но фактически это будет изобретением велосипеда.

Новый способ

В версии Django 1.2 для создания прямого SQL запроса нужно написать следующее:

from library.models import Author 

query = "SELECT * FROM library_author"
authors = Author.objects.raw(query)

authors здесь будет экземпляром RawQuerySet. RawQuerySet во многом похож на QuerySet. В частности, сходство в том, что это — итерируемый объект, который возвращает экземпляр модели из результатов запроса с каждой итерацией. Отличие его от QuerySet состоит в том, что его нельзя встроить в цепочку. Но здесь это и не важно, поскольку запросы больше не формируются автоматически.

Как и при использовании БД курсора мы можем передать набор параметров запроса, Django заботливо их экранирует.

query = "SELECT * FROM library_author WHERE first_name = %s"
params = ('bob',)
authors = Author.objects.raw(query, params)

Так это же здорово! Код SQL защищён от атак, у нас экземпляр модели, который мы хотели, и никакого изобретения велосипеда.

«Но и это ещё не всё!»

Как и большинство инструментов Django, метод raw () оставляет пространство для использования дополнительных функций в неудобных ситуациях или для особо сложных запросов:

Независимый порядок полей

Для метода Model.objects.raw () неважно, в каком порядке возвращаются поля по запросу. Единственное, что важно — соответствуют ли имена полей запроса полям в модели.

# All of these queries will work the same
Author.objects.raw("SELECT * FROM library_author")
Author.objects.raw("SELECT id, first_name, last_name FROM library_author")
Author.objects.raw("SELECT last_name, id, first_name FROM library_author")

Аннотации

Если в ответ на запрос мы получаем поля, которые не существуют в классе модели, они добавляются в качестве аннотаций к тем экземплярам модели, которые возвращает метод RawQueryset. Это с лёгкостью позволяет использовать все преимущества действий или вычислений, выполнение которых более эффективно на уровне базы данных.

>>> authors = Author.objects.raw("SELECT *, age(birth_date) as age FROM library_authors")
>>> for author in authors:
...     print "%s is %s." % (author.first_name, author.age)
John is 37.
Jane is 42.
...

Определения соотношения между полями модели и запроса

Если по какой-либо причине не удаётся точно сопоставить имена поля запросов и имена поля модели, метод Model.objects.raw () возможность указать его вручную.

Чтобы сопоставить поля запроса с полями модели, нужно просто использовать словарь, содержащий требуемые соответствия, в качестве одного из параметров метода raw (). Соответствия необходимо определить только для тех полей, которые не удалось сопоставить с полями модели.

field_map = {'first': 'first_name', 'last': 'last_name'}
    query = 'SELECT id, first_name AS first, last_name as last FROM library_author'
    authors = Author.objects.raw(query, translations=field_map)

Отложенные поля

Те поля, которые предполагаются в модели, но не возвращаются запросом, отмечаются как отложенные. Они будут заполнены только при запросе к полю экземпляра модели. Это полезно в тех случая, когда данные запрашиваются не из «реальной» таблицы для модели, или когда сами таблицы достаточно большие. Здесь надо иметь в виду, что первичный ключ не может быть отложен и должен быть возвращён всеми запросами. Если запрос не возвращает первичный ключ, будет вызвано исключение InvalidQuery.

Ограничения

На действия метода raw () накладываются некоторые ограничения. Самое существенное из них заключается в том, что метод raw () позволяет работу только с SELECT. При попытке использовать любой другой тип запроса, будет вызвано исключение InvalidQuery. Первоначально это было сделано с целью защиты, но отчасти это сделано так и потому что нет смысла возвращать экземпляр модели для чего-либо ещё кроме запроса типа SELECT. Изменение данных с помощью прямого SQL — это последнее, что стоит делать с помощью Django. Чтобы не провоцировать эти действия, мы ни в коем случае не хотим делать их удобнее для пользователя. Если вам требуется использовать именно прямые запросы SQL кроме запроса типа SELECT, у вас всегда остаётся возможность создать курсор БД и работать оттуда.

Вот и всё

Ну вот и всё. В версии Django 1.2 существенно упрощена работа с SQL запросами там, где это необходимо. Кривая, о которой мы говорили выше, имеет гораздо более гладкий вид. Официальную документацию для этой функции можно найти в разделе, посвящённому SQL.

Шон О’Коннор — разработчик HUGE и один из партнёров организаторов сайта для группы пользователей Django NYC. В течение трёх предыдущих лет Шон работал над приложениями Django и последнее время внёс существенный вклад в базовые усовершенствования раздела прямых SQL запросов в Django 1.2. В свободное от разработки Django и HUGE время он пьёт пиво и играет в настольные игры. Найти в интернете его можно по адресу seanoc.com (http://seanoc.com/), его твиттер — @theSeanOC.

Блокнот разработчика

Django, разочарование, часть 1.

Последние три недели наша команда ударными темпами ведёт работу над сайтом 13-го Томского инновационного форума. Всё бы ничего, но по ходу дела возникли несколько проблем. Обо всём по-порядку:

Теггирование.

Существующее решение позволяет добавлять теги к любой сущности (что нам собственно и надо). Делает это за счет generic relations со стороны тегов (Tag + TaggedItem). И тут то возникает первая проблема – расчет тег-клауда. Проходит это в несколько этапов:

1. сам запрос выглядит довольно просто:

SELECT tags_tag.id, count(tags_tagged_item.id)
FROM tags_tag
JOIN tags_tagged_item ON tags_tagged_item.tag_id = tags_tag.id
GROUP BY tags_tag.id

Плюс, по идее HAVING на минимальное кол-во сущностей, помеченных этим тегом. В принципе, выбирая сразу название тега у нас на руках уже есть все данные для формирования клауда, осталось только вывести всё на шаблон, применив стили на основанию веса каждого тега в их общем множестве, объединив соседние величины, чтобы вписаться в ограниченный набор стилей – в итоге заимеем те самые размеры текста, которые так греют глаз. Но, нам надо формировать ссылку на список всех сущностей (сгруппированных по типу), которые были помечены тегом, нужен цельный экземпляр модели Tag. Итого – чтобы вернуть экземпляр тега + 1 запрос на каждый тег, для получения всех полей и маппинга данных в объект питона.

Самое интересное, что эта выборка-маппинг происходит еще на этапе расчета, т.к. в коде мы оперируем уж экземпляром тега:

max_weight = 10
cloud = []
for tag in Tag.objects.all():
    cloud.append([
        tag,
        math.log(tag.tagged_items.count()) * max_weight / math.log(max_weight)
    ])

Итог данного решения – жуткие тормоза в приложении, возникающие при передергивании БД на пересчет клауда.

Решение проблемы пришло с неожиданной стороны:

Для поиска по сайту мы используем solr, в индекс которого кладётся много чего, но все протеггированные сущности туда в итоге попадают тоже. Осталось ввести поле tags, объявив его списочным, наложить токенайзер, который разобъёт теги по запятым, и сделать фасеточную выборку из индекса, использовав теги в качестве ключевого поля:

        solr = Solr(settings.HAYSTACK_SOLR_URL)
        kwargs = {
            'facet': 'on',
            'facet.field': 'tags',
            'rows': 0,
            'start': 1
        }
        result = solr.search(q = 'language:%s' % language, **kwargs)

Возможно, конечно, что проблема тут в SQL бэкенде, в качестве которого был выбран PostgreSQL, и доступа к тюнингу которого у нас нет (сайт как и база крутится на хостинге заказчика, причем доступа к хосту с БД у нас нет). Но сам факт порождения такого числа запросов уже слегка расстраивает. Тегов много, очень много, одних статей в периодике уже >3K, отдельно лежат интервью (~1K) и библиотека мультимедиа (в сумме около 1K записей), и далеко не все теги встречаются с более-менее завидной частотой.
Но и это еще не всё, второй момент никак не затрагивает Django, скорее само решение по теггированию. При добавлении тегов к сущности как и все белые люди в обычное текстовое поле мы заносим список ключевых слов через запятую, при сохранении экземпляра разбираем их на этом основании и, лишив хвостовых пробелов, вызываем следующу магию – определяем, есть ли уже такой тег и если нет – создаём новый экземпляр, обновляем TaggedItem с внешними ключами на сам тег и модель. Это еще три запроса на каждый тег. Все происходит быстро, но только потому, что кол-во тегов к одной сущности вряд ли превышает десять штук. И каждый экземпляр модели обрабатывается контент-менеджером более-менее индивидуально.

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

В общем – или отказываться от услуг ORM, или использовать стороннее решение, что произошло в нашем случае.

Если кто-то предложит красивый способ решения такой проблемы, буду очень благодарен.

Блог python на хабрахабре

Язык программирования Python / [Ссылка] Вышел SQLAlchemy 0.6

После года плодотворного труда обновился SQL toolkit и ORM — SQLAlchemy. Из основных изменений следует выделить поддержку py3k и новых баз данных.

Блог python на хабрахабре

Язык программирования Python / [Ссылка] Использование Django ORM отдельно

О том, как засунуть все необходимое для работы джанга-орм в один файлик models.py

В чем я?!

Nhibernate в .NET

Надоел мне, так называемый raw SQL на работе, и воодушевившийся django я подумал, а нет ли ORM для C# .NET 2.0 (тем более, что в NET 3.5 и студии 2008 уже заложена пооддержка ORM в виде LINQ (хотя, как я понял - это не совсем ORM, а скорее аналог HQL). Оказалось есть и не одна.

Так как я приверженец os, поэтому неудивительно, что мой выбор пал на NHibernate.

NHibernate — ORM-решение для платформы Microsoft .NET портированное с Java. Это бесплатная библиотека с открытым кодом, распространяется под лицензией GNU Lesser General Public License.

NHibernate позволяет отображать объекты бизнес-логики на реляционную базу данных. По заданному XML-описанию сущностей и связей NHibernate автоматически создает SQL-запросы для загрузки и сохранения объектов.

NHibernate является портом на .NET популярной на платформе Java библиотеки Hibernate. Версия 1.0 покрывает набор возможностей Hibernate 2.1, а также часть возможностей Hibernate 3. NHibernate 1.2.1, выпущенная в ноябре 2007 года, предоставляет ещё больше возможностей из Hibernate 3, поддерживает .NET 2.0, хранимые процедуры, generics и nullable типы. NHibernate 2.0.1, выпущенная в конце сентября 2008 года предоставляет бо́льшую часть возможностей Hibernate 3.
Поддерживаемые СУБД

* Microsoft SQL Server
* Oracle
* Microsoft Access
* Firebird
* PostgreSQL
* DB2 UDB
* MySQL
* SQLite


http://ru.wikipedia.org/wiki/NHibernate

Чтобы быстро начать с ним работать, рекомендую просмотреть следущие скринкасты:
Summer of nhibernate

Через день вы сможете уже писать приложения, используя NHibernate, что в дальнейшем сэкономит вам уйму времени (написать один класс для таблицы и все - использовать эти классы в любой программе).
В общем, рекомендую посмотреть, а потом решить, подходить ли данное решение для Вас.

ps. приверженцы Djanjo оценят, я уверен =)
pps. Да, с Днем Знаний всех! Помните, учиться никогда не поздно! ;)

В чем я?!

Nhibernate в .NET

Надоел мне, так называемый raw SQL на работе, и воодушевившийся django я подумал, а нет ли ORM для C# .NET 2.0 (тем более, что в NET 3.5 и студии 2008 уже заложена пооддержка ORM в виде LINQ (хотя, как я понял - это не совсем ORM, а скорее аналог HQL). Оказалось есть и не одна.

Так как я приверженец os, поэтому неудивительно, что мой выбор пал на NHibernate.

NHibernate — ORM-решение для платформы Microsoft .NET портированное с Java. Это бесплатная библиотека с открытым кодом, распространяется под лицензией GNU Lesser General Public License.

NHibernate позволяет отображать объекты бизнес-логики на реляционную базу данных. По заданному XML-описанию сущностей и связей NHibernate автоматически создает SQL-запросы для загрузки и сохранения объектов.

NHibernate является портом на .NET популярной на платформе Java библиотеки Hibernate. Версия 1.0 покрывает набор возможностей Hibernate 2.1, а также часть возможностей Hibernate 3. NHibernate 1.2.1, выпущенная в ноябре 2007 года, предоставляет ещё больше возможностей из Hibernate 3, поддерживает .NET 2.0, хранимые процедуры, generics и nullable типы. NHibernate 2.0.1, выпущенная в конце сентября 2008 года предоставляет бо́льшую часть возможностей Hibernate 3.
Поддерживаемые СУБД

* Microsoft SQL Server
* Oracle
* Microsoft Access
* Firebird
* PostgreSQL
* DB2 UDB
* MySQL
* SQLite


http://ru.wikipedia.org/wiki/NHibernate

Чтобы быстро начать с ним работать, рекомендую просмотреть следущие скринкасты:
Summer of nhibernate

Через день вы сможете уже писать приложения, используя NHibernate, что в дальнейшем сэкономит вам уйму времени (написать один класс для таблицы и все - использовать эти классы в любой программе).
В общем, рекомендую посмотреть, а потом решить, подходить ли данное решение для Вас.

ps. приверженцы Djanjo оценят, я уверен =)
pps. Да, с Днем Знаний всех! Помните, учиться никогда не поздно! ;)

Метки

.net .NET C# .sort 1.2 2009 2010 404 error admin ajax amazon analytics and apache api archlinux asp.net async asynchronous autocomplete bash blender blog blogengine blogs book bootstrap bot bpython buildout byteflow bzr C c plus plus C++ cache cbv Chaco checkio chrome ci ckeditor class based views clojure closure cms cms с удобной админкой code coding style collectd COM comet competition conference ConfigParser contest Context continuous integration CouchDB coverage CppCMS cpyext cpython crud csrf CSS ctypes curl custom model fields cx_freeze cython database db dbm dbqueries debian debug debugging decorator decorators deploy deployment descriptor design dev devconf developers development diveintopython Django django 1.2 django 1.3 django advent django framework django template django trunk django weblog django-admin-tools django-cms django-compressor django-hosts django-piston django-registration django-sphinx django.admin djangoadvent djangocms djangodash doc documentation drupal e-legion eclipse EGit emacs encoding Enthought epoll erlang event exception ExtJS fabric facebook fastcgi finaloption fixtures fonts forms formset fp framework freebsd freeswitch fs2web ftp fun funcparserlib functional gae gamin gandi generic views gettext gevent gil git github gitosis Google Google App Engine google picasa Google Translate google wave Google Web Toolkit grab grablab greenlet gtd gui haskell hg hgshelve highlighter host hosting how-to howto html html5lib Hudson humor i18n icfpc ide idiomatic image-scripting improvements Internet interpreter ipython ironpython izmenimsya.ru jabber java javascript jenkins jetbrains JIT job jquery json jstree jython kde kiev kiyv kyivpy l10n ldap library libs Life Links linux Linux & Unix LLVM logging logs lxml Mac OS X magic mail markdown Matplotlib Mayavi maybe mediavirus meetup memcache Memcached memory messages metaclass middleware migration mikrotik mkd model models mod_python mod_wsgi mongodb monitoring mptt musicmans.ru musicx mvc my-projects mysql netCDF networkx newforms newforms-admin news nginx Nhibernate nix nose NoSQL numpy oop open source OpenID openoffice opster optimization oracle orm os pagination parsing path patterns pdf PDF-принтер PEP PEP8 performance performance optimization perl personality photo php picture-driven computing PIL pinax pingback pip plasma plone plugin plugins postgresql programming progress bar psycopg2 py2exe pybb pybbm pycamp pycharm pycon pycow pycurl pydev pygtk pylons PyNGL pypy pyqt PyQt4 pyrad pyramid PySide Python Python 2.5 python 2.7 python 3 python c api python speed python-mssql python3 pywinauto Qt Qt4 queue rabbitmq radius raw sql re redis redsolution redsolution cms regexp regular expressions release repoze.bfg RequestContext reusable apps robokassa rss ru ruby ruby-on-rails sample satchmo scalability SciPy scraping screencast search selenium self.error seo server setattr settings setuptools shell sikuli sms snippet socket.io software sorting south sphinx spider sql sqlalchemy sqlite ssh startup step-by-step subdomain subversion svn SyntaxHighlighter system tags tdd tddspry teh drama template templates templatetags test testing thinkpad threading threads tips tips and tricks tools tornadio tornado tornado server tricks tutorial tweepy twisted twitter typography uapycon Ubuntu ucsvlog uml Uncategorized unicode unit test unit testing UnitTest Unladen Swallow upload urllib urls utf-8 uwsgi validation vcs versioning video vim virtualenv Visual Studio vkontakte voip wave web web-devel web-services web-разработка webdev webfaction webkit webpy websockets webtest widget widgets Win API windows Wirbel work wrapper wsgi wxPython wxWidgets wysiwyg xapian xml xmonad xmpp xpath yandex youtube zip zomg zope [cdata[cbv]] [cdata[ci]] [cdata[class based views]] [cdata[continuous integration]] [cdata[django framework]] [cdata[django-sphinx]] [cdata[django]] [cdata[nginx]] [cdata[python]] [cdata[virtualenv]] [cdata[программирование]] автоматизация администрирование администрирование django админка алгоритмы архитектура атрибуты базы данных Без рубрики безопасность библиотеки блоге бот веб-разработка видео Визуализация данных вконтакте Все записи гвидо ван россум граббер графика графы декоратор декораторы дескриптор дескрипторы документация заметки игра жизнь идея интересное киев Клиентам книги конференция личное математика метаклассы модели модули монады морфология мысли невозможное новости о облачные вычисления обо мне Обработка данных оптимизация оптимизация кода Основная лента основы парсинг парсинг сайтов перевод песочница Питон поебень поиск правила кодирования программирование Проектирование производительность работа рабочее размышлизмы Разное разработка разработка приложений разработки регулярные выражения сайт событие события ссылки статьи тестирование тесты Тюмень убунтариум фигня философия формы форум Хабрахабр хакинг хостинг шаблоны шаблоны проектирования эксперимент Эксперименты юмор я пиарюсь Яндекс