loginza + django-avatars + facebook + vkontakte

Авторизуємось через loginza, і якщо facebook нам віддає ім'я і аватар юзернейма, зберігаємо у моделі користувачів.

def loginza_auth_handler(sender, user, identity, **kwargs):
    try:
        #loginza_models.UserMap.objects.get(user=user, verified=True)
        map = loginza_models.UserMap.objects.get(user=user)
        data = json.loads(map.identity.data)

        if data['provider'] == "http://www.facebook.com/":
            system_user = User.objects.get(username=user)
            if data['name']['first_name']:
                system_user.first_name = data['name']['first_name']
            if data['name']['last_name']:
                system_user.last_name = data['name']['last_name']
            if data['photo']:
                try:
                    Avatar.objects.get(user=system_user)
                except:
                    (filename, headers) = urllib.urlretrieve(data['photo'])
                    img_name = '%s.jpg' % os.path.basename(filename)
                    copy_path = os.path.join(settings.MEDIA_ROOT, settings.AVATAR_STORAGE_DIR, system_user.username, img_name)
                    copy_dir = os.path.join(settings.MEDIA_ROOT, settings.AVATAR_STORAGE_DIR, system_user.username)
                    if not os.path.isdir(copy_path):
                        os.makedirs(copy_dir)
                    shutil.copy2(filename, copy_path)
                    path_ava = os.path.join(settings.AVATAR_STORAGE_DIR, system_user.username, img_name)

                    avatar = Avatar(
                        user = system_user,
                        primary = True,
                        avatar = path_ava,
                    )
                    avatar.save()
            system_user.save()

        auth.login(sender, user)

Для Вконтактє ситуація аналогічна, лише Вконтактє віддає малюнок з розширенням, а Фейсбук без.

Django pagination

Вбудована пагінація якась надто скромна і не функціональна. Перепробував кілька реалізацій і зупинився на django-paging. Жахливо документована, але запрацювала на "ура" у всіх аппах.

Зміна xml шаблону Sitemap в Django

Захотілось додати CSS в XML-файл Sitemap'у по типу WordPress'івського. Йдемо в /usr/lib/python2.7/site-packages/django/contrib/sitemaps/templates/ (в Fedora такий шлях) і копіюємо sitemap.xml в свій template каталог проекту. Робимо потрібні зміни в ньому і маємо щастя.

авторизація через соціальні мережі в Django

Дуже класний сервіс, який позбавляє головного болю з САБЖом - loginza.ru
Для Django розроблений апп під нього. Все запрацювало з першого разу. Єдине що поправив в обробнику сигналів, то авторизувати неперевірених, а в мануалі вказано що авторизувати лише перевірених

def loginza_auth_handler(sender, user, identity, **kwargs):
    try:
        # it's enough to have single identity verified to treat user as verified
        #loginza_models.UserMap.objects.get(user=user, verified=True)
        loginza_models.UserMap.objects.get(user=user)
        auth.login(sender, user)
    except loginza_models.UserMap.DoesNotExist:
        sender.session['users_complete_reg_id'] = identity.id
        return redirect(reverse('users.views.complete_registration'))

ThreadedComment і TOP10 найкоментованіших об'єктів

Чомусь розробники не написали такої корисної API. Моє рішення, мабуть тимчасове, бо треба буде потестувати на швидкість.

post_type = ContentType.objects.get_for_model(Post)
counts = ThreadedComment.objects.filter(content_type=post_type).values('object_id').annotate(Count('object_id')).order_by()[:10]
counts = [ ( Post.objects.get(pk=cc['object_id']), cc['object_id__count']) for cc in counts ]

а як прикольно це виглядає у одній стрічці :))

counts = [ ( Post.objects.get(pk=cc['object_id']), cc['object_id__count']) for cc in ThreadedComment.objects.filter(content_type=ContentType.objects.get_for_model(Post)).values('object_id').annotate(Count('object_id')).order_by()[:10] ]


No Django support installed in selected interpreter

Давненько не пітонив. Скачав IDE Pycharm і отримав зразу САБЖ-помилку:



Вирішується додаванням потрібного інтерпретатора:

Перевірялка Апача на живучість

#!/bin/bash

PATH=/bin:/usr/bin
THEDIR=/tmp/apache-watchdog
EMAIL=blablabla@gmail.com
mkdir -p $THEDIR

if ( wget --timeout=30 -q -P $THEDIR http://localhost/robots.txt )
then
# we are up
touch ~/.apache-was-up
else
# down! but if it was down already, don't keep spamming
if [[ -f ~/.apache-was-up ]]
then
# write a nice e-mail
echo -n "apache crashed at " > $THEDIR/mail
date >> $THEDIR/mail
echo >> $THEDIR/mail
echo "Access log:" >> $THEDIR/mail
tail -n 30 /var/log/httpd/access_log >> $THEDIR/mail
echo >> $THEDIR/mail
echo "Error log:" >> $THEDIR/mail
tail -n 30 /var/log/httpd/access_log >> $THEDIR/mail
echo >> $THEDIR/mail
# kick apache
echo "Now kicking apache..." >> $THEDIR/mail
/etc/rc.d/init.d/httpd stop >> $THEDIR/mail 2>&1
killall -9 httpd >> $THEDIR/mail 2>&1
/etc/rc.d/init.d/httpd start >> $THEDIR/mail 2>&1
# send the mail
echo >> $THEDIR/mail
echo "Good luck troubleshooting!" >> $THEDIR/mail
mail -s "apache-watchdog: apache crashed" $EMAIL < $THEDIR/mail
rm ~/.apache-was-up
fi
fi
rm -rf $THEDIR


(c) десь нагуглено і допиляно під федору

Perl date regexp

while () {
if ($_ =~ /^(.+) (.+) (.+) (\[.+\]) (\".+\") (\d+) (\d+)$/) {
($remote, $hz1, $hz2, $date_a, $url, $code,$size) = ($1,$2,$3,$4,$5,$6,$7);
if ($date_a =~ /^\[(.+)\/(.+)\/(\d+):(\d+):(\d+):(\d+)/) {
($day,$month,$year,$hour,$minute,$second) = ($1,$2,$3,$4,$5,$6);
}
}
}

домашній стаціонарник xorg.conf

Section "Monitor"
Identifier "Monitor0"
VendorName "Asus"
ModelName "VW222U"
HorizSync 30.0 - 83.0
VertRefresh 55.0 - 75.0
ModeLine "1680x1050_60.00" 147.14 1680 1784 1968 2256 1050 1051 1054 1087 -hsync +vsync
EndSection


Section "Device"
Identifier "Device0"
Driver "nvidia"
VendorName "NVIDIA Corporation"
BoardName "GeForce 7300 GT"
EndSection

ection "Screen"
Identifier "Screen0"
Device "Device0"
Monitor "Monitor0"
DefaultDepth 24
Option "TwinView" "0"
Option "TwinViewXineramaInfoOrder" "CRT-0"
Option "metamodes" "1680x1050_60.00 +0+0"
SubSection "Display"
Depth 24
EndSubSection
EndSection

zoneminder: підключення звичайної веб-камери

Штатна ноутбучна запустилась відразу, а "Bus 003 Device 003: ID 046d:08da Logitech, Inc. QuickCam Messanger" не захотіла. Тому, що ця камера працює в v4l версії 1, а не у версії 2, яка стоїть за замовчуванням. У файлі: /usr/bin/zmdc.pl робимо відповідні зміни

delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
$ENV{LD_PRELOAD} = '/usr/lib/libv4l/v4l1compat.so';
my @daemons = (
'6zmc',
'zma',
'zmf',
'zmfilter.pl',
'zmaudit.pl',
'zmtrigger.pl',
'zmx10.pl',
'zmwatch.pl',
'zmupdate.pl',
'zmtrack.pl'
);

D-Link DIR-620

Давненько не танцював з бубном біля девайсів. Погрався сьогодні з САБЖом.
- відразу потрібно міняти прошивку на нову;
- при використанні USB-модему потрібно заводити інтерфейс і активовути його як по замовчуванні;
- при налаштуванні Wi-Fi потрібно вказувати канал, ні у якому разі не Auto;
- після зміни любого параметру обов'язково перезавантажувати роутер;

Ці поради стосуються не тільки конкретної моделі, а і багатьох девайсів цього бренду

Perl, threads, tcp port checker

Давненько не програмував, але вирішив згадати Perl на одній задачці, вірніше, її частині.
Є текстовий файл з проксями (2000 штук), які треба перевірити для початку на елементарну живучість хоста і сервіса. По одному перевіряти - займе пів дня. Тут приходять на поміч threads (нитки чи потоки). 200 потоків, який кочегарять 10 проксяків кожен, і того справляються з задачею за одну хвилину.


slugify не працює з unicode стрічками

а щоб працював, є чудовий пакет Unidecode

і приклад:
from django.template.defaultfilters import slugify
import unidecode
from unidecode import unidecode
#...
task_object.slug = slugify(unidecode(u'%s' % name))

зміна queryset в формі

Є три моделі з наступними зв'язками і форма:

class Task(models.Model):
location = models.ForeignKey(Locations, related_name='Task_location')
buyer = models.ForeignKey(UserProfile, related_name='Task_buyer')

class Locations(models.Model):
user = models.ForeignKey('registration.UserProfile', \
verbose_name=_('user'), related_name="user_locations")

class UserProfile(User):

class TaskForm(ModelForm):

class Meta:
model = Task
fields = ('location', 'name', 'description')


Тобто, в таск привязаний до юзера і юзерського локейшена. Форма мені виводить всі локейшени всіх юзерів, що не є добре. Хочу щоб виводились лише рідні локейшени юзера.

Рішення просте. Перевизначити init TaskForm'и і змінити queryset поля location
У вюшці в конструктор TaskForm передаємо request.user, бо нам треба буде фільтрувати по ньому Locations
views.py
@login_required
@render_to('')
def task_add(request, template="tasks/add.html"):
if request.method == 'POST':
form = TaskForm(request.POST, user=request.user)
if form.is_valid():
task = form.save(request.user, 1)
TaskStatus.objects.create(task=task, task_status=1)
return redirect("home")
else:
form = TaskForm(user=request.user)
return {'form': form,}, template

forms.py
class TaskForm(ModelForm):

class Meta:
model = Task
fields = ('category', 'location', 'name', 'description', 'price')

def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(TaskForm, self).__init__(*args, **kwargs)
self.fields['location'].queryset=Locations.objects.filter(user=self.user)

def save(self, user=None, task_status=None, commit=True):
task_object = super(TaskForm, self).save(commit=False)
task_object.buyer = user
task_object.task_status = task_status
if commit:
task_object.save()
return task_object

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

Часто потрібно посилати користувачу якісь повідомлення про помилки чи успішні операції. Нутром почув, що є вже готове рішення і таки так. Переклад куска мануала по цьому: http://habrahabr.ru/blogs/django/87473/

розширення моделі User

Часто трапляється, що крім імені користувача потрібно зберегти ще багато даних, для цього треба розширювати вбудовану модель User. Оптимальне рішення: http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/

авторизація в соціальних мережах

Коротко і зрозуміло в себе в блозі розписав один з доробників socialauth, дописавши можливість авторизуватись на сервісах популярних в СНД.

sorl-thumbnail

https://github.com/sorl/sorl-thumbnail
Для створення аватарок та різних маніпуляцій з малюнками прямо з шаблона - в самий раз.

В моєму випадку є 2 аватарки, одна 100х100, друга 50х50, то друга створюється на льоту за допомогою sorl-thumbnail

приклад:
{% load thumbnail %}
{% thumbnail r4.from_user.avatar "50x50" crop="center" as im %}
<img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
{% endthumbnail %}

методи в моделі та виклик їх з шаблонів

є дві моделі, в одній опис задачі, в іншу скаладються статуси до цієї задачі. Потрібно визначити поточний статус задачі, тобто зробити вибірку в TaskStatus по конкретній задачі і обчислити максимальне значення.

class Task(models.Model):
name = models.CharField(max_length=120)
category = models.ForeignKey('TaskCategory')
description = models.TextField()
price = models.DecimalField(max_digits=5, decimal_places=2, blank=True)
buyer = models.ForeignKey(Profile, related_name='buyer')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

class Meta:
verbose_name = _('Task')
verbose_name_plural = _('Tasks')
ordering = ['created_at']

def __unicode__(self):
return self.name

def current_status_str(self):
task_status = TaskStatus.objects.filter(task=self)
current_status = task_status.aggregate(Max('task_status'))
return TaskStatus.TASK_STATUS_VALUES[current_status['task_status__max']-1][1]

def current_status_int(self):
task_status = TaskStatus.objects.filter(task=self)
current_status = task_status.aggregate(Max('task_status'))
return current_status['task_status__max']

def get_absolute_url(self):
return reverse('tasks_details', args=[self.pk])

class TaskStatus(models.Model):
POSTED, ASSIGNED, COMPLETED, CLOSED = range(1,5)
TASK_STATUS_VALUES = (
(POSTED, 'Posted'),
(ASSIGNED, 'Assigned'),
(COMPLETED, 'Completed'),
(CLOSED, 'Closed'),
)
task = models.ForeignKey(Task)
task_status = models.IntegerField(choices=TASK_STATUS_VALUES, blank=True, null=True)

provider = models.ForeignKey(User, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)

def timesince(self, now=None):
return timesince_(self.created_at, now)

def __unicode__(self):
return unicode(self.task)

і в шаблоні звертаємось до потрібного метода
{% for task in tasks %}
{{ task }}
{{ task.current_status_str }}
{% empty %}
no tasks yet
{% endfor %}


і результат:

timesince і модна дата

зараз модно дату писати в такому форматі: Review to Юзернейм from Юзернейм 2 hours, 44 minutes ago

реалізація в моделі:
from django.utils.timesince import timesince as timesince_

class Review(models.Model):
to_user = models.ForeignKey('users.Profile', verbose_name=_('from user'), \
related_name="review_to_user")
from_user = models.ForeignKey('users.Profile', verbose_name=_('to user'), \
related_name="review_from_user")
timestamp = models.DateTimeField(_('timestamp'), auto_now_add=True)

class Meta:
verbose_name = _('rating')
verbose_name_plural = _('ratings')
ordering = ['timestamp']

def timesince(self, now=None):
return timesince_(self.timestamp, now)


def __unicode__(self):
return unicode("Review to %s from %s %s ago" % \
(self.to_user, self.from_user, self.timesince()))


def get_absolute_url(self):
return reverse('reviews_reviews', args=[self.to_user])

choices значення в шаблоні

class TaskStatus(models.Model):
POSTED, ASSIGNED, COMPLETED, CLOSED = range(1,5)
TASK_STATUS_VALUES = (
(POSTED, 'Posted'),
(ASSIGNED, 'Assigned'),
(COMPLETED, 'Completed'),
(CLOSED, 'Closed'),
)
task = models.ForeignKey(Task)
task_status = models.IntegerField(choices=TASK_STATUS_VALUES, blank=True, null=True)


Класичний вивід в шаблоні task_status видасть int значення.
Щоб вивести асоційоване ім'я, в шаблоні потрібно писати наступну конструкцію:
{% for ts in task_status %}
{{ ts.get_task_status_display}}
{% endfor %}

django debug

Простий і швидкий варіант переглянути вміст змінної, кортежу, об'єкту...:
import pprint


assert False, pprint.pformat(object)
assert False, pprint.pformat(object.__dict__)
assert False, pprint.pformat(object.__dict__.keys())
assert False, pprint.pformat(object.__dict__.values())

джоіни

є моделі:
class Task(models.Model):
name = models.CharField(max_length=120)
buyer = models.ForeignKey(Profile, related_name='buyer')

class TaskStatus(models.Model):
POSTED, ASSIGNED, COMPLETED, CLOSED = range(1,5)
TASK_STATUS_VALUES = (
(POSTED, 'Posted'),
(ASSIGNED, 'Assigned'),
(COMPLETED, 'Completed'),
(CLOSED, 'Closed'),
)
task = models.ForeignKey(Task)
task_status = models.IntegerField(choices=TASK_STATUS_VALUES, blank=True, null=True)


треба зробити вибірку з TaskStatus але відфільтрувати по Task.buyer, тобто зробити JOIN-запит. Порився трошки в гуглі, не особливо щось знайшов. От і думаю, вот ORM не така вже універсальна! Але стандартні засоби все ж є:
TaskStatus.objects.filter(task__buyer=request.user)

django fixtures

Наскільки зручна річ...
Коли проект розростається, додаються нові аплікейшени і роширюються моделі, інколи простіше витерти чи обнулити файл бази даних. Тоді з нуля потрібно заводити користувачів, постити якісь дані, на що йде додатковий час. Фікстури дозволяють при manage.py syncdb догружати потрібні дані.
Для цього в потрібному аплікейшені створюємо каталог fixtures і зберігаємо JSON (також можливі інші формати) файл даних з іменем initial_data.json
Для того щоб зробити повний дамп даних проекту, потрібно виконати manage.py dumpdata > dump.json Потім я в PyCharm привожу код до читабельного вигляду, вирізаю потрібні дані і роблю вищеописані дії.
Приклад:
[
{
"pk": 64,
"model": "auth.permission",
"fields": {
"codename": "add_attachment",
"name": "Can add attachment",
"content_type": 22
}
},
{
"pk": 65,
"model": "auth.permission",
"fields": {
"codename": "change_attachment",
"name": "Can change attachment",
"content_type": 22
}
},
{
"pk": 66,
"model": "auth.permission",
"fields": {
"codename": "delete_attachment",
"name": "Can delete attachment",
"content_type": 22
}
},
{
"pk": 67,
"model": "auth.permission",
"fields": {
"codename": "delete_foreign_attachments",
"name": "Can delete foreign attachments",
"content_type": 22
}
},
{
"pk": 1,
"model": "auth.group",
"fields": {
"name": "attachments",
"permissions": [64, 65, 66, 67]
}
},
]

чим додаємо гурт і права до нього

керування користувачами, гуртами та правами

Досить мало про це написано в документації.
Наглядний приклад: http://parand.com/say/index.php/2010/02/19/django-using-the-permission-system/

int значення в radio форму



class ReviewForm(ModelForm):
RATING_CHOICES = ((1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'),)
rate = forms.TypedChoiceField(choices=RATING_CHOICES, \
widget=forms.RadioSelect, coerce=int)

def clean_rate(self):
if self.cleaned_data['rate'] >= 1 and self.cleaned_data['rate'] <= 5:
return self.cleaned_data['rate']
else:
raise forms.ValidationError(_("Rating error"))

class Meta:
model = Reviews
fields = ('rate', 'message', 'content_type', 'object_id')
widgets = {
'content_type' : forms.HiddenInput(),
'object_id' : forms.HiddenInput()
}

Передавання у формах значень у вигляді hidden полей

Є модель
class Reviews(models.Model):
object_id = models.PositiveIntegerField(_('object id'))
content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
object = GenericForeignKey('content_type', 'object_id')
rate = models.DecimalField(decimal_places=1, max_digits=2)
user = models.ForeignKey(User, verbose_name=_('user'))
message = models.TextField(_('message'))
timestamp = models.DateTimeField(_('timestamp'), auto_now_add=True)
def __unicode__(self):
return unicode(self.user)

при роботі з формами треба object_id і object_id передавати у hidden полях і також ініціалізувати їх при створенні форми.
forms.py
class ReviewForm(ModelForm):

class Meta:
model = Reviews
fields = ('rate', 'message', 'content_type', 'object_id')
widgets = {
'content_type' : forms.HiddenInput(),
'object_id' : forms.HiddenInput()
}


views.py
    if request.method == "POST":
form = ReviewForm(request.POST)
if form.is_valid():
r = form.save(commit=False)
r.user = request.user
r.save()
return redirect('home')
else:
ct = ContentType.objects.get_for_model(user)
form = ReviewForm(initial={'content_type': ct.pk, 'object_id': user.id})

coercing to Unicode: need string or buffer, Task found

Цікаву САБЖ помилку дебажив.
class Task(models.Model):
name = models.CharField(max_length=120)
description = models.TextField()
price = models.DecimalField(max_digits=5, decimal_places=2)
buyer = models.ForeignKey(User, related_name='buyer')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.name

class TaskStatus(models.Model):
POSTED, ASSIGNED, COMPLETED, CLOSED = range(1,5)
TASK_STATUS_VALUES = (
(POSTED, 'Posted'),
(ASSIGNED, 'Assigned'),
(COMPLETED, 'Completed'),
(CLOSED, 'Closed'),
)
task = models.ForeignKey(Task)
task_status = models.IntegerField(choices=TASK_STATUS_VALUES, blank=True, null=True)
provider = models.ForeignKey(User, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.task


лікується:
    def __unicode__(self):
return unicode(self.task)

Сигнали в Django

Простенький приклад. При створенні об'єкту моделі, необхідно в автоматично створити об'єкт в сусідній моделі.

class Task(models.Model):
name = models.CharField(max_length=120)
description = models.TextField()
price = models.CharField(max_length=5)
promotional_code = models.CharField(max_length=24)
city = models.CharField(max_length=24)
is_active = models.BooleanField(default=True)
buyer = models.ForeignKey(User)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.name

class TaskStatus(models.Model):
POSTED, ASSIGNED, COMPLETED, CLOSED = range(1,5)
TASK_STATUS_VALUES = (
(POSTED, 'Posted'),
(ASSIGNED, 'Assigned'),
(COMPLETED, 'Completed'),
(CLOSED, 'Closed'),
)
task = models.ForeignKey(Task)
task_status = models.IntegerField(choices=TASK_STATUS_VALUES, blank=True, null=True)
provider = models.ForeignKey(User, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)

def update_task_status(sender, instance, *args, **kwargs):
TaskStatus.objects.create(task=instance, task_status=1)

models.signals.post_save.connect(update_task_status, sender=Task)

fedora 14 boot screen

Дефолтова тема якась дуже страшна. Міняються теми за допомогою plymouth (який потрібно при потребі доставити)
перелік тем: /usr/share/plymouth/themes/
# plymouth-set-default-theme <назва_теми>
# cd /usr/libexec/plymouth/
# ./plymouth-update-initrd

в /etc/grub.conf ще бажано вказати ядру бажану роздільну здатність. Потестити: vbetest
моя vga=773

периферійний зір

коли сконцентровано програмую в навушниках, периферійний зір вимикається повністю.

додавання значення поля форми через перевизначення ModelForm

ситуація наступна: є форма, але передається весь комплект значень окрім task.buyer, який береться з авторизації. Перевизначаємо save, де і зберігаємо task.buyer.
views.py
@render_to('')
def tasks_add(request, template="tasks/add.html"):
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
task = form.save(request.user)
return HttpResponseRedirect('/tasks/details/%s' % task.id)
else:
form = TaskForm()

return {'form': form,}, template

forms.py
class TaskForm(ModelForm):
class Meta:
model = Task
fields = ('name', 'description', 'price',)

def save(self, user=None, commit=True):
task = super(TaskForm, self).save(commit=False)
task.buyer = user
if commit:
task.save()
return task

ініціалізація значення поля форми

для простих речей можна використати initial, наприклад, автозаповнення дати.
Тут трошки інший підхід. Ініціалізація динамічного значення форми на стадії ініціалізації ModelForm
views.py
@render_to("messages/send.html")
def message_send(request, user_id=None):
form = MessageSendForm(message_to='sdsd')
return {"form" : form}

forms.py
class MessageSendForm(forms.ModelForm):
message_to = forms.CharField(label=_("Message to"), max_length=100, required=True)

class Meta:
model = Message
fields = ('message_to','subject','text',)

def __init__(self, *args, **kwargs):
message_to = kwargs.pop('message_to',None)
super(MessageSendForm, self).__init__(*args, **kwargs)
self.fields["message_to"].initial = message_to


На виході маємо:



трошки пізніше покажу наглядний приклад використання цієї конструкції

приклад роботи з context в templatetags

register = template.Library()
@register.inclusion_tag("users/_tasks_profile.html", takes_context=True)
def tasks_profile(context):
import re
request = context['request']
if re.search('/tasks/details/\d+', request.path):
task = Task.objects.get(id = int (request.path.split('/')[3]))
user = User.objects.get(id = task.buyer_id)
return {'profile': user}
else:
return False

attachments для моделі

https://github.com/bartTC/django-attachments
Дуже класний апп!

Виключалка CSRF

settings.py
class IgnoreCsrfMiddleware(object):
def process_request(self, request):
request.csrf_processing_done = True

MIDDLEWARE_CLASSES = (
'settings.IgnoreCsrfMiddleware',
'django.contrib.csrf.middleware.CsrfMiddleware',
...
)

PyCharm

взуває NetBeans

не юзайте django-facebookconnect

Гамно, старе і глючне

глюк в django-facebookconnect

отакий глюк:
  File "../lib/facebookconnect/models.py", line 26, in 
from facebook.djangofb import Facebook,get_facebook_client
File "../lib/facebook/djangofb/__init__.py", line 490, in
require_oauth = updater(require_oauth)
File "../lib/facebook/djangofb/__init__.py", line 489, in updater
return decorator.new_wrapper(updated, f)
AttributeError: 'module' object has no attribute 'new_wrapper'


Готового вирішення так і не нагуглив. Поміг манагер:
facebook/djangofb/__init__.py
    def updater(f):
def updated(*args, **kwargs):
original = f(*args, **kwargs)
def newdecorator(view):
return updated(original(view), view)
return updated(newdecorator, original)
return updated

форма з вибірковими полями моделі

def profile_profile(request, template="users/profile.html"):
profile = request.user
REQUIRED_FIELDS = ['first_name', 'last_name', 'email',]
FormClass = forms.models.modelform_factory(Profile, fields=REQUIRED_FIELDS)
form = FormClass(instance=profile)
if request.method == "POST":
form = FormClass(request.POST, instance=profile)
if form.is_valid():
profile = form.save()
profile.save()
return {'form': form,}, template

django-registration унікальний e-mail

хороший аплікейшен, але в ньому є один баг або фіча - дозволяє реєстрацію користувачів з однаковими e-mail'ами. Лікується додаванням метода clean_email:

forms.py
from django.contrib.auth.models import User
class RegistrationForm(forms.Form):
def clean_email(self):
"""
Validate that the supplied email address is unique for the
site.

"""
if User.objects.filter(email__iexact=self.cleaned_data['email']):
raise forms.ValidationError(_("This email address is already in use.\
Please supply a different email address."))
return self.cleaned_data['email']

auth_views.login після успішної логінації редіректить на /model/profile

А це реально кумарить
Лічиться додаванням в конфіг змінної:

LOGIN_REDIRECT_URL = '/'

Django Sitemap клас для Articles

### sitemap.py

from articles.models import Article

class ArticlesListSitemap(Sitemap):
changefreq = "weekly"
priority = 0.9
def lastmod(self, obj):
return obj.publish_date
def location(self, obj):
d = obj.publish_date.strftime("%Y")
return '/articles/' + d + '/' + obj.slug + '/'
def items(self):
return Article.objects.filter(is_active=True)

Модуль статтей в Django

http://pypi.python.org/pypi/django-articles/1.6a
Досить класний. Напильником працювати майже не прийшлось.

Модульне тестування

Ніколи раніше не писав тестів, але тепер відчуваю в них необхідність. Треба братись.

Django Sitemap з пустим items

Теж погано документована фіча.
Треба додати елемент в sitemap, але без прив'язки до моделі. В даному випадку це розділ "Контакти" з моделлю, але мені не потрібна індексація схованих даних.

Рішення

sitemap.py
from django.contrib.sitemaps import Sitemap

class ContactsSitemap(Sitemap):
changefreq = "weekly"
priority = 0.5
def lastmod(self, obj):
return datetime.datetime.now()
def location(self, obj):
return '/contact/'
def items(self):
return [self]


urls.py

sitemaps = {
#'flatpages': MyFlatPageSitemap,
'contact': ContactsSitemap,
}

Django FlatPageSitemap priority і changefreq

по замовчуванні FlatPageSitemap генерить лише loc, а priority і changefreq треба прописувати самому. Причому, в документації про це нічого немає, або я сліпий. Гугл теж дуже скупо описує. Танці з бубном 10 хв. і рішення знайдено.

sitemap.py

from django.contrib.sitemaps import FlatPageSitemap

class MyFlatPageSitemap(FlatPageSitemap):
def priority(self, item):
if 'about' in str(item.get_absolute_url).lower():
return 0.8
if 'clients' in str(item.get_absolute_url).lower():
return 0.3
if 'services' in str(item.get_absolute_url).lower():
return 0.8
else:
return 0.2
def changefreq(self, obj):
return "weekly"

urls.py

from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
from sitemap import *

sitemaps = {
#'flatpages': FlatPageSitemap,
'flatpages': MyFlatPageSitemap,
}

urlpatterns = patterns('',
(r'^sitemap\.xml', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
)

Гламурчик в photologue

Хороше і сире водночас jQuery рішення для photologue http://djangosnippets.org/snippets/1897/ Щоб фотки гарно гортались і слайдшоу.
Довелось підпиляти напильником як цей сніпєц, так і сам photologue, але вийшло симпатично.
Шукаємо:
{% for photo in gallery.public %}

Міняємо:
{% for photo in object.public %}


Ну і якщо не відразу зрозуміло, то пиляти треба файл шаблонів: /gallery_detail.html
А також треба позаливати в static/js всі jQuery плуги.

Теми в mc

Часто треба щось правити в mcedit, а кольори стандартні реально дістали.
Стандартні менюшки цього не дозволяють, але можна залізти в .mc/ini і підлаштувати все як потрібно.
шукаємо секцію Colors і правимо base_color
Мене ця заперла

[Colors]
base_color=lightgray,blue:normal=blue,default:selected=white,brightblue:marked=yellow,default:markselect=yellow,gray:directory=brightblue,default:executable=brightgreen,default:link=cyan,default:device=brightmagenta,default:special=lightgray,default:errors=red,default:reverse=green,default:gauge=green,default:input=white,gray:dnormal=green,gray:dfocus=brightgreen,gray:dhotnormal=cyan,gray:dhotfocus=brightcyan,gray:menu=green,default:menuhot=cyan,default:menusel=green,gray:menuhotsel=cyan,default:helpnormal=cyan,default:editnormal=green,default:editbold=blue,default:editmarked=gray,blue:stalelink=red,default





Редагувати конфіг треба іншим редактором, бо запущений mcedit перезапише конфіг на вихідний.

6 класичних SEO помилок

1. Продвижение "главной"

Не спорю, это главная страница вашего сайта, но это не значит, что вы хотите продвигать её по слову "главная". Вы же не ставите ссылку с таким анкором, когда покупаете постовые или обзоры. Так почему же на вашем сайте должна стоять такая сквозная ссылка?

Главная страница сайта должна продвигаться по тематическим ключевым словам. Например, если вы блоггер-манимейкер, поменяйте этот сквозняк в меню на слово "блог", "seo блог" или "блог о заработке".

2. Robots.txt и 301 редирект

Данная ошибка встречается гораздо чаще, чем хотелось бы, на просторах интернета. Суть её в следующем. Допустим, Яндекс определяет главным адрес с www (например, это было прописано в директиве Host в robots.txt), а потом веб-мастер установил 301 редирект в htaccess с него на адрес без www.

Приходит индексирующий робот и пытается проиндексировать сайт по адресу с www. Однако, при попытке индексации, робот перенаправляется на сайт без www при помощи серверного редиректа. Для робота это означает, что первичную страницу, с которой установлен редирект, нужно не индексировать, а заменить на страницу, на которую ведёт редирект. Продолжая попытки проиндексировать сайт, робот бегает по кругу, пока соединение не разрывается по таймауту. В результате в индексе остаётся обычно только главная страница, а на Сёрче появляется топик "Почему мой СДЛ попал под АГС?!".

3. Дублирование мета тегов

На всех страницах вашего сайта разный контент (по крайней мере, лучше бы это было так). Каждая страница предоставляет посетителю какую-то определённую полезную информацию. Так почему у каждой страницы должно быть одно и то же описание в мета?

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

4. Читать дальше

Вариаций данного словосочетания в сети довольно много: "Подробности", "Читать здесь", "Читать полностью" и т.д. Конечно, данная формулировка призывает читателя кликнуть по ссылке или перейти к более полному варианту статьи. Но поисковикам "читать дальше" мало о чём говорит.

Если у вас имеется текст "Нажмите здесь, чтобы перейти к статье 5 ошибок в SEO, которые нужно перестать допускать", сделайте анкором гиперссылки "5 ошибок в SEO, которые нужно перестать допускать". Это поможет поисковику понять, что ссылка ведёт на страницу, которая рассказывает о SEO ошибках. А вообще, старайтесь всегда правильно использовать перелинковку.

5. Прикрепил и забыл

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

С приходом новых терминов меняются старые ключевые слова. Вряд ли, например, в наше время вы получите трафик, продвигая страницу по запросу "румяна для ланит". Стратегии меняются достаточно быстро не только в бизнесе, но и в поисковой оптимизации.

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

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

Только в умелых руках seo-инструменты выявят проблемы и потенциальные возможности вашего сайта.

6. Нетерпеливость

Принцип "хочу всё и сразу" в SEO, увы, не работает. Вывод сайта в топ по ключевым словам это сложный и кропотливый процесс. Некоторым сайтам этот "топ" вообще не суждено увидеть.

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

Старайтесь не допускать этих 6 ошибок при оптимизации ваших веб-ресурсов!

взяв: http://seo-aspirant.ru/6-seo-mistakes

django-photologue Фото-галерея

http://code.google.com/p/django-photologue/ класний апп. Все запрацювало з першого разу. Переживаю лише щоб не було проблем на продакшені.

Caught UnicodeDecodeError while rendering: 'utf8' codec can't decode

Інколи десь символ якийсь нелюдський проскочить в шаблоні і паришся :)
Добре видно ці какахи це в mc-edit.

cpanel

Ніколи не користуйтесь простим редактором DNS-зон

Django'вський Денвер

http://bitnami.org/stack/djangostack
Теж для новачків буде корисно

Django Інтернет-магазини

http://www.satchmoproject.com/
http://www.getlfs.com/

Мені колись довелось розбиратись з CreLoaded, то це був просто жах. Все розкидано по різних папках, в бізнес-логіці купа HTML-коду, брррр... Але загалом розібрався :)

Щодо магазинів, які написані на хороших фреймворках, то тут набагато приємніше в них ковирятись. Буде більше часу, розберусь з цими, напишу свої враження.

Організація коду в Django

Дуже раджу почитати, особливо початківцям.
http://pyobject.ru/blog/2006/09/11/django-code-layout/

матюки в коді

Эндрю Воз (Andrew Vos) провел интересное исследование, заключающееся в анализе частоты использования ненормативной лексики, грубых выражений и ругательств в текстах примечаний к Git-коммитам на GitHub, в привязке к различным языкам программирования. Всего было проанализировано около миллиона коммитов, в которых было выявлено 210 ругательных слов. Лидируют по числу ругательств C++, JavaScript и Ruby.


по наводці Frulik'a

robots.txt в Django


### urls.py
from django.views.generic.simple import direct_to_template


#...
url(r'^robots\.txt$', direct_to_template, {'template': 'robots.txt'},)
#...


і в каталог з темплейтами кладемо robots.txt

Створення Django форм прямо з адмінки



http://formunculous.org/

пакетна нормалізація звуку в avi файлах

Є старий DVD-плеєр в публічному місці, який не має функції shuffle (випадкового програвання файлів з плей-ліста), крім того, старі відео-файли мають різний рівень звуку, що створює певні незручності.
Погуглив віндові рішення, але нічого толкового і безкоштовного не знайшов. Тож лінукс виручає, як завжди.


#!/bin/bash

IN_DIR="music_in"
OUT_DIR="music_out"

for i in `ls $IN_DIR`; do
##### extract MP3 file
ffmpeg -i $IN_DIR/$i $OUT_DIR/$i.mp3
##### normalize MP3 file
normalize -m -v $OUT_DIR/$i.mp3
##### remove MP3 from AVI
ffmpeg -i $IN_DIR/$i -vcodec copy -an $OUT_DIR/$i.avi
##### merge AVI and new normalized MP3
ffmpeg -i $OUT_DIR/$i.avi \
-i $OUT_DIR/$i.mp3 \
-vcodec copy \
-acodec copy \
$OUT_DIR/$i.new.avi
##### remove temp files
rm -f $OUT_DIR/$i.mp3
rm -f $OUT_DIR/$i.avi
##### change filename to random
mv $OUT_DIR/$i.new.avi $OUT_DIR/$RANDOM.avi
done

xml парсер

Постачальник міняв в себе щось в бухглатерії і ексельовський прайс змінив формат, старі парсери перестали працювати. Конвертую ексель файл у xml-документ, далі формую потрібний файлик даних розділений табуляторами.

import xml.dom.minidom
from xml.dom.minidom import Node

f = open('out.txt','w')
doc = xml.dom.minidom.parse("Neo110217.xml")
Row = doc.getElementsByTagName ( 'Row' )
for rows in Row:
i = 0
buffer = []
for cell in rows.getElementsByTagName ( 'Cell' ):
for data in cell.getElementsByTagName ( 'Data' ):
if (data.firstChild):
buffer.append(data.firstChild.data.encode('utf-8'))
i += 1
if (i == 12):
j = 0
buffer.insert(5, '0')
buffer.insert(5, '0')
buffer.insert(9, '0')
buffer.insert(9, '0')
buffer.insert(13, '0')
buffer.insert(15, '0')
buffer.insert(17, '0')
for b in buffer:
f.write("%s\t" % b.strip())
j += 1
f.write("\n")

'ascii' codec can't decode byte 0xe0

При спробі доступитись до медіа/статичних файлів, виникає САБЖ помилка.
Лікарство: http://victor-k-development.blogspot.com/2010/07/unicodedecodeerror-django.html?showComment=1295949774000#c844394053254047980