Ответы пользователя по тегу Django
  • Как в админке Django настроить codemirror на подсветку нескольких языков?

    Sergei_Erjemin
    @Sergei_Erjemin Автор вопроса
    Улыбайся, будь самураем...
    Для наложения режимов подсветки в CodeMirror нужно использовать CodeMirror.multiplexingMode. Причем последовательно для каждой пары открывающий/закрывающий тег. Таким образом для смешивания html+jinja2 в админке Django получится вот такой /static/js/codemirror/init_jinja2.js:

    // Этот файл нужен для инициализации html+jinja-редактора шаблонов codemirror в админке Django
    // рецепт написал сам: https://qna.habr.com/q/1284408
    (function () {
      var $ = django.jQuery;
      $(document).ready(function () {
        // Включаем "темную" или "светлую" тему в зависимости от настроек браузера пользователя
        var theme_is = 'idea';
        if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) theme_is = 'rubyblue';  // dark mode
    
        // Включаем подсветку jinja-тегов {{...}} внутри html
        CodeMirror.defineMode("html+jinja2{}", function (config) {
          return CodeMirror.multiplexingMode(
            CodeMirror.getMode(config, "text/html"), {
              open: "{{", close: "}}",
              mode: CodeMirror.getMode(config, "jinja2"),
              parseDelimiters: true,
            }
            );
        });
    
        // Включаем подсветку jinja2-тегов {%...%}
        CodeMirror.defineMode("html+jinja2%%", function (config) {
          return CodeMirror.multiplexingMode(
            CodeMirror.getMode(config, "html+jinja2{}"), {
              open: "{%", close: "%}",
              mode: CodeMirror.getMode(config, "jinja2"),
              parseDelimiters: true,
            }
            );
        });
    
        // Включаем подсветку jinja2-комментариев {#...#}
        CodeMirror.defineMode("html+jinja2", function (config) {
          return CodeMirror.multiplexingMode(
            CodeMirror.getMode(config, "html+jinja2%%"), {
              open: "{#", close: "#}",
              mode: CodeMirror.getMode(config, "jinja2"),
              parseDelimiters: true,
            }
            );
        });
    
        // инициализация codemirror
        $('#code_editor').each(function (idx, el) {
          var editor = CodeMirror.fromTextArea(el, {
            lineNumbers: true,
            tabSize: 2,
            mode: 'html+jinja2',
            gutters: ['CodeMirror-lint-markers'],
            theme: theme_is,
            lint: true,
            autoCloseTags: true,
            matchBrackets: true,
          });
          editor.setSize('120em', 'auto');
          editor.addKeyMap({
            'Ctrl-S': function (cm) {$(el).closest('form').submit(); },     // submit
            'Ctrl-F': 'findPersistent',       // поиск
          });
        });
      });
    })();
    Ответ написан
    Комментировать
  • Как запустить функцию бесконечного цикла в django celery при запуске сервера?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Советую вместо Celery использовать django-background-task ... Для фонового парсера более чем достаточно, параллелится по ядрам, можно управлять фоновыми задачами из админки, работает на той-же СУБД что и основной проект (можно и разнести, при желании). Сам же процесс django-background-task запускать при старте при старте системы через systemctl или rc.local
    Ответ написан
    Комментировать
  • Как победить «Raw query must include the primary key» в Django?

    Sergei_Erjemin
    @Sergei_Erjemin Автор вопроса
    Улыбайся, будь самураем...
    Непонятно почему, но срабатывает так:
    q_filial = TbPeople.objects.raw("SELECT DISTINCT web_tbpeople.szFilial, 1 AS id "
                                    "FROM web_tbpeople ORDER BY web_tbpeople.szFilial;")


    Ну и еще, надо следить какое поле -- первичный ключ. В моем случае ключ был szID и это строковая переменная. Таким образом для моего случая рабочий код вот такой:
    q_filial = TbPeople.objects.raw("SELECT DISTINCT web_tbpeople.szFilial, '1' AS szID "
                                    "FROM web_tbpeople ORDER BY web_tbpeople.szFilial;")


    !!!ПРИ ЭТОМ, ЕСЛИ НЕ СОЗДАВАТЬ ВТОРОГО ФИКТИВНОГО ПОЛЯ С КЛЮЧОМ, ТО УЖЕ НЕРАБОТАЕТ!!! НАПРИМЕР ВОТ ТАК:
    q_filial = TbPeople.objects.raw("SELECT DISTINCT web_tbpeople.szFilial AS szID"
                                    "FROM web_tbpeople;")

    !!ПАДАЕТ!!
    Ответ написан
    2 комментария
  • Как на MacOS правильно подключить Django к БД в Docker?

    Sergei_Erjemin
    @Sergei_Erjemin Автор вопроса
    Улыбайся, будь самураем...
    Пока разбирался, проделал несколько итерация установить/удалить/переустановить (примерно час заняло) -- поэтому не могу сказать, что конкретно сработало. Выводы такие:
    • mysql-connector-python -- не нужен. Похоже именно он вставал колом, и не давал коннекта в базу данных. Похоже, что его притащило в виртуальное окружение во время предыдущих экспериментов, и я его принял за зависимость..
    • симлинки не нужны -- mysqlclient отлично находит сокет базы.

    В результате последовательность действий:
    brew install mariadb-connector-c
    source ~/path-to-project-enveroment/bin/activate
    pip install mysqlclient
    
    # выключаем окружение отключаем системный коннектор (можно не делать)
    deactivate
    brew unlink mariadb-connector-c

    После обновления macOS до 13.0 Ventura потребуется предварительно сделать команду:
    xcode-select --install
    И повторить все еще один раз.

    P.S. Отдельную проблему создает то, что имя хоста на macOS может меняться. У меня код в Django проверяет на каком хосте запущен и подставляет соответствующие настройки в settings.py ... В моем случае, в hostname прописано m1, но проверка через Python показывает:
    import socket
    socket.gethostname()
    'm1.N1'

    Догадки, но кажется добавление N1 как-то связано с моделью домашнего интернет-роутера... Почему macOS его добавляет, а другие компы в сети нет -- загадка. Иногда hostname может становится 'm1.local' (и для этого не надо перезагружаться, достаточно просто чтоб компьютер перешел в режим сна... хотя х.з. ... в macOS гибридный режим сна, и если во время сна перегружается по питанию, внешне это никак не заметно). В общем, это странное поведение hostname тоже попортило немного крови. :)
    Ответ написан
    Комментировать
  • Как в Django переопределить метод «чтение» только в/для админки?

    Sergei_Erjemin
    @Sergei_Erjemin Автор вопроса
    Улыбайся, будь самураем...
    Делается в два действия:

    Во-первых,
    в models.py переопределяем метод save() для нашей модели TbTemplate. Заодно можем переопределить метод delete(), чтобы он ничего не удалял (или наоборот удалял не только запись в БД но и соответствующий файл... или удалял запись в БД, а соответствующий файл переименовывал...). Получим такую модель:
    # -*- coding: utf-8 -*-
    from django.db import models
    from my_app.settings import *
    
    
    class TbTemplate(models.Model):
        """ Шаблоны """
        szFileName = models.CharField(
            primary_key=True,
            db_index=True,
            unique=True, 
            verbose_name="Имя шаблона"
            )
        szJinjaCode = models.TextField(
            verbose_name='Шаблон',
            help_text='Код шаблона (jinja2)'
        )
        szDescription = models.CharField(
            max_length=100,
            verbose_name='Описание'
        )
    
        def __unicode__(self):
            return f"{self.szFileName} ({self.szDescription})"
    
        def __str__(self):
            return self.__unicode__()
    
        # переопределяем save() для записи шаблонов не только в ДБ, но и в файл
        def save(self, *args, **kwargs):
            with open(TEMPLATES_DIR / self.szFileName, "w+", encoding="utf-8") as tmplt_file:
                tmplt_file.write(self.szJinjaCode)
            super(TbTemplate, self).save(*args, **kwargs)
            # TODO: для продакшн, возможно, нужно добавить "дёргание" touch_reload и "моргнуть" uWSGI
    
        # переопределяем метод delete() (пока, не удаляется)
        def delete(self, *args, **kwargs):
            pass
            # super(TbTemplate, self).delete(*args, **kwargs)
    
        class Meta:
            verbose_name = '[…Шаблон]'
            verbose_name_plural = '[…Шаблоны]'


    Теперь, при изменении и создании шаблона в базе создастся соответствующий файл...

    Во-вторых,
    в файле admin.py при определении класса admin.ModelAdmin для управления моделью TbTemplate нужно переопределить метод get_fields() который отвечает за получения полей в форму админки (пришлось искать метод тупо пробуя кучу схожих которые делают что-то похожее, но не то). В результате получаем примерно вот такой вот admin.py:
    # -*- coding: utf-8 -*-
    from django.contrib import admin
    from web.models import TbTemplate
    from my_app.settings import *
    
    class AdminTemplate(admin.ModelAdmin):
        search_fields = ['szFileName', 'szDescription', 'szJinjaCode']
        list_display = ('szFileName', 'szDescription')
        list_display_links = ('szFileName', 'szDescription', )
        empty_value_display = '<b style=\'color:red;\'>—//—</b>'
        actions_on_top = False
        actions_on_bottom = True
    
        def get_fields(self, request, obj=None):
            try:
                with open(Path(TEMPLATES_DIR) / obj.szFileName, "r", encoding="utf-8") as template:
                    obj.szJinjaCode = template.read()
            except (AttributeError, FileNotFoundError, TypeError):
                pass
            return ['szFileName', 'szDescription', 'szJinjaCode']
    
    admin.site.register(TbTemplate, AdminTemplate)


    Теперь, если какие-то внешние силы изменят файл шаблона (или кто-то поменяет код шаблона в БД в обход админки) , то при открытии шаблона на редактирование из админке, данные будут получены из файла.

    Все.

    UPD: в settnig.py проекта надо добавить что-то типа:
    TEMPLATES_DIR = BASE_DIR / 'templates-jinja2'
    Чтоб модель и админка знали в какой каталог лить файлы шаблонов.
    Ответ написан
    Комментировать
  • Кто-нибудь сталкивался, что не отображается статика в Django 3.2 (но иногда отображается)?

    Sergei_Erjemin
    @Sergei_Erjemin Автор вопроса
    Улыбайся, будь самураем...
    Методом тыка выяснил, что почему-то в Django 3.2 не работает STATIC_ROOT ... При этом в документации ничего про эти изменения нет. В общем, заработало так:

    settings.py:
    from my_project.my_secret import *
    
    # ...
    # ...
    # ...
    
    STATIC_URL = '/static/'
    MEDIA_URL = '/media/'
    
    if DEBUG:     
        if socket.gethostname() == MY_HOST_HOME:      # домашний комп
            MEDIA_ROOT = MY_MEDIA_ROOT_DEV1
            STATICFILES_DIRS = [MY_STATIC_ROOT_DEV1, ]
        if socket.gethostname() == MY_HOST_WORK:      # офис комп
            MEDIA_ROOT = MY_MEDIA_ROOT_DEV2
            STATICFILES_DIRS = [MY_STATIC_ROOT_DEV2, ]

    В url.py ничего про каталог static не пишем вообще:
    if settings.DEBUG:
        urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

    В общем странно, похоже они в Django 3.2 отказались от STATIC_ROOT в пользу более гибкого STATICFILES_DIRS... Почему в документации про это не написано -- ещё страннее. И совсем странно, что если оставить присвоение STATIC_ROOT -- все ломается.

    А вот применение MEDIA_ROOT оставили без изменений, работает как прежде, и даже запись в url.py для него обязательна.
    Ответ написан
  • Как можно поменять местами элементы QuerySet?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Самое очевидное -- превратить QuerySet в list и в нем уже переставлять как тебе хочется, или собирать новый list...

    Либо писать raw-запрос на SQL с применением встроенных в твой SQL механизмов (два запроса -- товары на цену больше заданной, товары на цену меньше заданной... в каждый запрос добавлять какой-нибудь хитрый-ключ-номер-позиции, объединять эти запросы и результат сортировать по ключу)...

    Ещё вариант -- два QuerySet (товары на цену больше заданной, товары на цену меньше заданной). И из этих двух QuerySet собрать лист, поочерёдно беря записи из каждого QuerySet
    Ответ написан
    1 комментарий
  • Какие существуют недорогие хостинги с поддержкой python+django?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Во-первых, неплохая подборка на хабре, плюс ещё несколько в комментариях к публикации.

    Во-вторых, на мой вкус довольно экономно Yandex.Cloud -- Compute Cloud
    Непрерываемая виртуалка на два ядра -- 225 рублей в месяц (с публичным IP -- 335 рублей)... Плюс на старте дают 4000 рублей (кажется на два месяца) подарок и можно ещё много чего попробовать. Своя виртуалка -- сам себе хозяин.

    В-третьих, самое дешёвое -- сесть "на хвоста" к знакомым у которых есть безлимит-хостинг... Кстати, безлимит-холстинг -- если сразу на год брать -- тоже не дорого. Вот например коллокейшн на DreamHost -- 120$ в год, позволяет SSH и там уже ставь любые версии Python и Django... ограничения только в выборе CGI. У DreamHost используют Passenger, а он не во всем хорош -- например, приходится его "передёргивать" при обновлении кода (зато обновления шаблонов ловит моментально)... если хотите, могу вам организовать отдельный аккаунт внутри своего. Из минусов -- придётся просить меня делать изменения в DNS...
    Ответ написан
    Комментировать
  • Как сократить текст в админке Django?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    В модели добавляешь функцию усечения строки, например:

    def trim50(self):
            return u"%s..." % (self.szYouFileldNeedTrim[:50],)


    В админ вместо указания поля szYouFileldNeedTrim, которое при выводе ты хочешь укоротить, указываешь эту функцию trim50. При редактировании записи, будут подгружены реальные поля (т.е. в нашем случае szYouFileldNeedTrim), а в табличке записей синоним trim50
    Ответ написан
    1 комментарий
  • Почему удаляется parent запись?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Ты указываешь для ForeignKey models.CASCADE, вот она и удаляет все связные записи. Установи models.SET_NULL или models.DO_NOTHING, в зависимости от того какое поведение БД ты хочешь получить при удалении связанных записей...

    Документация: https://djbook.ru/rel3.0/ref/models/fields.html#ar...
    Ответ написан
  • Как исправить ошибку sqlite3 в django?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Что-то с django_date_extract... Вообще в SQLite немного странные обработки даты-времени, и в часовых поясах она от версии к версии путается, и в форматах... Я бы посмотрел в сторону так ли указано формирование даты-времени в твоём проекте, когда ты это дату-время "засовываешь" в виде срок в БД. На домашней и продашен-системе, форматы по умолчанию могут быть разные, и надёжнее все преобразования строк типа 2020-12-14 15:27:00.000 MSK в дату-время не получать БД, а делать на стороне Python...
    Ответ написан
    Комментировать
  • Как добавить множество категорий к одному товару?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Да.

    Для назначения категорий в Django отлично подходит батарейка django-taggit
    Ответ написан
  • Почему не отправляет письма Django + Google SMTP?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Гуглится, что не хватает:

    EMAIL_USE_SSL = False
    DEFAULT_FROM_EMAIL = EMAIL_HOST_USER

    или даже:
    EMAIL_USE_SSL = False
    SERVER_EMAIL = DEFAULT_FROM_EMAIL = EMAIL_HOST_USER

    И еще, возможно у тебя двухфакторная аутентификация, и тогда твое приложение не авторизовано... читать тут: https://support.google.com/accounts/answer/6010255 ... тогда нужно или отключить двухфакторую авторизацию, или приложение следует подписать и указать в сетингах ключи EMAIL_SSL_CERTFILE и EMAIL_SSL_KEYFILE ... или использовать не gmail, а еще что-то...

    Кроме того, чтобы не падало по таймауту -- поставить исключение на это событие, и/или использовать в сетингах EMAIL_TIMEOUT

    P.S. Оправка почты из приложения -- медленная штука. Даже если ничего не падает -- пользователь замечает подтормаживание (2-3 секунды и больше). Так что почту лучше отправлять асинхронно или в параллельном потоке. Например, с помощью модуля django_background_tasks ...
    Ответ написан
    Комментировать
  • Django - как настроить удаление ненужных файлов (из FileField) при удалении/изменении модели?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Тут посоветовали django-cleanup -- это действительно хорошее решение, но когда в каких-то моделях нужно удалять, а в других не нужно, или даже разное поведение с файлами в разных местах (где-то удалять файлы, а где-то оставлять), то есть другое решение:

    Допустим есть модель:
    class tb_icons(models.Model):
        image = models.ImageField(max_length=128, verbose_name=u"картинка")
        # ...
        # ...


    Если хочешь удалить запись из таблицы tb_icons и сопутствующий файл из image делаешь так (например, для удаления первой записи):

    tb_icons.objects.get(id=1).image.delete(save=True)
    tb_icons.objects.get(id=1).delete()


    Соответственно если save=False файл удаляться не будет.
    Ответ написан
    Комментировать
  • Django и множество фоновых заданий (manage команд), какая альтернатива CRON?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Есть батаерйка django-background-tasks для работы с фоновыми задачами. Очереди хранятся в основной базе, можно через стандартную django-админку управлять фоновыми задачами и пр. У меня в продакшене почти уже год. Порядка 10 млн. задач выполнено. Полет нормальный...
    Ответ написан
    Комментировать
  • При переходе в domain/static/admin/css/base.css выходит Page not found. Почему?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    Проверь, что присваивается в STATIC_ROOT . Часто на хостинге статика хранится совсем в другом месте и приходится задавать STATIC_ROOT руками. И даже больше, если статика хитро разбросана по разным серверам, еще и web-сервер настраивать чтобы он по нужным правилам проксировал запросы в нужные места.
    Ответ написан
  • Как переместить корень Django-проекта в подкаталог внутри проекта PyCharm?

    Sergei_Erjemin
    @Sergei_Erjemin Автор вопроса
    Улыбайся, будь самураем...
    Решение: В дереве проекта правой кнопкой мыши кликнуть папку django_prj и выбрать mark directory as source root!
    Ответ написан
    Комментировать
  • Как инициировать «генерацию» других форматов видео в django-videokit?

    Sergei_Erjemin
    @Sergei_Erjemin Автор вопроса
    Улыбайся, будь самураем...
    В версии django 1.11 другое расположение файлов media и static внутри проекта. Раньше эти папки находились в "корне" относительно manager.py. теперь внутри папки с setings.py и url.py. Соответственно VideoKit пытался найти и приготовить файлы .ogg и .webm в тех папках которых не существовало. Но т.к. происходило это асинхронно в параллельном процессе, то и поймать ошибку сложно. Никаких сообщений в консоль основного процесса не попадало.
    Ответ написан
    Комментировать
  • Почему в Django преобразование List(QuerySet) такое медленное?

    Sergei_Erjemin
    @Sergei_Erjemin Автор вопроса
    Улыбайся, будь самураем...
    Возня с отладчиком показал:

    Проблема в "Отложенной загрузке полей" при исполнении raw-запросов! В большом-пребольшом raw забыл указать одно из полей (не люблю использовать звездочки, т.к. хочется иметь названия всех полей перед глазами). Все работает, но при получении значения из неуказанного поля выполнялся еще один запрос! Соответственно, при выполнении list() никакой "Отложенной загрузкой полей" в этом большом-пребольшом запросе происходило, а происходил маленький запрос get по id в нужную таблицу (Django сам понимал, какие данные из БД хотят и сам строил маленький get-запрос). Понятно, что выполнение 35 маленьких get запросов быстрее чем 35 больших-пребольших.

    Добавление нужного поля в большой-пребольшой запрос решило проблему. Аналогично ее решает использование звездочек в SQL-запросе.
    Ответ написан
  • Авторизация в django?

    Sergei_Erjemin
    @Sergei_Erjemin
    Улыбайся, будь самураем...
    У джанги логин в сессиях хранится. Так что, request.GET['login'] не нужен и применим только на страничке логирования. Да и там можно без него, и проверить все через request.user.is_authenticated().

    Удачи.
    Ответ написан
    Комментировать