Comments 41
Админка за 0 минут — запустите любой Database-клиент. Я так некоторые проекты годами веду, очень удобно, все возможности.
Restfull-админка — это неудобно, потому что элементарные сущности и так можно добавлять в базу, а что-то более сложно все равно придется допиливать.
Restfull-админка — это неудобно, потому что элементарные сущности и так можно добавлять в базу, а что-то более сложно все равно придется допиливать.
Админкой пользуются не только люди, которые умеют с бд клиентом работать. Кроме того в таких приложениях часто присутствует какая-то логика помимо просто записей в базе и не на уровне бд.
Хранимые процедуры и вот вам логика
Во многих проектах стараются их избегать. Потому что уйдет человек, которых их писал и это очень тяжело поддерживать. И с версионированием этого всего как-то непонятно. И удобнее когда вся логика в одном месте — в приложении.
Каким образом хранимые процедуры могут, например, отправить письма или сделать https запрос?
Легко, в MS SQL есть CLR-сборки, по сути вызов функций из DLL.
Source: Работаю с биллинговой системой, построенной подобным образом.
Source: Работаю с биллинговой системой, построенной подобным образом.
Для Postgres есть hasura (https://hasura.io/), которая позволяет создавать graphql endpoint, который имеет в себе всё что только нужно, однако, все подобные технологии пока ещё не до конца готовы и сыроваты, ещё требуется 2-3 года до готовности, когда наконец-то монолит из данных и логики будет храниться полностью в БД.
Ты же просто хранимая процедура, имитация ЯП. Разве может хранимая процедура отправить письмо или сделать http-запрос?
UFO just landed and posted this here
Джанговская админка обычно всех устраивает и там есть всё что угодно из коробки или на pypi. И кастомизируется как угодно.
UFO just landed and posted this here
Какой-то абстрактный пример. В админке django modelform можно кастомизировать, какие там должны быть поля и как их потом сохранить в бд Остальное накручивается на js.
Стоит добавить в проект webargs и marshmallow. С ними делать REST под flask прям хорошо и прекрасно.
Спасибо за коммент. А вроде есть reqparse в flask_restful.
https://flask-restful.readthedocs.io/en/latest/api.html#module-reqparse
Нужен ли webargs?
А по-поводу marshmallow, я к нему присматриваюсь. И раз вы здесь его упомянули хотелось бы спросить: у вас есть позитивный опыт использования? Можете что-нибудь про него рассказать хорошее?
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.
Дополнительно он имеет отличную интеграцию с marshmallow, что позволяет прям объекты напрямую из запроса доставать.
Marshmallow я использую и это единственный на данный момент сериализатор под python который нормально из PostgreSQL жрет нативные uuid. Дополнительно там есть слой совместимости с sqlalchemy. Который мне правда не актуален, я использую PonyORM.
Мне больше нравится pydantic. У себя я превращаю аргументы функции в параметры запроса так:
это можно использовать потом в декораторе или middleware
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
К примеру кусок кода из моего проекта. Аннотацию входных данных можно так же брать из схемы marshmallow.
На abort дополнительно можно довесить обработчик, в 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 если требуется так же можно его довесить.
Так это код из моей библиотеки. А в самом проекте будет просто
Мне нравится использовать аннотации типов, этим pydantic больше нравится. В fastapi он аналогично используется
async def test_handler(request, query: str, page: int=1): pass
Мне нравится использовать аннотации типов, этим pydantic больше нравится. В fastapi он аналогично используется
norguhtar, спасибо за наводку.
Получается, что если мы хотим использовать webargs в примере из статьи.
То писать надо примерно так:
Верно? Я проверил, в принципе, работает.
Получается, что если мы хотим использовать 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
Верно? Я проверил, в принципе, работает.
Да. Только еще стоит добавить:
В этом случае если параметры не обнаружены, вывалит ошибку по умолчанию. Туда можно вставить свой хендлер обработки и обрабатывать отсутствие параметров единожды. Там еще есть опция missing которая позволяет задавать умолчания, с ней надо учесть, что умолчания там задаются один раз при старте. А то я там положил раз datetime.now() :)
@use_args({
'username': fields.Str(required=True),
'password': fields.Str(required=True)},
locations=['json'])
В этом случае если параметры не обнаружены, вывалит ошибку по умолчанию. Туда можно вставить свой хендлер обработки и обрабатывать отсутствие параметров единожды. Там еще есть опция missing которая позволяет задавать умолчания, с ней надо учесть, что умолчания там задаются один раз при старте. А то я там положил раз datetime.now() :)
Допустим удалим один или несколько параметров, то мы получаем в json
А если есть required=True, то получим
Видимо, надо по ситуации все же решать. Либо как-то научить его отдавать 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
И все. Обработка ошибок делается один раз и выводит то что надо.
Если хочется отдавать 200 то так
webargs.readthedocs.io/en/latest/advanced.html#returning-http-400-responses
И все. Обработка ошибок делается один раз и выводит то что надо.
Просто годная библиотека для сериализации. Кстати, я тут сделал генераторы маршмаллов из моделей peewee и алхимии, может кому пригодится
github.com/pawnhearts/aiorf/blob/master/aiorf/modelschema.py
github.com/pawnhearts/aiorf/blob/master/aiorf/saschema.py
github.com/pawnhearts/aiorf/blob/master/aiorf/modelschema.py
github.com/pawnhearts/aiorf/blob/master/aiorf/saschema.py
UFO just landed and posted this here
Можно попробовать начать с simple:
codesandbox.io/s/github/marmelab/react-admin/tree/master/examples/simple
codesandbox.io/s/github/marmelab/react-admin/tree/master/examples/simple
Довелось мне поработать с react-admin.
Это какой-то сборник антипаттернов, а не фреймворк для админок.
Сойдет, если заказчика устраивает функционал точь в точь, как в демке. Шаг в сторону, получаем проблему на проблеме.
Это какой-то сборник антипаттернов, а не фреймворк для админок.
Сойдет, если заказчика устраивает функционал точь в точь, как в демке. Шаг в сторону, получаем проблему на проблеме.
Спасибо за инфу.
Можете порекомендовать альтернативу?
Можете порекомендовать альтернативу?
Сомневаюсь, что есть хорошие альтернативы.
Я не пробовал, но встречал следующие:
cxjs.io — выглядит довольно интересно;
pro.ant.design
Я не пробовал, но встречал следующие:
cxjs.io — выглядит довольно интересно;
pro.ant.design
Sign up to leave a comment.
Админка за 5 минут. Фронтэнд — react-admin, бэкэнд — Flask-RESTful