• Как организовать микро слипы в задаче celery?

    @marazmiki
    Укротитель питонов
    Вынесите в отдельную задачу отправку одного сообщения в один чат. А во второй задаче, внутри цикла, вызывайте эти задачи с указанием ETA
    Ответ написан
    Комментировать
  • Как на Django/DRF сделать поиск слов с неполным соответсвием или ошибками?

    @marazmiki
    Укротитель питонов
    Именно средствами Django вряд ли получится, ибо ни DRF, ни Django сами не занимаются каким-либо поиском и\или фильтрацией; они лишь предоставляют единый интерфейс к средствам хранения и поиска (ака базам данных). А вот некоторые базы данных вполне умеют в полнотекстовый поиск. В частности, так умеют делать популярные среди джанго-разработчиков PostgreSQL и MySQL.

    С Постгресом всё относительно просто: поиск работает практически из коробки, достаточно лишь использовать django.contrib.postgres.

    Для MySQL придётся воспользоваться raw-кверисетами или написать собственный lookup, который бы генерировал нужный запрос к базе данных, но тоже проблем нет.

    Резюме такое: сделать полнотекст, не прибегая к сторонним решениям, чаще всего можно. Но и про сторонние решения, типа Elasticsearch, забывать не стоит.
    Ответ написан
  • Django добавление атрибутов в input через if?

    @marazmiki
    Укротитель питонов
    А что будет, если кто-то из этих остальных пользователей через инспектор уберёт атрибут readonly из поля, изменит значение и отправит форму? :)

    Как средство защиты readonly — ну такое.

    Я бы посоветовал просто вывести поля, стилизовав их под форму. Остальные решения подразумевают создание собственных форм, виджетов и тому подобных сущностей, использование которых является оверкиллом
    Ответ написан
  • Как организовать тесты в django на уровне app?

    @marazmiki
    Укротитель питонов
    Чисто субъективно, лучше выбрать что-то из одно: или размещать тесты на уровне приложений, или использовать pytest.

    Хранить тесты внутри приложений полезно при использовании стандартного джанговского тест-раннера: он знает и умеет их находить и запускать. Pytest же не является родным инструментом для джанги, соответственно, использует свой механизм дискаверинга тестов.

    Если Вы нацелены именно на реюзабельные приложения (я полагаю, это означает вынос их в отдельные пакеты и размещение в индексах типа PyPI или собственных частных?), я бы порекомендовал поступать следующим образом:
    • Создавать приложения в отдельном репозитории, вне Вашего основного проекта;
    • Не забывать про setup.py и setup.cfg; либо познакомиться с замечательным пакетом poetry, который снимет кучу головняка с моментами, связанными с упаковкой и публикацией пакетов. Он и в разработке основного проекта очень поможет, но речь сейчас не об этом :)
    • Писать тесты с использованием pytest. Это гораздо мощнее и удобнее, чем родной джанговский раннер
    • Размещать тесты вне директории питоновского пакета, т.е. вашего django-приложения, и вообще не помещать их в архив пакета. В конце концов, тесты — они про разработку, а не про продуктив, на боевой машине не нужны
    • Создать минималистичный джанго-проект внутри директории тестов и не добавить в его urls ссылку на URLConf Вашего приложения
    • И, как следствие, не бойтесь тестировать вьюхи так, как будто они уже часть проекта (так и есть, собственно). Конечно, никто не запретит конструировать объект request и вызывать вьюхи как обычные функции, передавая этот request в виде первого аргумента, но мы ведь тестируем не для покрытия, а для того, чтобы работало, правда?



    Ответ написан
  • Как правильно добавить файлы миграций в gitignore?

    @marazmiki
    Укротитель питонов
    Правильно — никак. Миграции являются неотъемлемой частью проекта, не нужно их исключать.
    Ответ написан
    Комментировать
  • Как в Django организовать миграцию для двух связанных моделей с автоматической подстановкой дефолтных значений поля?

    @marazmiki
    Укротитель питонов
    Сценарий для джанги я бы предложил такой:
    1. Создать модель ModelY, сгенерировать и применить соответствующую миграцию.
    2. Добавить в Model необязательный (т.е. null=True) внешний на ModelY. Сгенерировать миграцию и применить её.
    3. Создать дата-миграцию, в которой создадим "дефолтный" объект ModelY. Накатить её.
    4. Изменить внешний ключ (см. п.2) таким образом, чтобы он подставлял дефолтное значение. Пока не убирать null=True. Накатить. Теперь все вновь созданные модели будут создаваться правильно
    5. Создать дата-миграцию, которая заменила бы все нулевые внешние ключи на дефолтный объект. Накатить.
    6. Теперь можно сделать внешний ключ из п.2. обязательным, т.е. убрать null=True.


    Что же касается best practices, то из опыта я бы посоветовал следующее:

    • Не надо пытаться минимизировать число миграций или засунуть всё в одну. Напротив, лучше идти по пути упрощения миграций, а не уменьшия их количества.
    • Миграции лучше делать максимально независимыми друг от друга.
    • Не нужно заморачиваться с two-way; если какое-то решение оказалось неудачным, лучше сделать ещё одну миграцию, которая делает противоложную операцию относительно предыдущей, чем откатываться.
    • Если добавляете новое поле, обязательно делайте его сперва null, а лишь потом заполняйте.
    Ответ написан
    7 комментариев
  • Почему форма ведет себя странно?

    @marazmiki
    Укротитель питонов
    Подозреваю, что у Вас вот тут сustomer = form.save(commit=False) опечатка. Скорее всего, вместо английской "C" набрана русская "С" :)
    Ответ написан
    1 комментарий
  • Как избавиться от ошибки 1064 You have an error in your SQL syntax?

    @marazmiki
    Укротитель питонов
    Думаю, дело обстоит так: проект был создан относительно давно, а Вы пытаетесь его актуализировать и обновлять зависимости. С кодовой базой сложностей нет, а вот cleardb, похоже, застрял в прошлом и работает на основе древнего mysql 5.5, поддержку из которого выпилили то ли в версии 2.0, то ли 2.1 Джанги. Отсюда и непонятки: джанга генерирует современный SQL, который старый Мускуль не в состоянии распарсить.

    Проще всего именно эту ошибку исправить, заманкипатчив джангу в момент запуска проекта :
    # settings.py
    
    from django.db.backends.mysql.base import DatabaseWrapper
    
    DatabaseWrapper.data_types['DateTimeField'] = 'datetime'
    Но надо понимать, что это всего лишь костыль, чтобы быстро запуститься. Поэтому потом всё же лучше актуализировать движок базы.
    Ответ написан
    6 комментариев
  • Какие инструменты использовать при поиске в базе с большими данными?

    @marazmiki
    Укротитель питонов
    Если про фасетирование речь, то лучше всего, на мой взгляд, использовать elasticsearch. Но не обязательно, если уже используете Postgres.
    Ответ написан
    Комментировать
  • Запустить Angular и Django одной командой?

    @marazmiki
    Укротитель питонов
    Я так понимаю, что ng build --watch запускается в терминале и, таким образом, пока не завершится, следующие команды не будут выполяться, что логично.

    Запускайте его в фоне через nohup.

    Ну или docker-compose попробуйте :)
    Ответ написан
    Комментировать
  • У меня версия джанго 2.1 и как я знаю что в urls.py нужно писать path вместо url?

    @marazmiki
    Укротитель питонов
    Начиная с версии 2.0, все штуки, связанные с обарботкой урлов, хранятся в django.urls, а не django.conf.urls, как ранее. Старый url() доступен в этом пакете, вероятно, для совместимости.

    Подробнее в описании релиза (см. последний абзац)
    Ответ написан
    Комментировать
  • Какой фреймворк выбрать в качестве альтернативы django?

    @marazmiki
    Укротитель питонов
    Зависит от задач. Если планируете делать микросервисы, то лучше посмотреть на flask или aiohttp. Говорят, ещё есть pyramid, но это очень вещь в себе.

    Если же Вы делаете многостраничные приложения с шаблонами, ничего лучше джанги, если смотреть по совокупности, Вы найти не сможете.
    Ответ написан
    5 комментариев
  • Как задать в ContentType только 2 модели?

    @marazmiki
    Укротитель питонов
    Используйте limit_choices_to. Принципиально решение будет выглядеть примерно так:

    from django.db.models import Q
    class Slider(models.Model):
       # ...
        content_type = models.ForeignKey(
            ContentType,
            limit_choices_to=Q(app_label='app_1', model='modelclass1') | Q(app_label='app_2', model='modelclass2')
        )
        object_id = models.PositiveIntegerField()
        content_object = GenericForeignKey('content_type', 'object_id')    
       
        # ...


    Нового тут особо ничего не придумаешь, но есть есть возможность украсить код, например, написав функцию (limit_choices_to может принимать callable-объект, возвращающий dict или Q):

    from operator import or_ as OR
    from functools import reduce
    from django.apps import apps
    from django.db.models import Q
    
    def limit(*models):
        """
        Динамически построит выражение Q для списка заданных в аргументе моделей.
        """
        return reduce(OR, [Q(
            app_label=apps.get_model(m)._meta.app_label,
            model=apps.get_model(m)._meta.model_name) for m in models
        ])
    
    
    class Slider(models.Model):
        # ...
        content_type = models.ForeignKey(
            ContentType,
            limit_choices_to=limit('app_1.ModelClass1', 'app_2.ModelClass2'),
        )
        object_id = models.PositiveIntegerField()
        content_object = GenericForeignKey('content_type', 'object_id')    
        # ...


    Можно пойти ещё дальше и написать собственное поле, заменяющее три поля generic одним. На мой взгляд, код будет лаконичнее:

    class Slider(models.Model):
        content_object = LimitedGenericForeignKey(
            allowed_models=['app_1.ModelClass1', 'app_2.ModelClass2']
        )


    Но это оставлю на домашнее задание :)
    Ответ написан
  • Почему под root virtualenv активируется а под пользователем выдает ошибку -sh: 3: source: not found?

    @marazmiki
    Укротитель питонов
    Похоже, что под рутом в качестве шелла используется bash или что-то совместимое, а под пользователем, под которым вылазит ошибка — что-то другое. Чтобы проверить догадку, сделайте echo $SHELL под рутом и обычным пользователем.
    Ответ написан
    1 комментарий
  • Проблема после обновления django?

    @marazmiki
    Укротитель питонов
    В Django 1.11 изменили систему виджетов: раньше генерируемый ими HTML был захардкожен, теперь же используются шаблоны.

    Вероятно, приложение в пакете pagedown (не знаю, что это такое, но быстрый поиск показал, что существует некий django-pagedown, который в принципе похож) ещё не обновилось. Или Вы его не обновили. Попробуйте сделать pip install --upgrade django-pagedown (если я угадал с приложением, конечно)
    Ответ написан
    1 комментарий
  • Как организовать хранение файлов на сервере?

    @marazmiki
    Укротитель питонов
    Правильно ли я понимаю что для хранение файлов их нужно разбивать по папкам,

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

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

    А на боевых серверах использование хранилища в локальной файловой системе и вовсе нонсенс. Там эта проблема не стоит в принципе.

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

    допустим на сервер пользователь с Ником username заливает файл file.mp4, и я сохраняю этот файл по пути u/us/use/user/usern/file.mp4 и такой путь формируется для каждого пользователя

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

    Во-первых, никогда, НИКОГДА не доверяйте данным, пришедшим от пользователя. Сохраняете загруженный файл – сгенерируйте имя сами. А оригинальное имя не возбраняется сохранить и в другом месте. Если понадобится дать возможность пользователю сохранить файл под оригинальным именем, это делается в несколько строк.

    Во-вторых, Ваша сегментация слишком агрессивная. Если представить, что юзернейм может состоять только из строчных латинских букв и цифр (итого алфавит 26+10=36 символов), то такое хранилище сможет вместить 36 ^ (1 + 2 + 3 + 4 + 5) = 36^15 = 2.21 * 10^23 файлов без повторений. А что если юзернейм короче 5 символов? А что если он переименуется?

    Помните "во-первых"? Так как нам нужно самим придумать имя файла, почему бы не воспользоваться либо UUID и сгенерировать уникальное (с высокой достоверностью) значение вида 28c5a6d8-f7b5-440f-aeaa-150e4fd0bebc, а его уже сегментировать? Например, два сегмента по два символа датут прикольные ссылки вида 28/c5/28c5a6d8-f7b5-440f-aeaa-150e4fd0bebc и возможность разместить 65 тысяч файлов так, чтобы они были по одной штуке в директории :)

    Есть также вариант использовать не UUID, а посчитать контрольную сумму файла и взять её в качестве имени. Практическая ценность такого хеша, правда, тоже стремится к нулю :)

    если будет 2 одинаковых названия файла у одного пользователя то просто добавляю к новому файлу номер (file1.mp4)

    Если речь идёт о Джанге, то она сама так делает, это штатная функция хранилища.
    Ответ написан
    1 комментарий
  • Как оптимальным образом в Django проверить текст на наличие слов из базы?

    @marazmiki
    Укротитель питонов
    Если абстрагироваться от базы данных, то задача выглядит слегка проще. Считайте, что есть два множества: множество плохих слов и множество слов в тексте. Остаётся определить, пересекаются ли эти множества. Если пересекаются, то как минимум одно плохое слово есть :-)

    >>> a = { 1, 2, 3 }
    >>> b = { 2, 3, 4 }
    >>> c = { 5, 6 }
    >>>
    >>> a & b
    {2, 3}
    >>> a & c
    set()


    Теперь ближе к прикладной задаче. Поскольку множество "плохих слов" у нас хоть и хранится в БД (к слову, модели принято называть в единственном числе — BadWord — а не во множественном, как у Вас), но пока не происходят изменений, его можно считать статичным. Поэтому можно без зазрения совести брать это множество из кеша.

    # utils.py
    from django.core.cache import cache
    
    def get_bad_words():
        return cache.get('bad_words')


    и пересчитывать кеш при создании, редактировании или удалении записей из BadWords. Например, с помощью сигналов:

    # models.py
    def set_bad_words(**kwargs):
        from django.core.cache import cache
        cache.set('bad_words', {w.bword for w in BadWords.objects.all()})
    
    models.signals.post_save.connect(set_bad_words,  sender=BadWords)
    models.signals.post_delete.connect(set_bad_words, sender=BadWords)


    Теперь остаётся только преобразовать входящий текст в множество слов

    И пример использования:
    # utils.py
    
    def get_words_from_text(text_string):
        return set([w for w in text_string.lower().split()])


    и определять, есть ли плохие слова (т.е. пересекаются ли множества):

    # utils.py
    
    def has_bad_words(text_string):
        return bool(get_bad_words() & get_words_from_text(text_string))


    В общем, пространство для рефакторинга и улучшений ещё есть (неплохо бы вычистить из текста пунктуацию, стоп-слова, лишние пробелы, сигналы перенести в apps.py согласно новым правилам application loading, а то и вовсе выкинуть их), но идея, думаю, ясна.
    Ответ написан
    4 комментария
  • Как в Heroku создать несколько проектов?

    @marazmiki
    Укротитель питонов
    Если специально постараться, то зальётся :-)
    Ответ написан
    Комментировать
  • Как импортировать настройки Django из самостоятельного скрипта?

    @marazmiki
    Укротитель питонов
    Рабочий (но неправильный) ответ — добавить директорию, в который лежат и apps, и standalone, в PYTHONPATH.
    Это делается двумя путями: или через переменную окружения PYTHONPATH при запуске из консоли:

    $ PYTHONPATH=. python standalone/syncaw.py

    либо добавить директорию прямо в скрипте:

    # syncraw.py
    import sys
    import os
    
    sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
    
    from apps.settings_local import *
    print(DEBUG)


    Правильный ответ — не делать так вообще. И отказываться от колхоза с settings_local
    Ответ написан
    3 комментария
  • Как понять какой метод будет вызван в Django?

    @marazmiki
    Укротитель питонов
    А это и не нужно понимать. По крайней мере, я не могу представить такой кейс, когда это действительно нужно.

    Есть методы perform_create() и perform_update() у вьюхи или вьюсета. Есть create() и update() у сериалайзера. Пусть они реализуют конечную логику сохранения или создания объекта.
    Ответ написан
    6 комментариев