Страницы

Поиск по вопросам

Показаны сообщения с ярлыком pymongo. Показать все сообщения
Показаны сообщения с ярлыком pymongo. Показать все сообщения

среда, 22 января 2020 г.

Авторизация и сессии в django и piston

#сессия #pymongo #django


Доброе время!
Имею urls.py:
auth = JSONAuthentication()
user_handler = Resource(UserHandler, auth)

urlpatterns = patterns('',
    url(r'^login/$', user_handler, { 'emitter_format': 'json' }),
)

Авторизация работает как. Сначала challenge 401, отправляю username и password, request.user
устанавливается какой нужно. дальше в handlers.py:
class UserHandler(BaseHandler):
    allowed_methods = ('POST',)
    def create(self, request):
        auth.login(request, request.user)
        return {u'user':unicode(request.user)}

т.е. делаю login, django в auth.login пишет request.session['_auth_user_id']=user.id,
далее оно сохраняется в базу сессий.
С этим всем хозяйством разобрался, отдебажил. Дальше в браузере жму F5, ожидаю, что
сессия (длиной в неделю) сохранится и request.user заполнится по имеющейся сессии...
Срабатывает опять авторизация, в авторизации предварительно ищу юзера в сессии:
class JSONAuthentication(object):
    ....
    def is_authenticated(self, request):
        user = auth.get_user(request)
        ....

И вот тут оказывается, что auth.get_user ищет в сессии request.session['_auth_user_id'],
которой там нет, и отваливается. В сессии на этот момент есть только sessionid=<правильная
сессия>, по которой надо получить сессию из базы - по коду оно дальше - и там уже user_id
будет и проч...
Вот я думаю, либо неправильно что-то делаю, либо что-то не допилил.
ЗЫ используется связка python 2.7, django 1.3, piston (rest), pymongo (база), mango
(хранение сессий и юзеров в mongodb)
UPD Проблему решить удалось, осталось выяснить чей косяк. Мой или pymongo :)    


Ответы

Ответ 1



Проблему удалось решить двумя способами. Сложный. Проблема была в том, что сессия не восстанавливалась (обращение в auth.get_user к request.session['_auth_user_id'] вызывало восстановление сессии). При восстановлении возникало исключение UnicodeEncodeError в pymongo'вском bson.Objectid, который pymongo не обрабатывают (обработывается только UnicodeDecodeError, т.е. зеркальный эксепшин). Добавление в __setstate__ дополнительного UncodeEncodeError в секцию except решало проблему. Написал краткий тест с минимальными вызовами для выявления бага, в шелле он отрабатывал как надо, а в eclipse при отладке в частности валился, притом молчаливо - except перекрывал исключение, но не обратывал его. Пытался даже с pymongo'вцами разобраться, у них тоже в шелле все прекрасно работало. Оттуда выяснился более простой метод "исправления". Простой метод основан на том, что pydev в eclipse при старте приложения устанавливает свои параметры консоли, в частности меняет дефолтную кодировку (которая указывается в настройках pydev'а). Из-за нее-то и возникала проблема. У меня стояла в настройках cp1251, а в шелле - ascii, при которой работало все. Установил в настройках pydev'а ascii (utf-8 тоже можно как оказалось) и все заработало. В инете много где пишут про этот баг, мол setdefaultencoding - evil, evil, evil и в частности про pydev этим занимающийся в частности. Относительно pymongo, не знаю баг ли это в pymongo - то, что при одной дефолтной кодировке работает, при другой не хочет, решать им конечно, но имхо, стоит добавить этот злосчастный UnicodeEncodeError, хотя я видимо был бы единственным, кого это коснулось. UPD Разработчики pymongo признали такое поведение как ошибку, связанную с кодировками, используемыми в настройках сайта. В транк внесены изменения, убрана заглушка от ошибки преобразования из неизвестной кодировки в latin-1. Внесена корректная проверка значения, восстанавливаемого через pickle. В общем рад, что смог помочь выяснить причины, что привело к более правильному коду.

пятница, 10 января 2020 г.

Удаление словаря из массива Mongodb по номеру (pymongo)

#python #mongodb #pymongo


В коллекции есть массив со словарями:

{
"_id": {
    "$oid": "576502aca43aa11ca48bb8d5"
},
"event_name": "Тестовое",
"participants": [
    {
        "name": "Участник 1",
        "start_debt": 2500,
        "income": 0,
        "debt": 2500
    },
    {
        "name": "Участник 2",
        "start_debt": 2500,
        "income": 0,
        "debt": 2500
    },
    {
        "name": "Участник 3",
        "start_debt": 2500,
        "income": 0,
        "debt": 2500
    },
    {
        "name": "Участник 3",
        "start_debt": 2500,
        "income": 0,
        "debt": 2500
    }
],
"debt": 10000,
"income": 0,
"budget": 10000}


Нужно удалить элемент из массива(словарь) именно по порядковому номеру.

Пытался сделать таким образом:

data_base.update_one({"_id": ObjectId(event_id)},
                                 {'$pull': {'participants': 2}})


Естественно результата не добился.
    


Ответы

Ответ 1



Напрямую удалить элемент по индексу пока нельзя, смотрите ишью https://jira.mongodb.org/browse/SERVER-1014. Но можно обойти с помощью двух последовательных команд: mongo.db.collection.update({}, {'$unset': {'participants.2':1}}) mongo.db.collection.update({}, {'$pull': {'participants': None}}) Сначала заменяем нужный элемент (в данном случае с индексом 2) на null, затем второй командой удаляем его. Большой минус - действия не атомарные, и после выполнения первого, какое то время в массиве будет элемент со значением null. Если это критично, то верный способ это вручную считать/модифицировать/записать документ.

среда, 10 июля 2019 г.

Mongodb aggregate (агрегация/группировка товаров)

Решил поупражняться с парсерами и работой с mongodb, но столкнулся со следующей проблемой.
Имеется коллекция с товарами. Для примера возьмем следующие документы из нее:
{title: 'acer aspire 6420', source: 'amazon', price: 300} {title: 'acer aspire 6420', source: 'ebay', price: 320}
Из них необходимо получить следующий документ:
{title: 'acer aspire 6420', amazon: 300, ebay: 320}
то есть сгруппировать по названию и вывести цену в каждом из магазинов.
В ходе разбирательства написал следующий код на питоне:
from bson.code import Code
reducer = Code(""" function(origin, res){ res[origin.source] = origin.price } """)
db.products.group(key={"source":1, 'title': 1, 'price': 1}, condition={'title': 'acer aspire 6420'}, initial={}, reduce=reducer)
но по итогу получаю вот такой результат:
{title: 'acer aspire 6420', source: 'amazon', price: 300, amazon: 300, ebay: 320} {title: 'acer aspire 6420', source: 'ebay', price: 320, amazon: 300, ebay: 320}
Подскажите, пожалуйста, в какую сторону дальше копать или как это реализовать, чтобы результат получился таким:
{title: 'acer aspire 6420', amazon: 300, ebay: 320}
Я гуглил и читал документацию, правда. Просто не очень понимаю как это все правильно связать вместе.


Ответ

Вариант с aggregation framework (для mongoshell):
db.products.aggregate([ {$match: {title: "acer aspire 6420"}}, {$project: {_id: 0, title: 1, price: {source: "$source", value: "$price"} }}, {$group: {_id: "$title", prices: {$push: "$price"}}} ])
Что дает на выходе:
{ "_id" : "acer aspire 6420", "prices" : [ { "source" : "amazon", "value" : 300 }, { "source" : "ebay", "value" : 320 } ] }
Первая операция фильтрует по названию (title) товара, следующая формирует поле price в виде объекта с двумя полями - source и value, и последняя операция группирует по title, добавляя все варианты цен в создаваемое поле-массив prices
Т.к. у вас по условию задачи source должен быть именем поля в результате, а пока в mongodb aggregation framework поддержки динамических имен для создаваемых полей нет (https://jira.mongodb.org/browse/SERVER-5947), то можно пройтись по результату с помощью map
db.products.aggregate([ {$match: {title: "acer aspire 6420"}}, {$project: {_id: 0, title: 1, price: {source: "$source", value: "$price"} }}, {$group: {_id: "$title", prices: {$push: "$price"}}} ]).map(function(e) { var r = {} r.title = e._id; e.prices.forEach(function(i){r[i.source] = i.value}); return r; })
Что даст на выходе:
[ { "title" : "acer aspire 6420", "amazon" : 300, "ebay" : 320 } ]
В этом случае есть вероятность (в отличии от первого варианта) потерять информацию, если у вас, например, для amazon есть две разных цены.
Для питона, думаю, сможете сделать по аналогии.

пятница, 5 июля 2019 г.

Подтверждение записи данных в mongoDB

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


Ответ

Для этой цели в MongoDB специально введено понятние Write Concern. Выдержка из документации: write concern Specifies whether a write operation has succeeded. Write concern allows your application to detect insertion errors or unavailable mongod instances. For replica sets, you can configure write concern to confirm replication to a specified number of members. See Write Concern Дословно: Указывает, успешно ли выполнилась операция записи. "Write concern" позволяет вашим приложениям выявлять ошибки вставки или недоступности mongod. Для replica-set вы можете задать write concern, чтобы подтверждать репликацию в заданное число реплик. Существует определенный набор значений для этого параметра: Unacknowledged Acknowledged (by default) Journaled Replica Acknowledged По умолчанию драйвером используется Acknowledged уровень - mongod в этом случае будет сообщать клиенту о результатах записи. Т.е. клиент может отловить сетевые ошибки , получить сообщение duplicate key или другую ошибку. (Всё расписано в документации). Значение write concern'а в драйвере можно поменять в классе MongoClient

суббота, 9 марта 2019 г.

Авторизация и сессии в django и piston

Доброе время! Имею urls.py: auth = JSONAuthentication() user_handler = Resource(UserHandler, auth)
urlpatterns = patterns('', url(r'^login/$', user_handler, { 'emitter_format': 'json' }), ) Авторизация работает как. Сначала challenge 401, отправляю username и password, request.user устанавливается какой нужно. дальше в handlers.py: class UserHandler(BaseHandler): allowed_methods = ('POST',) def create(self, request): auth.login(request, request.user) return {u'user':unicode(request.user)} т.е. делаю login, django в auth.login пишет request.session['_auth_user_id']=user.id, далее оно сохраняется в базу сессий. С этим всем хозяйством разобрался, отдебажил. Дальше в браузере жму F5, ожидаю, что сессия (длиной в неделю) сохранится и request.user заполнится по имеющейся сессии... Срабатывает опять авторизация, в авторизации предварительно ищу юзера в сессии: class JSONAuthentication(object): .... def is_authenticated(self, request): user = auth.get_user(request) .... И вот тут оказывается, что auth.get_user ищет в сессии request.session['_auth_user_id'], которой там нет, и отваливается. В сессии на этот момент есть только sessionid=<правильная сессия>, по которой надо получить сессию из базы - по коду оно дальше - и там уже user_id будет и проч... Вот я думаю, либо неправильно что-то делаю, либо что-то не допилил. ЗЫ используется связка python 2.7, django 1.3, piston (rest), pymongo (база), mango (хранение сессий и юзеров в mongodb) UPD Проблему решить удалось, осталось выяснить чей косяк. Мой или pymongo :)


Ответ

Проблему удалось решить двумя способами. Сложный. Проблема была в том, что сессия не восстанавливалась (обращение в auth.get_user к request.session['_auth_user_id'] вызывало восстановление сессии). При восстановлении возникало исключение UnicodeEncodeError в pymongo'вском bson.Objectid, который pymongo не обрабатывают (обработывается только UnicodeDecodeError, т.е. зеркальный эксепшин). Добавление в __setstate__ дополнительного UncodeEncodeError в секцию except решало проблему. Написал краткий тест с минимальными вызовами для выявления бага, в шелле он отрабатывал как надо, а в eclipse при отладке в частности валился, притом молчаливо - except перекрывал исключение, но не обратывал его. Пытался даже с pymongo'вцами разобраться, у них тоже в шелле все прекрасно работало. Оттуда выяснился более простой метод "исправления". Простой метод основан на том, что pydev в eclipse при старте приложения устанавливает свои параметры консоли, в частности меняет дефолтную кодировку (которая указывается в настройках pydev'а). Из-за нее-то и возникала проблема. У меня стояла в настройках cp1251, а в шелле - ascii, при которой работало все. Установил в настройках pydev'а ascii (utf-8 тоже можно как оказалось) и все заработало. В инете много где пишут про этот баг, мол setdefaultencoding - evil, evil, evil и в частности про pydev этим занимающийся в частности. Относительно pymongo, не знаю баг ли это в pymongo - то, что при одной дефолтной кодировке работает, при другой не хочет, решать им конечно, но имхо, стоит добавить этот злосчастный UnicodeEncodeError, хотя я видимо был бы единственным, кого это коснулось. UPD Разработчики pymongo признали такое поведение как ошибку, связанную с кодировками, используемыми в настройках сайта. В транк внесены изменения, убрана заглушка от ошибки преобразования из неизвестной кодировки в latin-1. Внесена корректная проверка значения, восстанавливаемого через pickle. В общем рад, что смог помочь выяснить причины, что привело к более правильному коду.

понедельник, 25 февраля 2019 г.

Удаление словаря из массива Mongodb по номеру (pymongo)

В коллекции есть массив со словарями:
{ "_id": { "$oid": "576502aca43aa11ca48bb8d5" }, "event_name": "Тестовое", "participants": [ { "name": "Участник 1", "start_debt": 2500, "income": 0, "debt": 2500 }, { "name": "Участник 2", "start_debt": 2500, "income": 0, "debt": 2500 }, { "name": "Участник 3", "start_debt": 2500, "income": 0, "debt": 2500 }, { "name": "Участник 3", "start_debt": 2500, "income": 0, "debt": 2500 } ], "debt": 10000, "income": 0, "budget": 10000}
Нужно удалить элемент из массива(словарь) именно по порядковому номеру.
Пытался сделать таким образом:
data_base.update_one({"_id": ObjectId(event_id)}, {'$pull': {'participants': 2}})
Естественно результата не добился.


Ответ

Напрямую удалить элемент по индексу пока нельзя, смотрите ишью https://jira.mongodb.org/browse/SERVER-1014
Но можно обойти с помощью двух последовательных команд:
mongo.db.collection.update({}, {'$unset': {'participants.2':1}}) mongo.db.collection.update({}, {'$pull': {'participants': None}})
Сначала заменяем нужный элемент (в данном случае с индексом 2) на null, затем второй командой удаляем его.
Большой минус - действия не атомарные, и после выполнения первого, какое то время в массиве будет элемент со значением null. Если это критично, то верный способ это вручную считать/модифицировать/записать документ.