Записи по тегу “django”.

django-sendmail

Не используйте email backend в django, если конечно вам достаточно smtp. А если нет? Вот я лучше прибегну к серверной команде sendmail для отправки почты, чем буду использовать smtp для приложения.

При этом подходе мы получаем возможность очереди отправки, нам не надо писать отдельный скрипт и ложить его в крон задачу. Улучшается производительность системы за счет того, что операций запроса, авторизации и отправки прямо из приложения нет. Вместо этого весь контент писем идет сплошным текстом к созданному sendmail каналу (pipe).

Приведу очень простой пример:

from django.conf import settings
from django.utils.encoding import smart_str
import os

def sendmail(to, subject, body):
p = os.popen('/usr/bin/sendmail -t', 'w')
p.write("MIME-Version: 1.0\n")
p.write("Content-Type: text/html;charset=utf-8\n")
p.write("To: %s\n" % to)
p.write(smart_str("From: %s\n" % settings.EMAIL_FROM))
p.write(smart_str("Subject: %s\n" % subject))
p.write("\n")
p.write(smart_str(body))
p.close()

Такой скрипт не будет работать на windows, я не видел пакета sendmail в этой операционной системе. Да и я не встречал серверов Windows, на которых бы хостились django приложения.

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

django теги для jccompressor

Буквально пару дней назад я выложил пакет с набором классов, которые позволяют сжимать скрипты в один файл, используя yui или google closure compiler. Продолжая развитие темы хочу сообщить что в пакете теперь присутствуют теги для django приложений. Я не буду расписывать как их использовать, это достаточно расписано в README файле репозитория. Вы еще не знаете в каком?

git@github.com:hash3g/jccompressor.git

Пользуйтесь на здоровье :)

Дайте гостю печеньку, он тоже ее достоин!

Допустим, у вас есть модель, которая хранит состояние в базе данных, данная модель связана с пользователями. Но как быть в случае неавторизованного пользователя? Хранить состояние в кукисах браузера. Для этого пришлось бы пробежаться по модулю и ставить проверки, добавлять новые методы, переменные. В итоге код превратится в сплошной if… else, а оно нам надо? «А что делать?» — спросите вы. (Надеюсь что не спросите =) Без паники! Я, как-а-вот, ваш спаситель, расскажу и покажу на выход.


Выход — сделать фейковую модель и орудовать ею, как и с обычной джанговской моделью.

Пример


class UserBasket(models.Model):
user = models.ForeignKey(User)
shakedtimes = models.IntegerField(default = 0)

Тогда нашей фейковой моделью будет


class AnonymousBasket(object):

def __init__(self, request, response):
self.shakedtimes = request.COOKIES.get('sk_times', '0')
self.response = response

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


def save(self):
self.response.set_cookie('sk_times', self.shakedtimes)

Красяво? Думаю да. Минусом выступает то что в фейковую модель необходимо передать объект запроса и ответа, но как вы собрались считывать и сохранять куки, а? А? То-то же.

Разбиение объектов на “равные” части

Предлагаю вашему вниманию небольшой снипет, который мы написали, когда понадобилось сделать вывод объектов по колонкам.

  1. from django import template
  2.  
  3.  
  4. register = template.Library()
  5.  
  6.  
  7. class SplitObjectsNode(template.Node):
  8.     def __init__(self, objects, list):
  9.         self._objects = template.Variable(objects)
  10.         self._list = list
  11.  
  12.     def render(self, context):
  13.         import math
  14.         columns_count = len(self._list)
  15.         objects = self._objects.resolve(context)
  16.  
  17.         for item in self._list:
  18.             context[item] = []
  19.  
  20.         if len(objects) == 0:
  21.             return
  22.  
  23.         dec = float(len(list(objects))) / columns_count
  24.         mid = int(math.ceil(dec))
  25.         pass_part = len(list(objects)) % columns_count
  26.         count_full_cols = columns_count – pass_part
  27.  
  28.         idx = 0
  29.         index = 0
  30.         for item in self._list:
  31.             step = mid
  32.             if idx >= pass_part and pass_part != 0:
  33.                 step = mid – 1
  34.             context[item] = objects[index: step + index]
  35.             index += step
  36.             idx += 1
  37.         return
  38.  
  39.  
  40. @register.tag
  41. def split_objects(parser, token):
  42.     """                                                                                                                                                                                                      
  43.    Split content of the objects to equal parts. Number of parts is a count of passed arguments to tag.                                                                                                      
  44.                                                                                                                                                                                                            
  45.    Example:                                                                                                                                                                                                
  46.    {% split_objects objects col1 col2 col3 %}                                                                                                                                                              
  47.    <table><tr><td>                                                                                                                                                                                          
  48.    {% for item in col1 %} {{ item.title }} {% endfor %}                                                                                                                                                    
  49.    </td><td>                                                                                                                                                                                                
  50.    {% for item in col2 %} {{ item.title }} {% endfor %}                                                                                                                                                    
  51.    </td><td>                                                                                                                                                                                                
  52.    {% for item in col3 %} {{ item.title }} {% endfor %}                                                                                                                                                    
  53.    </td></tr></table>                                                                                                                                                                                      
  54.    """
  55.     parts = token.contents.split()
  56.     if len(parts) < 3:
  57.         raise template.TemplateSyntaxError, "Tag should get at the least 3 arguments. Example usage is {% split_objects objects col1 col2 %}"
  58.  
  59.     tagname = parts.pop(0)
  60.     objects = parts.pop(0)
  61.     return SplitObjectsNode(objects, parts)

кваргиарги

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

  1. try:
  2.     if not objects:
  3.     objects = search_for(h_date__day = param)
  4. except:
  5.     objects = []
  6.        
  7. try:
  8.     if not objects:
  9.         objects = search_for(h_date__month = 11)
  10. except:
  11.     objects = []

Попробуем с этим что то сделать =) Для этого вспомним что такое kwargs и args и насколько полезными они часто бывают.

  1. objects = search_for(title__icontains = param)
  2.  
  3. if not objects:
  4.     objects = search_for(h_date__day = param)
  5.    
  6. if not objects:
  7.     objects = search_for(h_date__month = month)

Как видно я вынес одинаковую часть в отдельный метод. Посмотрим что делает этот метод:

  1. def search_for(*args, **kwargs):
  2.     try:
  3.         objects = MyModelObject.objects.filter(*args, **kwargs)
  4.     except:
  5.         objects = []
  6.     return objects

Ничего сверхъестественного, верно? А можно унифицировать метод и использовать его где захочется для простой фильтрации.

  1. def filter_for(model, *args, **kwargs):
  2.     try:
  3.         objects = model.objects.filter(*args, **kwargs)
  4.     except:
  5.         objects = []
  6.     return objects
  7.  
  8.  
  9. def func(request, param):
  10.     filter_for(MyModel, title_icontains = param)

Code completition для django проекта

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

Описываю как я подключал на своем Mac. По аналогии делается и на Windows машинах.

1. В Preferences эклипса есть раздел PyDev, внутри него таится Interpreter – Python. Что необходимо сделать это указать путь до вашего интерпретатора. Дальше он сам определит какие пакеты находятся в PYTHONPATH.

2. Открываете Preferences своего Django проекта. Чтобы заставить PyDev понимать его исходники как пакеты, достаточно выбрать в PyDev – PYTHONPATH Project Source Folders.

3. Чтобы наверняка в External Source Folders заносим путь до django директории.

Результат вот:

Django: изменение поля стандартного вида

Всем снова привет. Как это ни странно сейчас все мое внимание уделено исключительно разработке приложений основанных на web framework Django. Причиной перехода на django является разработке и ведению нового проекта, который наша компания делает для украинской компании. Надеюсь что мы выиграем с этим проектом.

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

В django 1.0 были сделаны некоторые очень полезные добавления, которых я ждал. Введение save_model метода в ModelAdmin класс избавило от написания менеджеров. Что же дает нам ModelAdmin для кастомизации полей?

Любой вам даст совет рыть исходники Django, и что собственно и было сделано. В ModelAdmin есть метод get_form который вернет экземпляр класса формы которая используется в административном интерфейсе. А что если переопределить эту функцию? Выйдет примерно следующее

  1. class MyModelAdmin(admin.ModelAdmin):
  2.     list_display = (‘title’, ‘latin_title’, ‘recipe’)
  3.     prepopulated_fields = {"url": ("latin_title", )}
  4.    
  5.     form = MyModelAdminForm
  6.        
  7.     def get_form(self, request, obj=None, **kwargs):
  8.         form = super(MyModelAdmin, self).get_form(request, obj=None, **kwargs)
  9.        
  10.         form.base_fields[‘custom’].widget = MyWidget(obj)
  11.         return form
  12.    
  13. admin.site.register(Medicine, MedicineAdmin)

То что доктор прописал, не так ли? Данного подхода в документации я не нашел, зато на сайте http://djangosnippets.org есть отличный пример, который использует подобную технику.

Отладка Django приложений в PyDev Eclipse

У меня получилось задебажить свое django приложение в Eclipse. И поэтому делюсь опытом с вами.

Что нам необходимо?

1. Среда разработки – Eclipse
2. Web framework – Django
3. Psyco
4. PyDev

Что нам нужно сделать?

1. Запустить Eclipse. Ставить PyDev рассказывать не буду и уж точно как django подымать у меня для вас язык не подвернется сказать. Дальнейшее рассказывается только с учетом того, что вы поставили все 4 компоненты и имеете уже созданный в Эклипсе проект

2. В меню Eclipse выбираем Run и Open Run Dialog…

3. Создаем новую конфигурацию Python Run. Называем ее как угодно, на рисунке у меня изображена пустая форма.

4. Вам необходимо заполнить диалог значениями. На рисунке пример рабочей конфигурации.

Но это не все, переходим на вкладку Arguments и вводим в поле Program arguments вот такую строку

runserver –noreload

Кто работает с django поймет что если нажать run то это будет эквивалентно manage.py runserver –noreload в командной строке, аля терминале ну или консоли (кому как нравится, я лично называю это консолью или терминалом) Жмем Run чтобы убедиться что все сделали правильно.

Все гуд и теперь смело можем открывать в браузере localhost:8000 и работать с проектом.

Но мы ведь не для того начали всю эту пляску. Мы хотим нормально работающий дебаггер. Поэтому продолжим. Что нам для этого надо? Объясняю как я вышел на дебаг. Логика простая. Если запускается сервер то должен запускаться и дебаггер. Хех. Жму в меню Run -> Open Debug Dialog… и выбираю созданную конфигурацию, Debug, облом…

Psyco not available for debugger speedups

Ну видимо PyDev уже позаботился о нас и поддерживает какой-то пакет, который по каким-то причинам не доступен. Если вы скачали уже его, то поставьте. Как ставить? На ваших плечах, у меня на Mac OS X я успешно скомпилил пакет и установил. Чего и вам советую. Простите меня, пользователи Windows =)

После всех мучений с установкой psyco, открываем manage.py нашего проекта и делаем воот такие изменения в нем

  1. #!/usr/bin/env python
  2. from django.core.management import execute_manager
  3. try:
  4.     import settings # Assumed to be in the same directory.
  5. except ImportError:
  6.     import sys
  7.     sys.stderr.write("Error: Can’t find the file ’settings.py’ in the directory containing %r. " +
  8.                      "It appears you’ve customized things.\nYou’ll have to run django-admin.py, " +
  9.                      "passing it your settings module.\n(If the file settings.py does indeed exist, "+
  10.                      "it’s causing an ImportError somehow.)\n" % __file__)
  11.     sys.exit(1)
  12.  
  13. if __name__ == "__main__":
  14.     import psyco
  15.     psyco.full()
  16.     execute_manager(settings)

Теперь запускаем дебагер и вуаля, без ошибок, ставим куда нить breakpoint и перезапускаем сервер. Открываем браузер, заходим на localhost:8000 и если все получилось удачно, то нас Eclipse спросит: ”А не открыть ли нам Debug перспективу?”

Какие минусы я заметил,

1. Во время сессии нельзя поставить новый брекпойнт, для это придется перезапускать консоль.
2. При таком подходе вы не сможете стартовать приложение из нормальной консоли. Чтобы это сделать, вам придется убрать те самые заветные две строки.

Остальное меня вполне устроило.