Django New Relic uWSGI

  • Устанавливаем New Relic
    pip install newrelic
  • Лицензионный ключ берем из личного кабинета, генерируем конфигурацию
    newrelic-admin generate-config LICENSE-KEY newrelic.ini
  • В конфигурации правим название приложения
    # The appplication name. Set this to be the name of your
    # application as you would like it to show up in New Relic UI.
    # The UI will then auto-map instances of your application into a
    # entry on your home dashboard page.
    app_name = app.name
  • Модифицируем project/wsgi.py
    import os
    import newrelic.agent
    newrelic.agent.initialize('/path/to/newrelic.ini')
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
    from django.core.wsgi import get_wsgi_application
    application = get_wsgi_application()
    application = newrelic.agent.wsgi_application()(application)
  • Правим конфигурацию uwsgi.ini
    #module = django.core.handlers.wsgi:WSGIHandler()
    module = project.wsgi
    eval = import newrelic.agent, wsgi; application = newrelic.agent.wsgi_application()(wsgi.application)

sql_joined_field sphinx Django m2m

Необходимо включить результаты поиска по полю name, связанной m2m модели Category.
Модели:

class Category(models.Model):
    name = models.CharField(_(u'Название категории'), max_length=60, unique=True)
    ...
class Product(models.Model):
    ...
    category = models.ManyToManyField(Category, verbose_name=u'Категория', related_name='products')
    ...

Конфиг сфинкса:

sql_attr_multi = uint category from query; SELECT product_id, category_id FROM core_product_category
sql_joined_field = category from query; SELECT core_product_category.product_id, name \
FROM core_category join core_product_category on core_category.id=core_product_category.category_id order by core_product_category.product_id ASC

ascii codec can't encode characters in position "" Django Supervisor

В конфиг supervisor добавляем

environment=LANG="ru_RU.utf8", LC_ALL="ru_RU.UTF-8", LC_LANG="ru_RU.UTF-8"

При обновлении MySQL миниатюры изображений, отрисованные с помощью Pillow и easy_thumbnails для Django, могут перестать работать, рецепт такой же, добавить локаль в конфиг supervisor и перезапустить его.

Django Ajax

Нашел чудесную библиотеку django-ajax, берет на себя всю рутину, связанную с ajax во вьюшках Django.
Вьюшка теперь может выглядеть так:

from django_ajax.decorators import ajax
from models import Product
from cart.cart import Cart
@ajax
def ajax_add_to_cart(request):
    if 'product_id' in request.GET and request.GET['product_id']:
        product_id = request.GET['product_id']
        product = Product.objects.get(id=product_id)
        cart = Cart(request)
        cart.add(product, product.price, quantity=1)
        items_in_cart = cart.itemCount()
        return {'items_in_cart': items_in_cart}

Ответом будет JSON такого вида

{"status": 200, "statusText": "OK", "content": {"items_in_cart": 5}}

Шаблон может выглядеть так:

<a class="in-cart" href="{% url 'add_to_cart' item.id 1 %}">в корзину</a>
<script type="text/javascript">
    $(function() {
                var lnk = $("a.in-cart");
                $(lnk).click(function() {
                    $.get($(lnk).attr('href')).done(function( json ) {
                        $("#items_in_cart").text(json.content.items_in_cart)
                    });
                    return false
                });
            });
</script>

django-simple-history трекинг истории изменения объекта

В джанго есть встроенный трекер истории, но он не позволяет откатиться на предыдущее состояние. Решить эту задачу можно с помощью удобного инструмента django-simple-history.

pip install django-simple-history

В settings.py

INSTALLED_APPS = (...
    'simple_history',
...)

В models.py

from simple_history.models import HistoricalRecords
class Product(models.Model):
    ...
    history = HistoricalRecords()

Выполняем миграцию

./manage.py schemamigration <app_name> --auto
./manage.py migrate <app_name>

Далее в admin.py

from simple_history.admin import SimpleHistoryAdmin
class ProductAdmin(SimpleHistoryAdmin, admin.ModelAdmin):
    ...
admin.site.register(Product, ProductAdmin)

Теперь при нажатии на кнопку «История» объекта можно откатиться на выбранную ревизию.

Memcached/Johnny Cache Django 1.5 Debian

Ничего нового, просто чтобы не гуглить в очередной раз.

aptitude install memcached
pip install python-memcached
pip install johnny-cache

Конфиг memcached находится в /etc/memcached.conf, оставляю все значения по умолчанию.
В settings.py

INSTALLED_APPS = (
    # ...
    'johnny',
)
MIDDLEWARE_CLASSES = (
    'johnny.middleware.LocalStoreClearMiddleware',
    'johnny.middleware.QueryCacheMiddleware',
    # ...
)
CACHES = {
    'default' : dict(
        BACKEND = 'johnny.backends.memcached.MemcachedCache',
        LOCATION = ['127.0.0.1:11211'],
        JOHNNY_CACHE = True,
    )
}
JOHNNY_MIDDLEWARE_KEY_PREFIX='jc_myproj'

— префикс текущего проекта
Если у вас Django 1.5 и при пустых queryset вываливается ошибка

AttributeError: 'module' object has no attribute 'iter'

ставьте с гитхаба

git clone http://github.com/jmoiron/johnny-cache.git
cd johnny-cache && ./setup.py


Django + Sphinx + Debian

Ставим отсюда

https://github.com/FactorAG/django-sphinx

В settings.py

INSTALLED_APS= (
...
'djangosphinx',
...
)
# Sphinx 2.0.4/2.0.6/2.0.8
SPHINX_API_VERSION = 0x119

models.py

from djangosphinx.models import SphinxSearch
class Product(models.Model):
    brand = models.ForeignKey(Brand, verbose_name=u'Бренд')
    title = models.CharField(_(u'Марка'), max_length=50, blank=True)
    caption = models.CharField(_(u'Серия'), max_length=150, blank=True)
    composition = models.CharField(_(u'Ингредиенты'), max_length=60, blank=True, null=True)
    about = RichTextField(_(u'Описание'), blank=True)
    pub_date = models.DateTimeField(default=datetime.now)
    pet = models.ManyToManyField(Pet, verbose_name=u'Животное')
    updated = models.DateTimeField(auto_now=True)
    search = SphinxSearch(weights={'title': 90, 'caption': 80, 'about': 100, 'composition': 70},
                          mode='SPH_MATCH_ALL', rankmode="SPH_RANK_BM25", sort='SPH_SORT_RELEVANCE',
                          )

views.py

@render_to('core/entry_list.html')
def searcher(request):
    if 'srch' in request.GET and request.GET['srch']:
        txt = request.GET['srch']
        entries = Product.search.query(txt)
        return {'object_list': entries, }
    else:
        return root(request)

django-sphinx умеет создавать конфигурацию для Sphinx

./manage.py generate_sphinx_config <app_name> > config/sphinx.conf

Получившийся файл конфигурации нужно немного дополнить

source core_product
{
    type                = mysql
    sql_host            =
    sql_user            = <DB_user>
    sql_pass            = ******
    sql_db              = <DB>
    sql_port            =
    sql_query_pre       = SET NAMES utf8
    sql_query_post      =
    sql_query           = \
        SELECT id, onmain, brand_id, title, caption, gen_caption, composition, about, ext, pub_date, updated\
        FROM core_product
    sql_query_info      = SELECT * FROM `core_product` WHERE `id` = $id
    # ForeignKey's
    sql_attr_uint       = brand_id
    # DateField's and DateTimeField's
    sql_attr_timestamp   = pub_date
    sql_attr_timestamp   = updated
}
index core_product
{
    source          = core_product
    path            = /home/project/var/sphinx
    docinfo         = extern
    morphology      = stem_enru, Soundex, Metaphone
    charset_type    = utf-8
    min_word_len    = 2
    min_infix_len   = 2
    min_prefix_len  = 0
    enable_star     = 1
    charset_table = 0..9, A..Z->a..z, _, a..z, U+0401->U+0435, U+0451->U+0435, U+410..U+42F->U+430..U+44F, U+430..U+44F
    index_exact_words = 1
    expand_keywords   = 1
    index_sp=1
    html_index_attrs = img=alt,title; a=title;
    html_strip=1
}
indexer {
    mem_limit = 64M
}
searchd {
    listen = 9312
    listen = 9306:mysql41
    log = /home/project/logs/searchd.log
    query_log = /project/logs/query.log
    read_timeout = 5
    client_timeout = 300
    max_children = 30
    pid_file = /home/project/tmp/searchd.pid
    max_matches = 1000
    seamless_rotate = 1
    preopen_indexes = 1
    unlink_old = 1
    mva_updates_pool = 1M
    max_packet_size = 8M
    max_filters = 256
    max_filter_values = 4096
    max_batch_queries = 32
    workers = threads
}

Стваим Sphinx

  • Качаем исходники (есть готовые пакеты) отсюда
    http://sphinxsearch.com/downloads/release/
  • Распаковываем и конфигурируем
    tar xzvf sphinx-2.0.8-release.tar.gz
    sphinx-2.0.8-release
    ./configure --with-mysql
    make
  • Осталось собрать пакет и установить его в ситему (никогда не делайте make install, каждый раз, когда вы так делаете, умирает котенок)
    aptitude install checkinstall
    checkinstall

    теперь вы можете управлять пакетом с помощью пакетного менеджера.

Sphinx установлен, осталось проиндексировать БД и запустить демона.

indexer --config config/sphinx.conf --all

Для дальнейшей индексации можно использовать

indexer --config config/sphinx.conf --all --rotate

Для запуска

searchd -c config/sphinx.conf

Для остановки

searchd --stop -c config/sphinx.conf

Для повышения качества поиска можно использовать словарь словоформ

aptitude install myspell-ru
spelldump /usr/share/hunspell/ru_RU.dic /usr/share/hunspell/ru_RU.aff wordforms_ru_RU.txt
cat wordforms_ru_RU.txt | enca -L ru
iconv -f KOI8-R -t UTF-8 -o wordforms_ru_RU_UTF8.txt wordforms_ru_RU.txt

Затем подключаем его в sphinx.conf в раздел index

wordforms = /path/to/wordforms_ru_RU_UTF8.txt

Источники
http://sphinxsearch.com/docs/manual-2.0.8.html#supported-system

http://osmanov-dev-notes.blogspot.ru/2011/06/how-to-create-sphinx-wordform.html

http://proft.me/2011/01/22/polnotekstovyj-poisk-v-django/

http://habrahabr.ru/post/136261/

http://habrahabr.ru/post/147745/

http://habrahabr.ru/post/132118/

Пример использования функции reverse() при построении sitemap.xml для Django

Иногда, при построении карты сайта удобно пользоваться reverse() из django.core.urlresolvers

from django.contrib.sitemaps import Sitemap
from django.core.urlresolvers import reverse
from views import root, product_detail, products_list
class RootSitemap(Sitemap):
    changefreq = "weekly"
    priority = 0.9
    def items(self):
        return [root]
    def location(self, obj):
        return reverse(obj)
class ProductDetailSitemap(Sitemap):
    changefreq = "weekly"
    priority = 0.9
    def items(self):
        return Products.objects.all()
    def location(self, obj):
        return reverse(products_list, kwargs={'product_id': obj.pk})
class ProductsListSitemap(Sitemap):
    changefreq = "weekly"
    priority = 0.5
    def items(self):
        return Pets.objects.order_by('url_id')
    def location(self, obj):
        return reverse('products_list', args=[obj.url_id])

django virtualenvs nginx uwsgi supervisor debian

Предполагается, что у нас уже есть настроенное виртуальное окружение virtualenv debian
nginx в стандартном репозитории Debian Squeeze старый, поэтому ставить будем из backports
/etc/apt/sources.list

deb http://backports.debian.org/debian-backports squeeze-backports main contrib non-free
deb http://ftp.ru.debian.org/debian testing main non-free contrib
deb http://ftp.ru.debian.org/debian unstable main non-free contrib

/etc/apt/preferences

Package: *
Pin: release a=stable
Pin-Priority: 700
Package: *
Pin: release a=squeeze-backports
Pin-Priority: 675
Package: *
Pin: release a=testing
Pin-Priority: 650
Package: *
Pin: release a=unstable
Pin-Priority: 600

Указываем использовать stable по умолчанию

echo 'APT::Default-Release "stable";' > /etc/apt/apt.conf.d/default

Обновляем список репозиториев

aptitude update

Читать далее django virtualenvs nginx uwsgi supervisor debian

Миграция моделей в Django с помощью South

Команда ./manage.py syncdb в django предназначена только для создания новых таблиц на основе моделей. Добавлять или удалять поля из существующей таблицы она не будет, ровно как и вносить изменения в существующие поля. Эти манипуляции остаются за разработчиком приложения. Однако есть удобное средство миграции, South

  1. Устанавливаем south
    pip install south
  2. Добавляем в ‘INSTALLED_APPS’ в вашем settings.py
    INSTALLED_APPS = (
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.sites',
        'django.contrib.messages',
        'django.contrib.staticfiles',
         'django.contrib.admin',
         'django.contrib.admindocs',
         'south',
  3. Выполняем syncdb
    python manage.py syncdb
  4. Читать далее Миграция моделей в Django с помощью South