Pull to refresh
38
0.1
Максим @danilovmy

Программист разработчик

Send message

Привет. Специально не указал источник "сыробора". Подсказка была дана в начале статьи.

в предложенном варианте это учтено.

  1. при старте проекта класс инициализируется MyCls(**initkwargs) из urls.

  2. при вызове __call __ копируется в новый инстанс. self = type(MyCls)(vars(self)).

    В предыдущем ответе перемудрил с созданием нового класса.

У нас в проекте переписаны to_python DECIMAL-полей. В Django метод "to_python" - это предобработчик одного значения из переданного комплекта данных до валидации. В твоем комментарии уже есть вероятное решение как это реализовать в Pydantic: можно создать кастомный тип для обработки значений подобных полей.

Так же, по умолчанию, для Django-модели со строковым полем ... min=3... max=15 автоматически у меня не брала валидатор min_length. Вероятно, что-то не то делал.

Для всех наследников Django FileField я придумал как использовать Pydantic.FilePath ну плюс подглядел в fastapi.issue тему про UploadFile. Но опять же нужна доработка.

После многих лет с Django от подобной библиотеки хотелось бы, как-то больше "из коробки" что ли. А то создать навороченный валидатор зачитывая словарь __annotations__, а до появления annotations из правильно написанного__doc__ я и сам могу.

Сигнализаторов - это что? Не встречал. Может речь идет о сериализаторах?

О реальной статической типизации речь, разумеется, не идет. Все, что у нас есть - это аннотация типов, которая была возможна с самого начала через docstrings и чекеры работали. Я не очень понимаю, почему этот вариант не устраивал, когда смысл тот же.

Вероятно, что на мою не совсем корректную терминологию повлиял Guido со своими набросками 2000 года об опциональной статической типизации. Или тут. Я прочитал это, конечно, намного позже, но в голове закрепился именно этот термин.

Спасибо, за замечание, соласен, что аннотация типов более точное обозначение.

Вот видишь, ты ответил похоже моим мыслям: зачем пихать pydantic в Django, когда для этого есть готовая инфраструктура с FastAPI, Scarlett и другой обвязкой. Я статью на этом завершаю: мне тоже не понятен смысл смешивания.

Да, норм метод, согласен. Можно даже поправить в репозитории. Только для Django не очень подходит: ModelField Pydantic не отражает всех параметров полей моделей Django, особенно, если в модели есть свои собственные поля. Попробуй сам, например, Decimal field с неопределенными точка или запятая как разделитель. Ну или ImageField.

Вариант, что не хотят __call __ использовать, подходит больше всего, как объяснение. Это объясняет появление setup вместо __init __.

Разумеется, что:

def __call__(self, request, *args, **kwargs):
  	# забыл инстанс создать
  	self = type('MyCurrentCall', (type(self),), vars(self))()
    self.setup(request, *args, **kwargs)
    return self.dispatch(request, *args, **kwargs)

Иначе получится админ панель Django, где именно синглтоны использованы.

Anker норм, там нажатие идет диагонально вниз к большому пальцу, и, если большой палец на месте, то он противодействует сползанию.

Skulpt нажатие вниз. ползти некому и некуда.

Logitech, Jelly - не замечал. Неохота доставать, что б проверить.

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

 

"Ничего ты не знаешь, Джон Сноу". Джордж Мартин, Буря мечей.

Вроде бы автор и выполнил доскональный разбор кода Django GCBV, но вот так ничего и не понял. А понимание приходит, когда Видами-Базирующимися-На-Классах пользуешься ежедневно несколько лет.

Я уважаю и ценю эту технологию Django из-за ее элегантности пользования и удобства работы с данными в процессе формирования тела response.

Теперь к статье:

Диспетчер URL Django всегда вызывает функцию которая должна вернуть response. В случае найденного совпадения - это будет функция добавляющая в response что-то, иначе это будет функция создающая 404 response, если не сказано другого.

Эти ВСЕ функции называются видами (Views).

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

func(*args, **kwargs); func1(*args, **kwargs); ....

func(*args, **kwargs): return func1(*args, **kwargs)....

Чехарда с локальными переменными в случае нескольких функций приводит к идее использования контейнера для хранения всех необходимых данных. Эту роль в GCBV выполняет видовый класс. Он прокидывается по всем вызовам функций первым аргументом, если не сказано иное.

По главам.

Глава 0. URL dispatcher

Обьявляем класс

class MyFrmView(FormView):
  pass # it should be my class body

myfrmview = MyFrmView.as_view()  # add alias

Как бы выглядел urlpatterns на основе привычного Function-based View (FBV) и на Class-based View (CBV):

urlpatterns = [
		# Function-based View
    path('aboutFunc/', myfuncview),

    # Class-based View
    path('aboutView/', myfrmview)
]

Упс. Я что то делаю не так? Увы все так, реальный пример, когда urlpatterns формируется автоматически только для тех классов у которых в файле прописан превдоним для .as_view()

Глава 1. Class View.

Исторически сложилось, что в диспетчере можно задать специальные декораторы для видовой функции и доп аргументы предназначенные для конфигурирования функции.

согласно django.core.handlers.base для

path('myurl/', view_func, {**initkwargs}, name='updmodel')

вызов диспетчером URL выглядит так:

if url.dispatch == True: 
	ready_view_func = view_function_configurator(**initkwargs)
	return ready_view_func(request, *args, **kwargs).

В итоге, зная как работает Python, пишем следующее:

class myfrmview(FormView):
  
def __call__(self, request, *args, **kwargs):
    self.setup(request, *args, **kwargs)
    return self.dispatch(request, *args, **kwargs)

И тогда в диспетчере URL больше не надо этих отвратительных .as_view():

path('myurl/', myfrmview, name='updmodel')

диспетчер инициирует класс, и вызовет метод __call __.

Увы эта полезная доработка ждет своего часа на полках bugtracing django core, поскольку она ломает в легаси проектах, где декорирование функции указано прямо в url. Но это стало поправимо с появлением method_decorator в Django.

# раньше
path('myurl/', csrf_exempt(myOldfrmview.as_view()), name='updmodel')
# теперь
path('myurl/', method_decorator(csrf_exempt, name='call')(myfrmview), name='myname')

в итоге родной as_view не будет вызываться никогда, вызов будет работать быстрее. и это хорошо.

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

Если автор и начал рассказывать про GCBV, то совсем не сказал зачем они, об их пользе. Cложность и простота GCBV скрываются в осознанном их применении, до которого еще надо дойти.

Идеология GCBV - это совсем не про код модулей django.views.generic, это, в первую очередь, про скорость разработки c неполным следованием в коде принципам SOLID/DRY.

Скорость выигрывается за счет того, что имея набор модулей из django.views.generic я могу создать 99% всех необходимых обработчиков запросов для проекта без написания кода и только обьявлением и конфигурацией видовых классов.

Скорость выигрывается еще за счет того, что это все уже протестировано.

Скорость выигрывается еще и за счет того, что архитектура цепочки вызовов более-менее оптимизирована.

В итоге с GCBV кодовая база проекта получается микроскопической, понятной и простой. И это хорошо!

Автору успехов, видно что он только в самом начале своего творческого пути.

Уже рассказвал тут

Имею у себя варианты Logitech MX, Anker, Sculpt, Jelly и aliexpress версию Swiftpoint.

Про Sculpt, кто-то писал, желание запустить в стену. У меня это было ежедневно полтора года! Решил поменять.

Сначала использовал Logitech MX, потом попробовал Anker и понял что он легче по весу.

Anker покупаю третий раз, самое лучшее, что использовал. Каждый раз ломался ресивер - рассыпался из за запихивания ноута в сумку вместе с ресивером.

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

Swiftpoint - фингя. Купил сразу, как увидел. Не пользовался и 10 минут. Причина - невозможно пользоваться. Дальнюю кнопку не удобно нажимать, ну или ближнюю - у кого пальцы толстые или короткие. Ну и главное - посмотрите на видео на 38 секунде в статье, как чел экраны windows переключает. Жесты выглядят как нервные тики, а не плавные движения. Чувствительность не причем.

@Igor_O

где можно Swiftpoint GT "руками пощупать"? Очень интересный вариант.

В сентябре буду в Москве, могу безвозбездно отдать "ali" версию. Абсолютно не используемо.

AlexNoo, разумеется, каждой задаче — свой алгоритм. Не считав значения из потока/файла — не поймешь, что дальше делать: побыстрее избавляться от него или обрабатывать.

В теории, если есть brake в for-цикле при считывании из файла, то, вероятно, что лучше использовать конструкцию while.

Так же, в теории, если есть пропуск в for-цикле при считывании из файла, то, вероятно, лучше вынести пропуск элемента в блок if pipe-генератора.

На практике — решение выбирается по задаче и по тому, сколько есть времени на разработку.
Часто бывает, что влепить continue в середине for бывает выгоднее всего.

У меня в текущем legasy-проекте встречается 1653 for/while, 138 break и 39 continue. Любопытно будет спустя год найти этот комментарий и сравнить, как это соотношение изменится.
Asserts everywhere

items = Item.objects.get()

print(items[0])

Согласен, пример неудачный


Эээ ребят, а это ничего что это не работает?
get() вернет объект у которого, скорее всего, нет [0] или упадет DoesNotExist/MultipleObjectsReturned.

Вероятно, подразумевалось Item.objects.all()[:1], но тогда это просто Item.objects.last() или Item.objects.first() в зависимости от задачи.
привет, kesn. Читал статью и кивал: вроде бы и да, а вроде бы и нет.

Писать на Python можно сразу хорошо, только у всех это свое хорошо. Тут соглашусь с David Beazley, что большинство не понимает идеологию Python вообще, но это не мешает им создавать интересные решения.

Что мне кажется полезным использовать:

1. Генераторы и Pipe-generators в любом виде.
Все, что необходимо для перечисления в последующих функциях, должно быть генератором.
Когда я вижу, что вычисляется лист/кортеж который прокидывается дальше в какой-нибудь цикл, то это означает, что алгоритм выполняет несколько ненужных проходов по коллекции элементов, прежде, чем выполнить что-то полезное. В моем теперешнем проекте можно встретить list только в одном случае: я пропустил это при рефакторинге.

2. Рефакторинг For цикла без использования Break.
Если идет цикл по набору элементов, который прерывается (break) по каким-либо причинам, оставляя часть элементов не обработанными, то получается, что набор был излишен. Ресурсы на создание и хранение этого набора частично были потрачены зря. Если же набор элементов — это генератор, не ясно, что мешало создать генератор на меньшее количество элементов?

3. Рефакторинг For цикла без использования Continue.
Если идет цикл по набору элементов, некоторые из которых пропускаются (continue) по каким-либо причинам, оставляя часть элементов не обработанными, то получается, что набор был излишен. Ресурсы на создание и хранение этого набора частично были потрачены зря. Если же набор элементов — это тоже генератор, не ясно, что мешало создать сразу правильный генератор?

4. While c break/continue является продолжением предыдущих выводов. Многие захотят поспорить, и я соглашусь, например, с тем, что использование break/continue убыстряет разработку, а это плюс!

5. map/zip в современном Python бессмысленны:
про map уже даже Гвидо сказал, что
«want to remove map»
zip стал не нужен после появления yield from:
zip = yield from (next(iterator) for iterator in tuple(iter(iterable) for iterable in iterables))

6. Ранний return — не панацея от удержания кода в голове. Если код надо запоминать — это скорее всего признак необходимости рефакторинга. А вот множественные точки выхода — это попо-боль для отладки.
Пример: поставил точку останова. А оно не остановилось… Оказалось создатель кода решил выйти из функции раньше.

7. Даже в документации Django list(queryset) отмечен, как опасный. Потому, как list запускает получение всех данных queryset.
Предлагаю Автору сделать assert list(queryset) на queryset под миллион записей и удивиться.
Кто-то посоветовал queryset.get(), но для версий django <=3.0 get имеет ту же проблему, я описывал это в своей статье.
Ложки Хорошего решения тут нет.
Иногда поможет queryset.exists(), но это n+1 запрос.
Иногда поможет выполнение кода и assert после:
item = None
for item in Item.objects.all()[:1]:  #  для твоего кода 
    print (item)
assert item  # для любителей assert-ов

8. select_related / prefetch_related ТОЛЬКО в случаях, когда это действительно надо.
Такое надо в коде бывает редко, если алгоритм выполняет то, что должен, и не более.
Пример: В Django 1.4 я прилепил select/prefetch всего сразу на базовый queryset цены товаров в магазине. Спустя 4 года любое обращение к queryset цен стало вешать базу. Я начал плакать, кода посмотрел код sql запроса price.objects.count(), который вызывается дважды в AdminModel в шаблоне change_list.html

В итоге Автор напомнил мне меня же лет 6 назад. Я тогда уже наелся первой стрельбой по ногам от python/django и сформировал кодекс «пиши вот так...». Cпустя время каждое второе правило этого кодекса оказалось очередной «случайной пулей в ногу», ведь задачи стали совершенно другими.

Автору успехов
Я из Австрии, мне легко предположить, что на руки в Белоруссии он реально мог получать больше, чем в Aвстрии Netto.
Спасибо! Оказывается я пропустил прошлый выпуск. Вот тебе и поклонник :(
Привет. Буквально вчера перебирал file_cache на сервере, папка для временных файлов, которые необходимы пользователям. У нас уже было создано нечто подобное сортировке из статьи. За 6 лет функция обросла многими доработками. Потому расскажу, какие сложности встречались в этой, казалось бы, банальной задаче:

Предпосылки — 100 гигабайт хранилище, 350 000 файлов, расширения png, gif, ico, jpeg, svg, webp, tiff, pdf, tmp, html, xml, xls, xslx, doc, docx, js, css. Видео ни разу не встречал, хоть и можно. И бесконечное количество файлов с .tmp, .temp или без расширений. Адобовские еще файлы какие то AI, PSD… и т.п.

Сложности:
1. Как уже в комментариях сказали — расширение файла не говорит ничего. Хуже того, если отсортировать картинки по расширению, не проверив, для последующей пакетной обработки, например, в PhotoShop или программно в том же PIL — все упадет. Хорошо так упадет.
Для проверки переимовываем xxxxx.jpg в yyyy.png и открываем в photoshop.

В итоге мы стали использовать Fleep автор Mykyta Paliienko. Эта утилита сообщает о типе файла по содержимому. Кстати пришлось доработать «fleeper» до рабочего состояния, в issue автору тоже сообщили.

2. Файлы с именами, написанными через точку, и без расширений. Отдельная задача, поскольку формально расширение есть.

Решилось так же флиппером.

В коде git статьи extension получается через fp.split('.')[-1], это аналог os.path.splitext(fp) и оба не помогут в решении пункта 2, но os.path.splitext более «кошерный» в случае работы с файловой системой.

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

# функция def sort_files()
for fp, (file, extention) in (fp.path, os.path.splitext(fp.path) for fp in os.scandir(folder_path) if not fp.is_dir()):
....

Если кого расстраивают длинные строки, например линтер black прям беснуется, то можно так же сделать генераторный pipline:
# функция def sort_files()
path_generator = (_file.path for _file in os.scandir(folder_path) if not _file.is_dir())
extentions_generator = (_path, os.path.splitext(_path) for _path in path_generator)
for path, (file, extention) in extentions_generator:
....


4. Создание папок, и, потом, удаление пустых папок — выглядит вычурно. Если есть словарь хранящий { расширение: путь }, тогда проще получать путь из словаря и создавать путь, если словарь вернул пустое значение.

5. Логирование. Сложная тема. Более 300 000 сообщений одномоментно в консоль бессмыслены и ломали «terminus»- консоль в sublime text.

Решение — использовать стандартный логгер Python c выводом в консоль/файл управляя выводом через атрибуты запуска.

Успехов автору в кодинге.
Привет, alexzfort. Спасибо, продолжаю быть поклонником твоей рубрики.

Статья «Выкладка нетрадиционной ориентации» это неполный перевод обьемной статьи css-tricks.com/hexagons-and-beyond-flexible-responsive-grid-patterns-sans-media-queries, текст примеры и картинки в переводе взяты оттуда. Хотя основная идея была озвучена крисом койером(Chris Coyier) еще год назад.

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

Эээ пометьте что это перевод https://css-tricks.com/hexagons-and-beyond-flexible-responsive-grid-patterns-sans-media-queries/

Кстати там рассмотрены ещё и сложности в такой вёрстке

Information

Rating
3,295-th
Location
Zams, Tirol, Австрия
Date of birth
Registered
Activity

Specialization

Backend Developer, Fullstack Developer
Lead
From 8,000 €
Python
Django
Ajax
OOP
Design patterns
Vue.js
JavaScript
HTML
CSS