«phpClub» — архив тем ("тредов"), посвящённых изучению PHP и веб-технологий.
someApprentice 2019/03/30 08:13:55  №1372258 1
Почему у меня в Питоне код работает по разному?

Вот набросок кода: https://repl.it/repls/ConsciousBlondElectricity (можно не разбираться в том что он делает, меня интересует синтаксис)

Вот примеры синтаксиса, которые работают как ожидаются:

https://repl.it/repls/GiantSeveralControlpanel

https://repl.it/repls/FixedAnnualRuntime

Но в рабочем коде, в первом случае, выдаётся ошибка

>if (details is None) or (not 'Bearer token' in details['authextra']):
>builtins.TypeError: argument of type 'NoneType' is not iterable

И, во втором случае,

>if (details is None) :
> principal[u'extra'] = {
> u'error': u"Access denied: No Bearer token in authexta"
> }
> return principal
>
>
> token = details['authextra']['Bearer token'];

return не выполняется и код продолжает выполнятся

>token = details['authextra']['Bearer token'];
>builtins.TypeError: 'NoneType' object is not subscriptable


И вот ещё 3-ий пример: https://repl.it/repls/DependentUnselfishDevelopment

но в рабочем коде

> try:
> payload = jwt.decode(token, JWT_SECRET)
> except Exception as e:
> principal[u'extra'] = {
> u'error': e
> }
> return principal
>
> ...

return снова не выполняется и вбрасывается ошибка: "WAMP message serialization error: Object of type 'InvalidSignatureError' is not JSON serializable"
https://pyjwt.readthedocs.io/en/latest/api.html#jwt.exceptions.InvalidSignatureError


Почему так может происходить? Версии Питона 3.6.x что на Repl.it, что и на машине, и примеры синтаксиса проверялись и у себя на машине в консоли с помощью команды python.
Ответы: >>1372272 >>1377739
Аноним 2019/04/03 03:06:29  №1374075 2
>>1353705 (OP)
Вопрос по HTML и CSS...

Задача: сделать вывод текста и текстареи - в две колонки,
как тут: https://css-live.ru/articles/css-gridy-css-kolonki-%E2%99%A5.html
но так, чтобы при изменении размера текстареи, изменялся и размер line-height,
и чтоб не ровно на половину страницы разделялось, а с небольшим отступом.

Вопрос - как правильно сделать?
Ответы: >>1377517
Аноним 2019/04/08 05:52:07  №1377063 3
Какой редактор / IDE посоветуешь, анон? Я нью, планирую вкатываться. Хочу нормальную подсветку синтаксиса, автокомплит и прочее.
Ответы: >>1377106 >>1377115 >>1378171
Аноним 2019/04/09 02:09:30  №1377517 4
threecolumns.png (46, 1309x605)
605x1309
>>1374075
Решил при помощи таблицы. Пикрил.
Ответы: >>1378171
Аноним 2019/04/09 06:43:33  №1377564 5
>>1353705 (OP)
Господа, я тупой или не очень?
Для одного проекта мне надо поставить db* из pear

Pear мне говорит, что db депрекейтед в пользу mdb2

При этом, \t1.9.3 (stable) released on 2018-12-05
https://pear.php.net/package/DB/download/

а mdb2: Status:\t2.5.0b5 (beta) released on 2012-10-29
Ответы: >>1378171
someApprentice 2019/04/09 11:27:15  №1377739 6
>>1372258
tl;dr

Проблема решена

>>if (details is None) or (not 'Bearer token' in details['authextra']):
>>builtins.TypeError: argument of type 'NoneType' is not iterable
>
>И, во втором случае,
>
>>if (details is None) :
>> principal[u'extra'] = {
>> u'error': u"Access denied: No Bearer token in authexta"
>> }
>> return principal
>>
>>
>> token = details['authextra']['Bearer token'];
>
>return не выполняется и код продолжает выполнятся
>
>>token = details['authextra']['Bearer token'];
>>builtins.TypeError: 'NoneType' object is not subscriptable
Здесь нет проверки на существование свойства authextra.

>> try:
>> payload = jwt.decode(token, JWT_SECRET)
>> except Exception as e:
>> principal[u'extra'] = {
>> u'error': e
>> }
>> return principal
>>
>> ...
>
>return снова не выполняется и вбрасывается ошибка: "WAMP message serialization error: Object of type 'InvalidSignatureError' is not JSON serializable"
>https://pyjwt.readthedocs.io/en/latest/api.html#jwt.exceptions.InvalidSignatureError
Питон сам по себе не сериализирует исключение в строку.


Решено в связи с https://forum.crossbar.io/t/python-code-does-not-work-as-expected/1495

Ответы: >>1378171
Аноним 2019/04/10 05:39:02  №1378171 7
>>1377739

Жаль, что-то у меня руки не дошли проверить код. Но я бы тебе посоветовал:

- использовать объекты вместо массивов для ключевых типов данных, которые используются во многих функциях в коде
- не городить такие массивы, когда можно обойтись без них. Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив. Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
- разбивать стену кода на отдельные функции. Ты и токены проверяешь, и к БД подсоединяешься, и что только не делаешь.
- в try/except указывать конкрентные классы исключений, которые тебя интересуют, а не ловить все подряд

>>1377564

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

>>1377517

Чтобы не нарушать семантику, можно еще использовать display: table вместо настоящих таблиц. А так, да, вертикальное центрирование - это table или flexbox.

>>1377063

Sublime Text, Netbeans for PHP, Eclipse PDT, если ты богат (или если можешь выпросить лицензию как студент) и у тебя быстрый проц, то PHPStorm. VS Code неплоха, но не у всех быстро работает.
Ответы: >>1378381
someApprentice 2019/04/10 14:02:33  №1378381 8
image.png (233, 1920x1080)
1080x1920
Протестировал WAMP и сейчас собираюсь писать сервис сообщений. И сначала я хотел бы обсудить архитектуру которую я выбрал. Я считаю её безупречный, но я не безупречный разработчик, поэтому мне хотелось бы услышать опытный взгляд со стороны.

Задача
Каждый пользователь может написать другому пользователю. При первой отправке сообщения создаётся диалог. Сообщения могут быть текстовые, могут быть голосовые и могут быть видео. Каждое сообщение может удаляться либо у себя лично, либо у обоих пользователей. Диалоги так же могут удаляться у каждого пользователя лично (но не у обоих, т.е. каждый пользователь имеет только ссылку на диалог).

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

Диалоги могут быть приватные (тет-а-тет) а могут быть публичные (конференции).


Соответственно архитектура будет такая:

пользователь:
- ...

текстовое_сообщение:
-uuid
-пользователь (автор сообщения)
-сообщение
-дата
-прочитано (булевый)
-...

аудио_сообщение:
-uuid
-пользователь (автор сообщения)
-звукозапись (путь/к/аудиофайлу)
-дата
-прочитано (булевый)
-прослушано (булевый)
-...

видео_сообщение:
-uuid
-пользователь (автор сообщения)
-видеозапись (путь/к/видеофайлу)
-дата
-прочитано (булевый)
-просмотрено (булевый)
-...


текстовое_сообщения: (хранит в себя ссылки на сообщения для каждого отдельного пользователя)
-uuid
-пользователь (кому принадлежит ссылка)
-диалог (диалог в котором сообщение находится)
-сообщение
-...

аудио_сообщения:
-...

видео_сообщения:
-...


прикрепление_изображения:
-uuid
-сообщение
-изображение
-...

прикрепление_аудиозаписи:
-...

и т.д. за исключением что у каждого прикрепления своя ссылка на источник.


диалог:
-uuid
-приватный (булевый)
-...

участники: (для приватных диалогов всегда будет два участника)
-uuid
-диалог
-пользователь
...

диалоги: (ссылки на диалоги для каждого пользователя)
-uuid
-пользователь
-диалог
...


Такова вся архитектура. База данных будет psql.

Название таблиц в боевой базе данных будет идентично т.е. будет и сообщение и сообщения. Это же не создаёт затруднения для понимания?

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


>>1378171
>Жаль, что-то у меня руки не дошли проверить код. Но я бы тебе посоветовал:
>
>- использовать объекты вместо массивов для ключевых типов данных, которые используются во многих функциях в коде
>- не городить такие массивы, когда можно обойтись без них. Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив. Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
>- разбивать стену кода на отдельные функции. Ты и токены проверяешь, и к БД подсоединяешься, и что только не делаешь.
>- в try/except указывать конкрентные классы исключений, которые тебя интересуют, а не ловить все подряд

>Жаль, что-то у меня руки не дошли проверить код.
Всё хорошо, у меня было время чем другим заняться. Я рад, что сэкономил ваше время.

>- использовать объекты вместо массивов для ключевых типов данных, которые используются во многих функциях в коде
>объекты
Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?


>- не городить такие массивы, когда можно обойтись без них. Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив. Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.

>Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив.
Разве это не создаст дополнительные проверки и не раздует код? Т.е. во время ответа писать: if error is not None: principals['extra']['error'] = error; Почему нельзя собирать массив "на ходу"?

>Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
Возвращать кортеж из функции авторизации, вы это имели ввиду? Это не правильно, потому что principals сериализуется в json и возвращается клиенту.


>- разбивать стену кода на отдельные функции. Ты и токены проверяешь, и к БД подсоединяешься, и что только не делаешь.
Конечно. Это только черновик где я тестирую чтобы посмотреть как работает код.

У меня будет вопрос по архитектуре, когда я его сформулирую.


>- в try/except указывать конкрентные классы исключений, которые тебя интересуют, а не ловить все подряд
Понял.
Ответы: >>1380460
Аноним 2019/04/13 18:20:25  №1380460 9
>>1378381

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

Если ты хочешь делать для каждого пользователя свою копию сообщения, то не проще ли просто сделать таблицу "диалог", и таблицу "сообщения", где в сообщении стоит ссылка на диалог и ссылка на получателя, кому оно предназначено? То есть, так:

диалог:
- uuid

участники диалога:
- id пользователя
- id диалога

сообщения: (копия для каждого получателя)
- uuid
- id, объединяющий копии одного сообщения
- id диалога
- id отправителя
- id получателя этой копии
- признак прочтения
- текст

Кстати, в больших чатах невыгодно будет делать для каждого свою копию сообщения. Выгоднее делать всего одну копию для всех.

Далее, у тебя есть наследование таблиц (для текстовых, аудио, видео сообщений сделаны отдельные таблицы). Ты использовал Concrete Table Inheritance. Не очень удобно делать их разными таблицами. Представь, что тебе надо получить последние 10 сообщений в диалоге. Тебе надо будет запрашивать данные из каждой таблицы (из текстовых сообщений, аудио сообщений, видео сообщений) и объединять. Как ты себе представляешь эффективную пагинацию по ним?

Логичнее было бы использовать другой паттерн наследования, например, Single Table Inheritance или Class Table Inheritance. В случае STI мы делаем единую таблицу для всех видов сообщений. В случае CTI мы делаем одну таблицу для полей, которые есть в каждом виде сообщения (отправитель, время отправки, получено), и отдельные таблицы для доп. полей (которые есть только у опр. вида сообщения):

- http://design-pattern.ru/patterns/class-table-inheritance.html
- http://design-pattern.ru/patterns/single-table-inheritance.html

В обоих случаях мы можем легко получить последние N сообщений или сделать пагинацию без использования OFFSET.

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

Примерно то же относится к вложениям. Возможно, проще просто сделать одну таблицу для любых вложений.

Наконец, тут надо быть готовым к денормализации или кешированию. Допустим, мы хотим показывать пользователю число непрочитанных сообщений в диалоге. Делать это через подсчет сообщений без признака прочтения неэффективно. Эффективнее иметь готовую циферку. Или, допустим, мы хотим вывести верхние 50 диалогов. Эффективнее одним простым запросом получить список диалогов, циферки непрочтенных сообщений, чем выбирать эти данные через джойны и группировки.

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

Поэтому, думаю, схему надо доработать.

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

Но оно не дает нам эффективно делать пагинацию по сообщениям, что сводит плюсы на нет.

> Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?

Да, как в PHP. Я имел в виду, что если у тебя в коде есть какой-то массив, который много где используется, передается и возвращается разными функциями, то правильнее будет сделать его объектом. Но, как я понял, там был просто тестовый код, и тогда это не важно.

> Разве это не создаст дополнительные проверки и не раздует код? Т.е. во время ответа писать: if error is not None: principals['extra']['error'] = error; Почему нельзя собирать массив "на ходу"?

Потому, что это разные задачи: получение данных и формирование ответа на JSON-запрос. И скорее всего, лучше чтобы отдельно была функция проверки авторизации, а отдельно функция, которая формирует JSON-ответ. А не совмещать это все в одной функции. Но так как это тестовый код, то это не принципиально.
Ответы: >>1380677 >>1380677
someApprentice 2019/04/14 08:05:28  №1380677 10
>>1380460
>Использование таблиц с похожими именами будет однозначно вызывать путаницу. Более того, я пока сам не понял, в чем разница между "сообщением" и "сообщениями" или "диалог" и "диалоги".
Сообщение - это конкретная запись сообщения имеющая все данные о нём, от содержания до автора, id, даты публикации и т.д., а сообщения - это запись какие сообщения принадлежат какому пользователю и/или диалогу. Т.е. "сообщения" хранит ссылки для пользователя на id конкретного сообщения.

Как можно понятней назвать таблицы чтобы не вызвать путаницу?

Такая схема была предложена вами при обсуждении моей задачи чата на чистом JS:

https://phpclub.tech/pr/res/1019301.html#1028550
>чаты и диалоги лучше делать одинаковым способом, то есть представить диалог 2 пользователей как чат из 2 участников.

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

Мне хочется казать, что я слегка её отшлифовал, убрав многократное копирование сообщений заменив его "ссылками" на сообщения.


>Если ты хочешь делать для каждого пользователя свою копию сообщения, то не проще ли просто сделать таблицу "диалог", и таблицу "сообщения", где в сообщении стоит ссылка на диалог и ссылка на получателя, кому оно предназначено? То есть, так:
>
>диалог:
>- uuid
>
>участники диалога:
>- id пользователя
>- id диалога
>
>сообщения: (копия для каждого получателя)
>- uuid
>- id, объединяющий копии одного сообщения
>- id диалога
>- id отправителя
>- id получателя этой копии
>- признак прочтения
>- текст
>
>Кстати, в больших чатах невыгодно будет делать для каждого свою копию сообщения. Выгоднее делать всего одну копию для всех.
>- id, объединяющий копии одного сообщения
Это получается что объединяющее копия одного сообщения это отдельная сущность/таблица? Я не понимаю тогда, это получается тоже самое что и я предложил, только колонки в разных таблицах. Ну и контент сообщения в моём предложении находиться только оригинальном, а не дублируется.

Повторюсь, что в ссылке на сообщение находиться id на сообщение, а не его содержимое:

>текстовое_сообщения: (хранит в себя ссылки на сообщения для каждого отдельного пользователя)
>-uuid
>-пользователь (кому принадлежит ссылка)
>-диалог (диалог в котором сообщение находится)
>-uuid сообщения
>-...


>Далее, у тебя есть наследование таблиц (для текстовых, аудио, видео сообщений сделаны отдельные таблицы). Ты использовал Concrete Table Inheritance. Не очень удобно делать их разными таблицами. Представь, что тебе надо получить последние 10 сообщений в диалоге. Тебе надо будет запрашивать данные из каждой таблицы (из текстовых сообщений, аудио сообщений, видео сообщений) и объединять. Как ты себе представляешь эффективную пагинацию по ним?
>
>Логичнее было бы использовать другой паттерн наследования, например, Single Table Inheritance или Class Table Inheritance. В случае STI мы делаем единую таблицу для всех видов сообщений. В случае CTI мы делаем одну таблицу для полей, которые есть в каждом виде сообщения (отправитель, время отправки, получено), и отдельные таблицы для доп. полей (которые есть только у опр. вида сообщения):
>
>- http://design-pattern.ru/patterns/class-table-inheritance.html
>- http://design-pattern.ru/patterns/single-table-inheritance.html
>
>В обоих случаях мы можем легко получить последние N сообщений или сделать пагинацию без использования OFFSET.
Я думал об этом и предполагал, что у psql есть встроенная поддержка этого, и как оказалось, действительно есть https://postgrespro.ru/docs/postgresql/11/ddl-inherit


>Также, возможно, имеет смысл свести любые сообщения к текстовым сообщениям с приложениями. Тогда аудиосообщение будет просто текстовым сообщением без текста с приложенным аудио, с пометкой, что аудиосообщение тут главное. Тут надо взвесить плюсы и минусы. Плюс в упрощении структуры таблиц.
Не согласен. Аудиосообщение по сути это аудиозапись, и в прикрепление тоже могут прикладывать аудиозапись, и поэтому будет конфликт, что есть голосовое сообщение, а что аудиофайл. Для этого нужно будет наследовать отдельный тип прикрепления, что является тем же самым что и унаследовать от сообщений. Я считаю, что голосовые сообщения должны быть отдельным типом сообщения, а не прикрепления.


>Наконец, тут надо быть готовым к денормализации или кешированию. Допустим, мы хотим показывать пользователю число непрочитанных сообщений в диалоге. Делать это через подсчет сообщений без признака прочтения неэффективно. Эффективнее иметь готовую циферку. Или, допустим, мы хотим вывести верхние 50 диалогов. Эффективнее одним простым запросом получить список диалогов, циферки непрочтенных сообщений, чем выбирать эти данные через джойны и группировки.
Да, я думаю, что можно хранить количество непрочитанных сообщений и дату последнего сообщения прямо в таблице диалога.


>>1380460
>> Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?
>
>Да, как в PHP. Я имел в виду, что если у тебя в коде есть какой-то массив, который много где используется, передается и возвращается разными функциями, то правильнее будет сделать его объектом. Но, как я понял, там был просто тестовый код, и тогда это не важно.
Этот массив был бы объектом с одним свойством этого самого массива?
someApprentice 2019/04/14 08:07:19  №1380679 11
Надеюсь мой ответ не потеряется >>1380677