Comments
Админка за 0 минут — запустите любой Database-клиент. Я так некоторые проекты годами веду, очень удобно, все возможности.

Restfull-админка — это неудобно, потому что элементарные сущности и так можно добавлять в базу, а что-то более сложно все равно придется допиливать.
Админкой пользуются не только люди, которые умеют с бд клиентом работать. Кроме того в таких приложениях часто присутствует какая-то логика помимо просто записей в базе и не на уровне бд.
Во многих проектах стараются их избегать. Потому что уйдет человек, которых их писал и это очень тяжело поддерживать. И с версионированием этого всего как-то непонятно. И удобнее когда вся логика в одном месте — в приложении.
Каким образом хранимые процедуры могут, например, отправить письма или сделать https запрос?
Легко, в MS SQL есть CLR-сборки, по сути вызов функций из DLL.
Source: Работаю с биллинговой системой, построенной подобным образом.
Для Postgres есть hasura (https://hasura.io/), которая позволяет создавать graphql endpoint, который имеет в себе всё что только нужно, однако, все подобные технологии пока ещё не до конца готовы и сыроваты, ещё требуется 2-3 года до готовности, когда наконец-то монолит из данных и логики будет храниться полностью в БД.
Ты же просто хранимая процедура, имитация ЯП. Разве может хранимая процедура отправить письмо или сделать http-запрос?
Вы сами себе ответили

имитация ЯП


Может ли имитация быть лучше хорошего ЯП?
Каждый инструмент должен быть на своём месте.
Требования этих людей к админкам порой настолько дикие, что приходится выкидывать весь фреймворк для для построения админок и писать ручками. Сомневаюсь что за 10 лет, когда я активно искал хорошее решение для PHP что-то поменялось. Я имею в виду условия когда вы не можете диктовать заказчику как должна выглядеть админка.
Джанговская админка обычно всех устраивает и там есть всё что угодно из коробки или на pypi. И кастомизируется как угодно.
Окей, классический пример. Есть форма с допдауном. В зависимости от выбора дропдауна подгружается динамически кусок другой формы. Некоторые подформы формы имеют в своей основе сущности из БД, некоторые нет, некоторые имеют сущность в своей основе, но и коллбэки на отрисовку через внешний API. В зависимости от некоторых данных введенных в одной из подформ, автоматически заполняются поля в основной форме.

Опишите пожалуйста как вы будете делать такое чучело на любой технологии, правда интересно. Потому что мне еще не попадался достаточный уровень декларативности описания такой вот формы доставки, чтобы я мог не дописывать куски управления формой ручками. И если покажете хотя бы подход или хороший проект на гитхабе, то буду премного благодарен.
Какой-то абстрактный пример. В админке django modelform можно кастомизировать, какие там должны быть поля и как их потом сохранить в бд Остальное накручивается на js.
Так вот хотелось бы без этой бесконечной накрутки на JS, вернее чтобы фреймворк сам ее делал. Пример не абстрактный — обычная форма доставки с расчетом стоимости разными ТК и заполнением ФИО учетки из адреса доставки.
Ну стоимость можно сделать readonly полем и после сохранения высчитывать её. ФИО тоже после сохранения заполнять.
Стоит добавить в проект webargs и marshmallow. С ними делать REST под flask прям хорошо и прекрасно.
Спасибо за коммент. А вроде есть reqparse в flask_restful.
https://flask-restful.readthedocs.io/en/latest/api.html#module-reqparse
Нужен ли webargs?

А по-поводу marshmallow, я к нему присматриваюсь. И раз вы здесь его упомянули хотелось бы спросить: у вас есть позитивный опыт использования? Можете что-нибудь про него рассказать хорошее?
webargs позволяет задавать параметры к запросу декоратором, что с моей точки зрения удобнее. Плюс он всеядный, если специально не указывать из какого источника брать ему можно присылать в любом виде, т.е. и query params и form-data и json. Он все обработает одинаково, главное чтобы имена совпадали.
Дополнительно он имеет отличную интеграцию с marshmallow, что позволяет прям объекты напрямую из запроса доставать.
Marshmallow я использую и это единственный на данный момент сериализатор под python который нормально из PostgreSQL жрет нативные uuid. Дополнительно там есть слой совместимости с sqlalchemy. Который мне правда не актуален, я использую PonyORM.
Мне больше нравится pydantic. У себя я превращаю аргументы функции в параметры запроса так:
from pydantic import create_model
def get_query_schema(handler):
    params = inspect.signature(handler).parameters
    query_params = {k: (p.annotation, p.default) for k, p in params.items() if k not in ('pk', 'request', 'self')}
    return create_model('query_schema', **query_params)

это можно использовать потом в декораторе или middleware
@web.middleware
async def webapi_validate_query(request, handler):
    self = handler.__closure__[0].cell_contents.__self__
    if request.method not in ('GET', 'POST', 'PUT', 'DELETE'):
        raise web.HTTPMethodNotAllowed(f'{request.method} not allowed')
    query = request.query.copy()
    if self.paginator:
        self.paginator.get_page_from_query(query)
    if self.filter_class:
        self.filter = self.filter_class(**request.query)
    validated_query = self.query_schema(**query.items()).dict()
    result = await handler(request, **request.match_info, **validated_query)
    return web.json_response(result, dumps=dumps)
Вот не лень писать столько кода? Смотрите как это выглядит в случае webargs

@bp.route('/charge', methods=['POST'])
@use_args({
    "account": fields.Int(required=True),
    "agent": fields.Int(required=True),
    "ts": fields.DateTime(),
    "unit": fields.Int(required=True),
    "service": fields.UUID(required=True),
    "amount": fields.Decimal(required=True),
    "count": fields.Decimal(missing=1),
    "note": fields.Str()
})
def add_charge(args):
    schema = ChargeMaSchema()
    if args.get('amount') < 0:
        return abort(422)
    try:
        new_charge = Charge(**args)
        commit()
        return schema.jsonify(new_charge)
    except:
        abort(422)

К примеру кусок кода из моего проекта. Аннотацию входных данных можно так же брать из схемы marshmallow.

На abort дополнительно можно довесить обработчик, в webargs если требуется так же можно его довесить.
Так это код из моей библиотеки. А в самом проекте будет просто
async def test_handler(request, query: str, page: int=1): pass

Мне нравится использовать аннотации типов, этим pydantic больше нравится. В fastapi он аналогично используется
Обычно, в rest, data это данные модели, для которых тоже есть сериализатор. Ну или писать отдельный.
Ну в случае использования webargs это там сразу из коробки, чем оно и хорошо.
norguhtar, спасибо за наводку.
Получается, что если мы хотим использовать webargs в примере из статьи.
То писать надо примерно так:
from webargs.flaskparser import use_args                                                                                                                                 
from webargs import fields    
# Code ...
class UserLogin(Resource):                                                                                                                                               
      @use_args({'username': fields.Str(),                                                                                                                                 
                 'password': fields.Str()}, locations=['json'])                                                                                                            
      def post(self, args):                                                                                                                                                
          if args.get('username') == 'admin' and args.get('password') == 'habr': 
             # Code


Верно? Я проверил, в принципе, работает.
Да. Только еще стоит добавить:

  @use_args({
'username': fields.Str(required=True),                                                                                                                                 
'password': fields.Str(required=True)}, 
locations=['json'])    


В этом случае если параметры не обнаружены, вывалит ошибку по умолчанию. Туда можно вставить свой хендлер обработки и обрабатывать отсутствие параметров единожды. Там еще есть опция missing которая позволяет задавать умолчания, с ней надо учесть, что умолчания там задаются один раз при старте. А то я там положил раз datetime.now() :)
Допустим удалим один или несколько параметров, то мы получаем в json
{
    "error": "Invalid username and password"
}


А если есть required=True, то получим
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>422 Unprocessable Entity</title>
<h1>Unprocessable Entity</h1>
<p>The request was well-formed but was unable to be followed due to semantic errors.</p>


Видимо, надо по ситуации все же решать. Либо как-то научить его отдавать json в качестве исключения.
Это отрабатывает глобальный хендлер. Его можно переопределить и это более правильный вариант обработки ошибок. Для flask webargs.readthedocs.io/en/latest/framework_support.html#flask
Если хочется отдавать 200 то так
webargs.readthedocs.io/en/latest/advanced.html#returning-http-400-responses

И все. Обработка ошибок делается один раз и выводит то что надо.
Не, я к тому, что react выведет ошибку «Invalid username and password», а в случае с required=True выведет «Network error».
Вот что будет в браузере.
А я вам и говорю, что поведение это изменяемое. И да 422 ошибка это не Network error. Она должна обрабатываться на стороне react особенно учитывая что подразумевается REST а там кодами отличными от 200 вообще-то пользоваться надо.
Потратил 3 часа на то, чтобы разобраться почему шаг в сторорону от стандартного туториала делает так что не работает ничего. Либо GuesserList выдает ошибку, либо самописный List вообще не делает запросов. Как отлаживать этого монстра вообще ума не приложу.
Можно было бы хотя бы попробовать сделать дебаггинг сетевых запросов, почему например тот же файл с JSON из туториала сохраненный на локальном сервере, сразу выдает ошибку что пользователи не найдены. При этом дебаг запросов есть только в FakeDataProvider, где он и так не нужен.
Ну, да. Я на это тоже посмотрел, разбираться поленился и просто добавил axios.
В итоге помогло добавление в ответ заголовка:

"Access-Control-Allow-Origin: адрес.сайта.на.которомюадминка.com"

Такое надо писать большими красными буквами, а ни в одном дефолтном провайдере это не написано.
Довелось мне поработать с react-admin.
Это какой-то сборник антипаттернов, а не фреймворк для админок.
Сойдет, если заказчика устраивает функционал точь в точь, как в демке. Шаг в сторону, получаем проблему на проблеме.
Only those users with full accounts are able to leave comments. Log in, please.