02 февраля 2014

Django - кот в мешке.

Django это фреймворк, при помощи которого быстро пишутся сайты. Так ли это?
Фичи джанги:
  • Админка
  • куча аппликэйшинов
  • Шаблоны
  • ORM
  • DebugToolbar

 

 

 

 

Админка

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

 

Аппликашины

Обычно очень посредственного качества и не совместимы друг с другом, поэтому танцы с бубном и велосипеды наше всё + djangosnippets

 

Шаблоны

  • нету break, continue  
 jinja2.ext.loopcontrols This extension adds support for break and continue in loops.
  • рекурсия Jinja
    <ul class="sitemap">
    {% for item in sitemap recursive %}
        <li><a href="{{ item.href|e }}">{{ item.title }}</a>
        {% if item.children -%}
            <ul class="submenu">{{ loop(item.children) }}</ul>
        {% endif %}</li>
    {% endfor %}
    </ul>
     а в джанге это видимо не нужно
  • не работает вызов функций типа item() (лолшто?)
  • [:-1] не работает, нужно писать |latest
  • для js выражение типа: 
         var foo = [{% for item in items %}{{ item.paren }}{% endfor %}]
        не работает, потому что js выполняется до рендеринга таких {%%} конструкций.
  • коменты {# #} неработают в несколько строк
  • вместо {{ list[5] }} -> {{ arr.5 }}
  • для словарей {{ dict.foo }} не работает, да здравствует template tag
  • для itertools.chain можно сосать лапу с таким фокусом {{ list[5] }}
  • о джанга ты прекрасна {{ myval|add:"-5" }} вместо уродского {{ myval - 5 }}
  • нельзя сделать так {{ dir(foo) }}
  • как узнать длинну itertools? в jinje делается так {{ list(foo).length }}

 

ORM

  • Page.objects.filter(parent__in=objects) вместо DBSession.query(Page).filter(parent in objects)
  • великолепный foomodel_set__barmodel__name лолшто?!
  • Entry.objects.all()[:1].get() вместо DBSession.query(Entry).first() или one()
  • добавляем в модель: class Meta: app_label = 'asdasda' и переходим к разделу дебаг!

Debug

Дебаг джанго кормит вас! Потому что вы Django программист и половину рабочего времени пытаетесь понять что за !@#$% произошла, получая за это зарплату.

class Meta: app_label = 'asdasda' генерит трейс:
django.core.management.base.CommandError: One or more models did not validate:
gallery.galleryimage: 'gallery' has a relation with model <class 'gallery.models.Gallery'>, which has either not been installed or is abstract.
Шыкарные красные ошибки джанги. Очень информативно, ищется аникейством и вспоминанием чё правил.

Я запилил небольшой пример что бы вы могли поднять его и посмотреть на эту магию.

Квик старт
  1. Установка по pip install -r requirements.txt
  2. Далее введим python manage.py syncdb и видим ImportError: cannot import name SEOModel Супер информативный вывод, все сразу стало понятно.
    • 2.1 Прошел все модели в проекте, SEOModel не нашел, при учете что у меня gallery стоит в virtualenv
    • 2.2 может во вью? Во вью то же нету
    • 2.3 ищем по проекту тупо по всем файлам, нету.
    • 2.4 идем в settings/apps.py и смотрим какое г может это содержать(так на угад), смотрим common опа вот он в моделях, осталось найти где он импортируется неправильно.
    • 2.5 ради интереса запускаю сервер python manage.py runserver и получаю:
      File ".../local/lib/python2.7/site-packages/gallery/models.py", line 12, in <module> from website.models import SEOModel, VisibleModel ImportError: cannot import name SEOModel 
т.е. в некоторых случаях джангу надо дебажить runserver'ом

Pages
  1. захожу в pages в админке
  2. добавляю дерево
  3. тыкаю по дереву(вложенность 2го уровня) что бы развернуть список и получаю:
Error while loading the data from the server.







  



и трэйс:
Internal Server Error: /admin/pages/page/tree_json/
Traceback (most recent call last):
  File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 115, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django_mptt_admin/admin.py", line 50, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django/utils/decorators.py", line 91, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django/views/decorators/cache.py", line 89, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django/contrib/admin/sites.py", line 202, in inner
    return view(request, *args, **kwargs)
  File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django_mptt_admin/admin.py", line 157, in tree_json_view
    node = self.model.objects.get(id=node_id)
  File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django/db/models/manager.py", line 143, in get
    return self.get_query_set().get(*args, **kwargs)
  File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django/db/models/query.py", line 404, in get
    self.model._meta.object_name)
DoesNotExist: Page matching query does not exist.
[16/Dec/2013 23:42:28] "GET /admin/pages/page/tree_json/?node=5&_=1387215746550 HTTP/1.1" 500 22668

бесполезный трейс и непонятная ошибка КРУТО!
Если я зайду на адрес /admin/pages/page/tree_json/?node=5&_=1387215746550 то получу статический trace и хер знает куда bp вставить что бы хоть за че-то зацепиться.

Ставлю django-extension через pip и wergzeug что бы получить хоть какой то интерактив на том же трэйсе.

Запускаю командой python manage.py runserver_plus, отваливается debug_toolbar. Но и интерактивный трейс мало чем помогает в этом случае. Джанга каким то чудом обходит то место которое все ломает, ГРЕБАННЫЙ СТЫД! Ошибка ищется тупым эникейством. Предполагая что django идеальна и в ней нет ошибок, django_mptt_admin на example работает хорошо, может быть что то в Pages??? Ад же какойто?

24 комментария:

  1. > В сайте среднего уровня жангоадминка уже не прокатит.

    Еще как прокатит. Не для сайтов "под заказ" с требованием к интуитивной админке, а для обычных "продуктовых компаний" джанго-админка просто неоценима.

    ОтветитьУдалить
  2. > Обычно очень посредственного качества и не совместимы друг с другом, поэтому танцы с бубном и велосипеды наше всё + djangosnippets

    Снова-таки. Вот как простейший пример -- gargoyle. Подключаешь -- получаешь функциональность фича-свитчей плюс админку. Да, пусть она не идеальна в интерфейсе, но с т.з. экономии времени на разработку сама по себе эта вещь уже экономит неделю-другую времени (попробуй по-быстрому что-то подобное написать со всеми фронт-эндами и бэк-эндами -- быстро надоест, по крайней мере я уж точно этим заниматься не хотел бы).

    ОтветитьУдалить
  3. В остальном (особенно ОРМ) согласен, код джанги оставляет желать лучшего, и множество неочевидных моментов также. Ну, вроде что-то народ пилить продолжает, проект не стоит на месте, одно решение проблемы с моделью юзеров чего стоит. Плюс миграции скоро будут "встроенными". Так что, желаем проекту развития в правильном направлении.

    ОтветитьУдалить
    Ответы
    1. Юзеры да это конечно реально проблема аж с 2007 года была, аж просто кастыли какие-то. Видимо джанга страдает из-за своего гигантизма. А вчём профит встроенных миграций?

      Удалить
  4. Вполне обоснованный вопль души, но, как говорил герой Леся Подэрэвянских, "яка цьому разумная альтернатива?". Где же та серебряная пуля которая которая спасет python разработчиков? Ответа на этот вопрос я не увидел.

    ОтветитьУдалить
  5. Люби джанго!!!!
    http://youtu.be/LoIJ4W7kXiQ

    ОтветитьУдалить
  6. > для js выражение типа:
    > var foo = [{% for item in items %}{{ item.paren }}{% endfor %}]
    > не работает, потому что js выполняется до рендеринга таких {%%} конструкций.
    Вы что курили? JS рендерится ДО того, как рендерится шаблон на сервере? мдааааа

    > вместо {{ list[5] }} -> {{ arr.5 }}
    а почему нет? вас смущает точка? или вы видимо хотели в квадратные скобки вставить индекс-переменную? ага...

    > для словарей {{ dict.foo }} не работает, да здравствует template tag
    вы явно чтото курили

    > нельзя сделать так {{ dir(foo) }}
    в шаблнах логика представления, а не бизнес логика

    ОтветитьУдалить
  7. > коменты {# #} неработают в несколько строк
    {% comment %} че никак? печатать долго? используйте code templates

    ORM и шаблонизатор можно заменить на что угодно. и jinja и алхимия - ради бога.

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

    ОтветитьУдалить
    Ответы
    1. можно то оно можно и джинджу и алхимию - но пофигачатся совместимость с аппликухами.

      Удалить
  8. > Entry.objects.all()[:1].get()
    Это что за ересь?
    Entry.objects.all()[:1] # может так?
    Удалили бы вы пост а? и не позорились бы

    ОтветитьУдалить
    Ответы
    1. > LeoK
      В вас столько энергии, это так хорошо!

      script>
      var foo = ({% for item in foo %}'{{ item }}',{% endfor %}0);
      alert(foo);
      alert('{{foo}}');
      /script>
      {{ foo }}

      Удалить
    2. Вы вообще знакомы с http?))) JS рендерится на клиенте, в браузере! Как могут СЕРВЕРНЫЕ шаблоны джанги рендерится после JS? (шок)

      Удалить
    3. Ну ок, с анонимом не поспорить, можно я вычеркну этот пункт )

      Удалить
    4. Да тут одним пунктом не обойдётесь однозначно. тут надо все в утиль и выпрямить руки, извиняюсь за резкость

      Удалить
  9. тоже отказался от джанги - юзаю фласк и пирамиду. джанга удивительно топорна местами.
    орм страшнее атомной войны
    шаблоны более чем средненькие
    реально полезных аппликух хорошо если десяток.
    я немного подпилил инит у пирамиды и стало просто отлично. близкий метод в этом блоге пробегал - у меня немного и ная структура
    щас вот крупный проект на пирамиде с развидой админкой(dojo) - радует

    ОтветитьУдалить
    Ответы
    1. но юниору есть свои бонусы

      Удалить
    2. >бесполезный трейс и непонятная ошибка КРУТО!
      Как это бесполезный??? Всё же написано:
      DoesNotExist: Page matching query does not exist.

      Видимо есть у вас модель Page, и где то вы пытаетесь вызвать для нее метод get не подумав обернуть вызов в блок try/except искомая запись не была найдена, соответственно джанга сгенерировала исключение.

      Удалить
    3. > джанга удивительно топорна местами
      У вас напильник не того размера просто.

      > реально полезных аппликух хорошо если десяток
      У джанги сотни полезных аппликух включая девелоперские, рассылки, оплаты, автовризация, парсинг, комментарии, асинхронными задачи. Практически для всего что есть в открытом доступе есть 1,2,3 аппликухи.

      >бесполезный трейс и непонятная ошибка КРУТО!
      По мне так тоже все ясно, во въю пытается сделать get по несуществующим параметрам, идем во въю и смотрим что там происходит.
      >File "/home/uralbash/.virtualenvs/django-hyango/local/lib/python2.7/site-packages/django_mptt_admin/admin.py", line 157, in tree_json_view
      node = self.model.objects.get(id=node_id)
      вот прямо написано на чем поломалось

      Удалить
    4. > анон
      у вас очень правильное направление мышления, осебенно про try/except, но к сожалению не завершенное. То что какой то get где то там что то видимо изза неподумав исключил потомушта get. Это так оно, но где чё править то?

      > node = self.model.objects.get(id=node_id)
      > вот прямо написано на чем поломалось
      или вот прямо здесь return self.get_query_set().get(*args, **kwargs)
      илиили здесь self.model._meta.object_name)

      закончите свой ход мыслей(репа с кодом есть)

      я же вам помогу таким примером:
      1) есть такой апп django-filebrowser, добавьте в нем объект и допишите в начале пути к файлу "/", ну например так "uploads/foo.jpg" -> "/uploads/foo.jpg"
      2) предайте объект в шаблон и напишите следующее img src="{% version foo_obj.file 'small' %}" />
      3) далее появится понятный трейс с ошибкой:
      Attempted access to '/uploads/foo.jpg' denied

      Если не знать 1 пункт и некие секреты посвящённых "контрибьюторами", то может возникнуть много вопросов что означает "Попытка доступа". Посмотрим откуда это исключение, а оно из django/core/files/storage.py которого в понятном трейсе нет.
      функция:
      def path(self, name):
      ----try:
      --------path = safe_join(self.location, name)
      ----except ValueError:
      --------raise SuspiciousOperation("Attempted access to '%s' denied." % name)
      ----return os.path.normpath(path)
      далее нужно закоментить try/except и получим новые подробности.. а знаете как я это нашел? при помощи pdb, удачи вам.

      Удалить
  10. Просто у Дмитрия случился разрыв шаблона :) После пирамиды, джинджа и алхимии. Джанго, нативные темплейты и ОРМ.

    ОтветитьУдалить
  11. > То что какой то get где то там
    Это не какой-то гет где то там, а вполне конкретный в конкретной view. Надо просто знать что DoesNotExists генерится им. Точно так же как dict выкидывает KeyError. Информация конкрентая где и что сломалось осталось пойти и понять почему.

    > raise SuspiciousOperation("Attempted access to '%s' denied." % name)
    Выброшен кастомный Exception. Лезешь по странному пути, и путь. Какую инфу вам надо предоставить чтобы было понятнее? Программист знал и предусмотрел что здесь могут быть некорректные данные и предупреждает об этом. Дескать проверь что там у тебя. При этом выше это можно перехватить и не ломать все приложение, А отключить то место где это используется. И это правильно. Ошибка не критическая, остальная функциональность должна работать.

    ОтветитьУдалить
    Ответы
    1. > Информация конкрентая где и что сломалось осталось пойти и понять почему.
      А помните ту серию из саучпарка где гномы воруют трусы. У них были замечательные аргументы...

      где чё править то, что бы заработало?

      Удалить
    2. > где чё править то, что бы заработало?
      Вы как шестилетка, дайте и быстро и чтобы работало.
      Вам указано, сломалось здесь, почему сломалось, разбирайтесь. Я стесняюсь спросить, а как вообще чинить баги? Ну если ни идти к источнику проблемы?

      Удалить
    3. > ув. ping
      решение проблемы я знаю, но путь к источнику проблемы - это аникей технология(с чем я в пирамиде, пайлонсе или web2py не сталкивался). Хотелось что бы вы нашли проблему и описали свою последовательность действий (как это делается правильно).

      Удалить