Самой приятной новостью с PyCon 2016 для меня стала презентация Andrew Godwin'а, разработчика Django и библиотеки channels, о которой в презентации и шла речь. Эта библиотека позволяет Django работать с WebSockets и HTTP2. Почитайте оригинал презентации здесь - в короткой презентации показаны примеры использования этой библиотеки.
четверг, 21 июля 2016 г.
среда, 22 июня 2016 г.
Не работает python RQ c Sentry?
Чтобы заставить ошибки ваших заданий отправляться из rq worker'а прямо в замечательную Sentry, в команде rq есть специальная опция --sentry-dsn. К сожалению, на практике выясняется, что она совсем не работает. Но почему?
В документации к rq указано, что необходимо использовать
(http://python-rq.org/patterns/sentry/)
Но, у rq в cli.py (command line interface) мы видим, что это не выполнено:
from rq.contrib.sentry import register_sentry
client = Client(sentry_dsn)
и не составит труда исправить на
from rq.contrib.sentry import register_sentry
from raven.transport.http import HTTPTransport
client = Client(sentry_dsn, transport=HTTPTransport )
Готово! Теперь ваши ошибки из rq workera отправятся прямо в Sentry при использовании в команде запуска rq из консоли опции "sentry-dsn".
Исправленный код можно взять на тут.
В документации к rq указано, что необходимо использовать
HTTPTransport
или RequestsHTTPTransport
в клиенте raven.from raven import Client
from raven.transport.http import HTTPTransport
from rq.contrib.sentry import register_sentry
client = Client('<YOUR_DSN>', transport=HTTPTransport)
register_sentry(client, worker)
(http://python-rq.org/patterns/sentry/)
Но, у rq в cli.py (command line interface) мы видим, что это не выполнено:
from rq.contrib.sentry import register_sentry
client = Client(sentry_dsn)
и не составит труда исправить на
from rq.contrib.sentry import register_sentry
from raven.transport.http import HTTPTransport
client = Client(sentry_dsn, transport=HTTPTransport )
Готово! Теперь ваши ошибки из rq workera отправятся прямо в Sentry при использовании в команде запуска rq из консоли опции "sentry-dsn".
Исправленный код можно взять на тут.
четверг, 28 апреля 2016 г.
Тестирование кэша
Для того, чтобы тестировать кэширование страниц сайта, хорошо подходит django-debug-toolbar
потребуется только установить тулбар и включить локальный кэш:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
Готово! Теперь на вкладке SQL тулбара мы видим только реально осуществленные запросы
а на вкладке Кэш - информацию об обращениях к кэшу
для кэширвания шаблонов используем конструкции вида:
где sidebar - название кэша
{% load cache %}
{% cache 500 sidebar sidebar_cache_key %}
.. sidebar ..
{% endcache %}
или для .jade:
- load cache
- cache 600 sidebar sidebar_cache_key
в видах
key = myvar_key # ключ кэша
myvar = cache.get(key)
if not myvar:
myvar = ... # get myvar value
cache.set(key, myvar, 5 * 60)
потребуется только установить тулбар и включить локальный кэш:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
Готово! Теперь на вкладке SQL тулбара мы видим только реально осуществленные запросы
а на вкладке Кэш - информацию об обращениях к кэшу
для кэширвания шаблонов используем конструкции вида:
где sidebar - название кэша
{% load cache %}
{% cache 500 sidebar sidebar_cache_key %}
.. sidebar ..
{% endcache %}
или для .jade:
- load cache
- cache 600 sidebar sidebar_cache_key
в видах
key = myvar_key # ключ кэша
myvar = cache.get(key)
if not myvar:
myvar = ... # get myvar value
cache.set(key, myvar, 5 * 60)
четверг, 21 апреля 2016 г.
Тестируем время отдачи страницы или выполнения какйо-либо функции
Если нам нужно узнать в тесте время отдачи страницы, то может пригодиться следующий сниппет:
import unittest
import time
from django.conf import settings
from django.test import Client
class SimpleTest(unittest.TestCase):
def setUp(self):
self.c = Client()
def test_CatalogCityTypeThingView(self):
start = time.time()
response = self.c.get(
"/catalog/odejda"
self.assertEqual(response.status_code, 200)
print 'response rendering took ', time.time() - start
import unittest
import time
from django.conf import settings
from django.test import Client
class SimpleTest(unittest.TestCase):
def setUp(self):
self.c = Client()
def test_CatalogCityTypeThingView(self):
start = time.time()
response = self.c.get(
"/catalog/odejda"
self.assertEqual(response.status_code, 200)
print 'response rendering took ', time.time() - start
пятница, 15 апреля 2016 г.
Статистика действий пользователей Django
Смотреть статистику действий пользователей Django поможет следующий код в admin.py:
class LogEntryAdmin(admin.ModelAdmin):
date_hierarchy = 'action_time'
readonly_fields = ([f.name for f in LogEntry._meta.fields] +
['object_link', 'action_description', 'user_link'])
fieldsets = (
(_('Metadata'), {
'fields': (
'action_time',
'user_link',
'action_description',
'object_link',
)
}),
(_('Detail'), {
'fields': (
'change_message',
'content_type',
'object_id',
'object_repr',
)
}),
)
list_filter = [
UserListFilter,
'content_type',
ActionListFilter,
('action_time', DateRangeFilter),
]
search_fields = [
'object_repr',
'change_message'
]
list_display_links = [
'action_time',
'change_message',
]
list_display = [
'action_time',
'user_link',
'content_type',
'object_link',
'action_description',
'change_message',
]
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
def object_link(self, obj):
if obj.action_flag == DELETION:
link = escape(obj.object_repr)
else:
ct = obj.content_type
try:
link = '<a href="%s">%s</a>' % (
reverse(
'admin:%s_%s_change' % (ct.app_label, ct.model),
args=[obj.object_id]
),
escape(str(obj.get_edited_object())),
)
except NoReverseMatch:
link = escape(obj.object_repr)
return link
object_link.allow_tags = True
object_link.admin_order_field = 'object_repr'
object_link.short_description = 'object'
def user_link(self, obj):
try:
ct = ContentType.objects.get_for_model(type(obj.user))
link = '<a href="%s">%s</a>' % (
reverse(
'admin:%s_%s_change' % (ct.app_label, ct.model),
args=[obj.user.pk]
),
escape(force_text(obj.user)),
)
except NoReverseMatch:
link = escape(force_text(obj.user))
return link
user_link.allow_tags = True
user_link.admin_order_field = 'user'
user_link.short_description = 'user'
def get_queryset(self, request):
queryset = super(LogEntryAdmin, self).get_queryset(request)
return queryset.prefetch_related('content_type')
def get_actions(self, request):
actions = super(LogEntryAdmin, self).get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
return actions
def action_description(self, obj):
return action_names[obj.action_flag]
action_description.short_description = 'Action'
admin.site.register(LogEntry, LogEntryAdmin)
Управление доступом в админке Django
В админке Django есть возможность запретить доступ к страницам редактирования отдельных моделей пользователям кроме superuser.
Это делается в admin.py:
Например:
class LogEntryAdmin(admin.ModelAdmin):
...
# Запрет на добавление всегда
def has_add_permission(self, request):
return False
# Запрет на редактирование если пользователь не superuser
def has_change_permission(self, request, obj=None):
return request.user.is_superuser and request.method != 'POST'
# Запрет на удаление всегда
def has_delete_permission(self, request, obj=None):
return False
Это делается в admin.py:
Например:
class LogEntryAdmin(admin.ModelAdmin):
...
# Запрет на добавление всегда
def has_add_permission(self, request):
return False
# Запрет на редактирование если пользователь не superuser
def has_change_permission(self, request, obj=None):
return request.user.is_superuser and request.method != 'POST'
# Запрет на удаление всегда
def has_delete_permission(self, request, obj=None):
return False
четверг, 14 апреля 2016 г.
Счетчик кликов по ссылке в Django
Появилась задача для подсчета числа переходов на Django сайте.
Для этого создадим промежуточную страницу /go/<thing_id>/:
Обработчик страницы будет производить две простых вещи:
- увеличение счетчика числа переходов;
- перенаправление пользователя на нужный урл;
1. Для начала напишем простенький тест:
2. Добавим таблицу в БД для подсчета кликов:
Для этого создадим промежуточную страницу /go/<thing_id>/:
Обработчик страницы будет производить две простых вещи:
- увеличение счетчика числа переходов;
- перенаправление пользователя на нужный урл;
1. Для начала напишем простенький тест:
class ThingCounterTest(unittest.TestCase):
def setUp(self):
self.thing _ = Thing.objects.get_or_create(name="test_channel")
def tearDown(self):
self.thing.delete()
def test_thing_counters(self):
self.thing.add_click()
self.assertEqual(self.thing.clicks_today, 1)
self.assertEqual(self.thing.clicks_yesterday, 0)
self.assertEqual(self.thing.clicks_last_week, 1)
self.assertEqual(self.thing.clicks_last_month, 1)
2. Добавим таблицу в БД для подсчета кликов:
class ThingClick(models.Model):
thing = models.ForeignKey(
Thing, related_name='clicks',
on_delete=models.CASCADE)
clicked_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = u"Клик на товар"
verbose_name_plural = u"Клики на товары"
Не забываем сделать миграцию.
3. Добавим функции для подсчета кликов за последние периоды:
(models.py)
class Thing(models.Model):
...
def add_click(self):
self.clicks.create()
@property
def clicks_today(self):
yesterday = timezone.now().date() - timedelta(days=1)
return self.clicks.filter(
clicked_at__gt=yesterday).count()
@property
def clicks_yesterday(self):
yesterday = timezone.now().date() - timedelta(days=1)
return self.clicks.filter(
clicked_at__gte=yesterday,
clicked_at__lt=timezone.now().date()).count()
@property
def clicks_last_week(self):
week_ago = timezone.now().date() - timedelta(days=7)
return self.clicks.filter(clicked_at__gte=week_ago).count()
@property
def clicks_last_month(self):
month_ago = timezone.now().date() - timedelta(days=30)
return self.clicks.filter(clicked_at__gte=month_ago).count()
4. напишем вид, который будет считать ссылки и редиректить на нужную страницу (ссылка на которую у меня уже хранится в thing.link):
(views.py)
def thing_click_admitad(request, thing_id=None):
"""
Переход по ссылке thing.link со счетчиком кликов
по каналу
"""
if thing_id:
thing = get_object_or_404(
Thing.objects.select_related("thing_set__channel"),
id=thing_id)
try:
thing.add_click()
except Exception, e:
logger.error("Failed to add a click for thing: " + str(e))
if thing.link:
return HttpResponseRedirect(thing.link)
else:
raise Http404
5. Добавим в urls.py соответствующий шаблон ссылок:
url(r"^go/(?P<thing_id>[0-9]+)/$",
"statistic.views.thing_click",
name="counted_link"),
6. Наконец, добавим в админку отображение результатов нашей статистики:
class ThingAdmin(admin.ModelAdmin):
list_display = (
"name", "url", "clicks_today", "clicks_yesterday",
"clicks_last_week", "clicks_last_month")
def clicks_today(self, obj):
return obj.clicks_today
clicks_today.short_description = u'Кликов сегодня'
def clicks_yesterday(self, obj):
return obj.clicks_yesterday
clicks_yesterday.short_description = u'Кликов вчера'
def clicks_last_week(self, obj):
return obj.clicks_last_week
clicks_last_week.short_description = u'Кликов за последнюю неделю'
def clicks_last_month(self, obj):
return obj.clicks_last_month
clicks_last_month.short_description = u'Кликов за последний месяц'
Подписаться на:
Комментарии (Atom)