Метка: django

  • Создание панели для Django Debug Toolbar

    Велосипед не мой, я лишь объяву разместил.

    У меня пока не хватает кармы на хабре, чтоб раздавать инвайты за полезные статьи, так что приходится, пока просто копировать в «бложик», ибо они совсем выпадут из выдачи гугла.


    Создание панели для Django Debug Toolbar

    При отладке джангопроектов становиться необходимостью просмотр различной отладочной информации.
    На вопрос отладки в полной мере отвечает приложение Django Debug Toolbar. Воспользуемся возможностью расширения функционала DDT и создадим панель для собственных нужд.

    Создание панели

    Так как системы контроля версий повсеместно используются для работы над проектами, время от времени возникает необходимость проверить когда тот или иной коммит был добавлен.
    По причине того, что я использую git, данная панель будет взаимодействовать именно с этой vcs.
    Примером при написании панели послужило приложение django-git, созданное с целью замены gitweb.
    В качестве обертки для git был использован модуль GitPython.
    Сначала напишем модуль с производным классом от DebugPanel (к примеру, в директории с каким-нибуть из приложений), назовем его panels.py:

    import os
    import time
    
    from git import *
    from debug_toolbar.panels import DebugPanel
    from django.template.loader import render_to_string
    
    class GitStatusPanel(DebugPanel):
    	name = 'Git'
    	has_content = True
    
    	def nav_title(self):
    		return self.name
    
    	def title(self):
    		return self.name
    
    	def url(self):
    		return ''
    
    	def content(self):
    		repo_path = '../GitPython'
    		repo = Repo(repo_path)
    		return render_to_string('debug_toolbar/panels/commits.html', {'commits': repo})Code language: Python (python)

    repo_path — относительный путь к репозиторию.
    Так как GitPython возвращает время добавления коммита в формате unixtime, напишем templatetag для конвертации даты в нужную форму.

    app_name/templatetags/cdate.py:

    import time
    
    from django import template
    
    register = template.Library()
    
    @register.filter
    def cdate(value):
    	date = time.strftime("%d.%M.%Y, %H:%M:%S", time.localtime(value))
    	return dateCode language: Python (python)

    Затем, создадим шаблон страницы в директории с остальными шаблонами DDT (templates/debug_toolbar/panels/commits.html) следующего содержания:

    	{% load i18n %}
    	{% load cdate %}
    	 <table>
    		 <thead>
    			 <tr>
    				 <th>Commit </th>
    				 <th>Date </th>
    				 <th>Author </th>
    			 </tr>
    		 </thead>
    		 <tbody>
    			{% for commit in commits.iter_commits %}
    				 <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
    					 <td><a href="/__debug__/commit/{{commit}}" class="remoteCall">{{commit}} </a></td>
    					 <td>
    						{{commit.committed_date|cdate}}
    					 </td>
    					 <td>
    						{{commit.committer.name}}, {{commit.committer.email}}
    					 </td>
    				 </tr>
    			{% endfor %}
    		 </tbody>
    	 </table>Code language: HTML, XML (xml)

    В результате получилась панель со списком коммитов:

    Так как задачей было также отобразить список действий в рамках коммитов, напишем соответствующую страницу.
    Правим urls.py:

    urlpatterns = patterns('',
    	...
    	(r'^__debug__/commit/([a-z0-9]+)$', show_commit),
    )Code language: Python (python)

    Добавим функцию show_commit к вьюхам приложения app_name (app_name/views.py) или к вьюхам проекта (views.py):

    def show_commit(request, commit):
    	repo_path = '../GitPython'
    	repo = Repo(repo_path)
    	commit_item = repo.commit(commit)
    	commit_diffs = commit_item.diff()
    	changes = []
    	for blob in commit_item.tree:
    		try:
    			changes.append({'path': blob[0].path, 'content': blob[0].data_stream.read()})
    		except:
    			changes.append({'path': blob.path, 'content': blob.data_stream.read()})
    	return render_to_response('debug_toolbar/panels/commit.html', {'commit': commit, 'commit_item': commit_item, 'commit_diffs': commit_diffs, 'changes': changes})Code language: Python (python)

    Воспользовавшись API модуля GitPython получили список изменений, а с помощью свойств объекта blob — путь к модифицированным файлам и их raw-содержимое.
    И наконец, создадим шаблон, templates/debug_toolbar/panels/commit.html:

    {% load i18n %}
    <div class="djDebugPanelTitle">
    	<a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a>
    	<h3>Commit {{commit}}</h3>
    </div>
    <div class="djDebugPanelContent">
    	<div class="scroll">
    	{% for diff in commit_diffs %}
    		<code><pre>{{diff}}
    
    
    {% if diff.deleted_file %}
    File was removed.
    {% endif %}
    {% if diff.new_file %}
    File was created.
    {% endif %}
    {% if diff.rename %}
    File was renamed: {{diff.rename_from}} → {{diff.rename_to}}
    {% endif %}
    {% endfor %}
    {% if commit_diffs %}
    Modified files
    
    {% for change in changes %}
    file: {{change.path}}
    
    {% autoescape off %}{{change.content}}{% endautoescape %}
    
    
    {% endfor %}
    {% endif %}Code language: Python (python)

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

    При необходимости, можно добавить подсветку синтаксиса с помощью pygments.

    Материалы

    Страница проекта на github — https://github.com/robhudson/django-debug-toolbar,
    статья о создании своей панели — http://blog.akei.com/post/3600587187/creating-a-custom-panel-for-the-django-debug-toolbar.
    страница модуля GitPython — https://github.com/gitpython-developers/GitPython,
    страница приложения django-git — https://github.com/sethtrain/django-git.

    PS Песочница хабра

  • django regroup пример

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

    в хтмл это примерно вот так

    <SELECT NAME=browser>
        <OPTGROUP LABEL="Firefox">
          <OPTION LABEL="2.0 or higher">
            Firefox 2.0 or higher
          </OPTION>
          <OPTION LABEL="1.5.x">Firefox 1.5.x</OPTION>
          <OPTION LABEL="1.0.x">Firefox 1.0.x</OPTION>
        </OPTGROUP>
    </select>Code language: HTML, XML (xml)

    Допустим у меня есть 2 вот таких модели в вакууме.
    Одна: категория — Firefox (как в примере выше.)

    class Category(models.Model):
        name = models.CharField(max_length=100)
        
        def __unicode__(self):
                return self.nameCode language: Python (python)

    И вторая не посредственно список, где есть поле указывающее на связь с родителем

    class SubCategory(models.Model):
        category_name = models.ForeignKey(Category)
        name = models.CharField(max_length=200)
        
        def __unicode__(self):
                return self.nameCode language: Python (python)

    Едем дальше.
    в PHP, я бы в цикле прорисовки категорий поставил бы ещё один цикл выбирающий всех детей для данной категории. (хаха — много запросов ну и ладно)

    Но тут у нас всё интереснее, в притом намного.
    Мы во views должны сформировать что-то и отправить в шаблон уже как-то почти готовое для отображения.

    Хочется городить велосипед, но тут Документация Django какбэ подсказывает

    что есть такая хреновая, которая мне поможет.

    Суть её в том, что тот массив что придёт к нам она может пересортироватьперегруппировать по любому полю, и поможет нам красиво его отобразить.

    В итоге во views.py я делаю выборку того, что мне нужно.
    Например вот так:

    sub_category_list = SubCategory.objects.all()Code language: Python (python)

    И всё вот это мы отправляем тихо мирно в шаблон.
    А в шаблоне нас встретит уже магический regroup

    Вот с такой конструкцией

    {% regroup sub_category_list by category_name as categ_list %}
         <select id="id_sub">
         <option value="">-----</option>
      {% for ca in categ_list %}
          <optgroup label="{{ca.grouper}}">
                 {% for item in ca.list %}
              <option value="{{ item.id }}"> {{ item.name }}</option>
                 {% endfor %}
          </optgroup>
      {% endfor %}
         </select>Code language: Python (python)

    Теперь по порядку.

    {% regroup sub_category_list by category_name as categ_list %}Code language: Python (python)

    Группируем sub_category_list по полю category_name и называем результат categ_list
    Дальше создаём html элемент, тут я думаю всё понятно.

    и начинаем разбирать то, что у нас в categ_list

    {% for ca in categ_list %}Code language: Python (python)

    Тут рассказывать ничего не надо, перебираем элементы по порядку

    <optgroup label="{{ca.grouper}}">Code language: Python (python)

    Тут мы выводим то почему мы группировали {{ca.grouper}}

    {% for item in ca.list %}Code language: Python (python)

    Вложенный цикл, обходим то, что внутри группы (тут нам помогает ca.list )

    <option value="{{ item.id }}"> {{ item.name }}</option>Code language: Python (python)

    Собственно выводим всю эту красоту в конечный элемент

                 {% endfor %}
          </optgroup>
      {% endfor %}Code language: Python (python)

    Вот и завершили все наши дела.
    и получили нечто похожее на селект рутреккера тот, что в самом верху.

    просто красиво и со вкусом.

  • Связанные списки select в django (ajax select linked)


    Потратив часа 3 на поиск рабочего решения связанных полей (Это когда в зависимости от категории нужно показывать под категорию) спешу поделиться чтоб не забыть

    в urls.py

    (r'^ajax/categ/$', 'proj.list.views.feeds_subcat'),Code language: Python (python)

    во Вьюхе

    def feeds_subcat(request):
        from django.core import serializers
        json_subcat = serializers.serialize("json", SubCategory.objects.filter(category_name=request.GET['id']))
        return HttpResponse(json_subcat, mimetype="application/javascript")Code language: Python (python)

    Примерно рассказываю что там происходит
    В модели SubCategory выбираем имя субкатегории, которая соответствует ид_категории, что к нам привалило.
    всё это оборачиваем в JSON попутно подгружаем библиотеки для этих дел.
    и отправляем прямо в формате js всё обратно.

    теперь создаём ещё один js файл называем его как душе угодно (и не забываем подключить)

    <script src="/static/js/categ.js" type="text/javascript"></script>Code language: HTML, XML (xml)
      $(function(){
        $("select#id_categ").change(function(){
          $.getJSON("/ajax/categ/",{id:+$(this).val()}, function(j) {
            var options = '<option value="">---------- </option>';
            for (var i = 0; i < j.length; i++) {
              options += '<option value="' + parseInt(j[i].pk) + '">' + j[i].fields['name'] + '</option>';
            }
            $("#id_sub").html(options);
            $("#id_sub option:first").attr('selected', 'selected');
            $("#id_sub").attr('disabled', false);
          })
          $("#id_categ").attr('selected', 'selected');
        })
      })Code language: JavaScript (javascript)

    Что он делает?
    находит элемент select с id = «id_categ»
    При изменении которого вызывает функцию $.getJSON, которая долбится по урле /ajax/categ/ гет запросом и параметром ?id=то_что_выбрано
    Если всё отлично выполняется функция j
    Которая и меняет значение во втором селекте.
    #id_categ — ИД_селекта категории
    #id_sub — ИД_селекта подкатегории

  • django devserver статические файлы ( django devserver static files)

    Во время разработки нужно чтоб статика обрабатывалась dev сервером, всякие файлы стилей js и т.п.

    Для решения этой задачи я использую вот это:

    urls.py

    (r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/home/django/list/static'}),Code language: Python (python)

    в корне проекта создаю каталог static, в котором создаю подкаталоги js images style и т.п

  • Django ajax пример ( django ajax autocomplite)

    Продолжаю понемногу разбираться в Django

    Дошло до авто заполнения полей, пару дней перечитывал все «Интернеты» о разных вариантах и методах, о готовых примерах и т.п.

    И так начнём я воспользовался jQuery плагином Autocomplete
    Всё это я брал вот тут
    Плагин совсем не полный ищет так себе похоже просто старый.
    Поэтому идём и берём самый новый ..

    тут оно будет работать без основного jQuery

    Но на других страницах проекта, есть более полные версии и более красивые.
    Вот их и качаем
    Из них нам нужны
    в папочку css кидаем

    indicator.gif
    jquery.autocomplete.cssCode language: HTML, XML (xml)

    в папочку js кидаем

    jquery-1.5.min.js
    jquery.autocomplete.min.jsCode language: HTML, XML (xml)

    Поехали, предположим, что у нас есть в urls.py

    (r'^asearch/$', 'man.views.ajax_search'),Code language: Python (python)

    man.views.ajax_search — я думаю рассказывать не надо, что это своё приложение и Вьюха его

    во views.py

    def ajax_search(request):
         if request.method == 'GET':  
             GET = request.GET  
             if GET.has_key('q'):
                 q = request.GET.get( 'q' )
                 search = Task.objects.all()
                 results = search.filter(name__contains = q)
                 matches = ""
                 for result in results:
                     matches = matches + "%s\n" % (result.name)
                 return HttpResponse(matches, mimetype="text/plain") 
     Code language: Python (python)

    В данном примере мы будет общаться через обычный get запрос.
    Можно сделать «не обычным»
    например так

    if request.is_ajax():
    q = request.GET.get( 'q' )Code language: Python (python)

    Но об этом попробую ещё написать, когда буду более тонко в этом разбираться
    В модели Task мы делаем поиск по полю «name».
    Потом всё это выстраиваем в красивый список с переносами строк
    в итоге в переменной matches у нас будет уже структурированный список.

    Теперь на стороне клиента.

    Во первых подключаем плагин со стилями и jquery

    <script src="/static/js/jquery-1.5.min.js" type="text/javascript"></script>
    <script src="/static/js/jquery.autocomplete.min.js" type="text/javascript"></script>
    <link type="text/css" rel="stylesheet" href="/static/css/jquery.autocomplete.css">Code language: HTML, XML (xml)

    Добавим простую функцию jQ

    <script type="text/javascript">
       $(document).ready(function() {  
           $("#q1").autocomplete("/asearch/");  
        });  
    </script>Code language: HTML, XML (xml)

    Тут мы говорим плагину: находим элемент с id=»q1″ и будем с ним работать.
    А всё запросы отправлять на урл «/asearch/»

    дальше собственно сам элемент

    <input id="q1" type="text" />Code language: HTML, XML (xml)

    Вот собственно и всё.