Страницы

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

четверг, 7 марта 2019 г.

Сессия пользователя

Пишу небольшой проект на связке flask + tornado. На flask написан общий функционал (на данный момент авторизация + регистрация пользователей, также создана модель), на торнадо пытаюсь написать чат.
Как tornado научить понимать, под каким пользователем мы сейчас авторизованы, и от его имени писать в чат сообщения?
P.S. Если кто желает поучастовать в разработке данного проекта - милости прошу. Хочу сделать нечто для голосового общения в веб. Цель - получения опыта в подобного рода разработке.


Ответ

Очень интересный и актуальный вопрос для Tornado. Способов реализации много, торнадо тем и хорош что из коробки нет никакого навязанного решения, можно дать волю фантазии и заточить все максимально вкусно для своего проекта.
Предлагаю такой вариант (опускаю лишний код, чтобы показать только суть):
class BaseHandler(tornado.web.RequestHandler):
...
def get_current_user(self): user = None user_json = self.get_secure_cookie("mysite_cookie_user") if not user_json: return None logging.info("User ID:%s is get response", str(user_json)) try: user = tornado.escape.json_decode(user_json) except Exception: logging.info("Error get user cookie") return user
def get_visiter_settings(self): settings_json = self.get_secure_cookie("mysite_cookie_settings") try: cur_settings = tornado.escape.json_decode(settings_json) logging.info("Visiter settings:%s is get response from cookie.", str(cur_settings)) except Exception: logging.info("Error get settings from cookie.") logging.info("Init new cookie settings.") cur_settings = { "visitor_uuid": str(uuid.uuid4()), "option1": "foo",
...
"option10": "bar"} self.set_secure_cookie("mysite_cookie_settings", json.dumps(cur_settings)) logging.info("New cookie settings initialize.") finally: return cur_settings
class MainHandler(BaseHandler):
@tornado.web.asynchronous @tornado.gen.coroutine def get(self,*args, **kwargs): current_user = self.get_current_user() visiter_settings = self.get_visiter_settings()
Хеши сессий можно хранить например в Redis.
В хендлерах состояние сессий можно получать и обновлять по ключам сформированным от current_user и visiter_settings["visitor_uuid"].
Например, для гостей сессии определять по visitor_uuid, для авторизованных по current_user + можно их линковать к их уже существующей сессии по visitor_uuid.

Обновление slug в django

category_name = models.CharField(max_length = 220) slug = AutoSlugField(populate_from='category_name', unique=True, db_index=True)
Есть поле slug. И там уже записано значение которое соответствует названию. Но когда я меняю название, slug не меняется. Подскажите что делать? Как обновить slug?


Ответ

Нужно добавить always_update= True Вот так будет:
slug = AutoSlugField(populate_from='category_name', unique=True, db_index=True, always_update=True)
А чтобы вручную править то можно еще добавить: editable=True

Обрабатывать нажатия на картинки

Есть масив
PictureBox[,] picture = new PictureBox[20, 20];
Создаю 20 картинок в масиве и вывожу их на панель
for (int j = 0; j < 20; j++) { picture[i, j] = new PictureBox();
picture[i, j].BackColor = Color.Azure; picture[i, j].Location = new Point(40 * i, 40 * j); panel1.Controls.Add(picture[i, j]); }
Как мне узнать и сменить цвет той картинке из массива, на которой кликнут мышкой?


Ответ

for (int j = 0; j < 20; j++) { ... picture[i, j].Click += PictureBoxClick; }
private void PictureBoxClick(object sender, System.EventArgs e) { if (sender is PictureBox) { ((PictureBox)sender).BackColor = Color.Red; } }

Сетевое взаимодействие в Андроид приложениях

Как в Андроид-приложениях программно реализуется сетевое взаимодействие между устройствами?
Например: есть приложение Шахматы. Играют два человека на разных устройствах (соединенных (как-то) через интернет/wi-fi) Один делает ход - движение фигуры отображается у другого и наоборот. Т.е. между устройствами передается какой-то объем данных, который обрабатывается и отображается.
Собственно, вопрос в чем: как и через что реализуется подобное взаимодействие? (как его запрограммировать)
с чего вообще начать в этом разбираться? начал изучать Google Cloud Messager, но есть сомнения, что это шаг в верном направлении. Рассмотрел возможность создания своего сервера под такие нужды, но понял, что пока не готов вкладывать время в его изучение.
Буду благодарен любым (особенно) русскоязычным ссылкам на то, что можно прочесть касательно программирования сетевого взаимодействия Андроид, и советам в каком месте интернета/девелопер гайда стоит покопаться.


Ответ

Без сервера никак не обойтись. У вас должна быть информация и логика игры написана на сервере, с которого клиенты должны получать данные в свои телефоны. Как это реализовать? Например, своровать код из гугла по созданию чата и переделать под свои нужды. Для сетевого взаимодействия здесь всё так же используется класс Socket

Асинхронный вызов метода в WPF

У меня имеется WPF-приложение, работающее с базой данных. Количество записей в базе довольно большое и постоянно растёт. В проекте используется ORM(EF 6). Имеется некий класс, работающий непосредственно с контекстом базы данных:
public class Store : IStore {...}
В интерфейсе класса определён ряд методов, код в которых обращается непосредственно к базе через EF-контекст, соответственно, выполнение метода занимает довольно длительное время, что может вызвать простой интерфейса пользователя, если я всё правильно понимаю. Следовательно, нужно вынести операции получения данных из базы в отдельные потоки. Так, скажем, в Store определен метод:
public ICollection GetAllProducts() {...}
Если я правильно понимаю, мне необходимо добавить его асинхронную реализацию:
public async Task> GetAllProductsAsync() { return await Task>.Factory.StartNew(GetAllProducts) }
Как правильно воспользоваться таким методом в самом приложении в ViewModel, Скажем, чтобы, пока данные загружаются, в StatusBar отображался прогресс загрузки данных, а DataGrid отобразил результат как только данные загрузятся?


Ответ

Окей, давайте начнём с Task.Factory.StartNew. Это нужно только если ваш запрос к базе данных не поддерживает асинхронность сам, и требует выделения отдельного потока (кстати, лучше писать просто Task.Run).
Для свежего Entity Framework это не так, асинхронные функции поддерживаются правильно, из коробки: Entity Framework tutorial: async query and save
С асинхронностью на уровне базы данных вам не нужно создавать отдельные потоки.

По поводу прогресса, с этим хуже. EF не поддерживает информацию о прогрессе операции, так что вы можете просто вывести состояние «читаю», и считывать до тех пор, пока не закончите.
В случае, когда/если будет имплементирована поддержка прогресса, вам можно будет воспользоваться интерфейсом IProgress

Таким образом, код в VM будет выглядеть так:
IsLoading = true; var localData = await model.LoadDataAsync(); IsLoading = false; Data = localData;

Скорее всего, вы не захотите выкладывать модельные классы для View, поэтому вам понадобится обёртка, создающая VM-объекты для ваших entity:
// в модели IQueryable GetData();

// в VM IsLoading = true; var localData = new List(); await model.GetData().ForEachAsync(entity => localData.Add(new EntityVM(entity)); IsLoading = false; Data = localData;
Ну и, как верно отмечает @Pavel Mayorov, возможно вы захотите ловить исключения, так что вам понадобится try:
IsLoading = true; var localData = new List(); try { await model.GetData().ForEachAsync(entity => localData.Add(new EntityVM(entity)); Data = new ObservableCollection(localData); } catch { IsFailed = true; throw; } finally { IsLoading = false; }
Или, если вы хотите, чтобы данные появлялись не все вместе, а по мере подгрузки, наверное подойдёт просто
await model.GetData().ForEachAsync(entity => Data.Add(new EntityVM(entity));
(но здесь я не уверен, т. к. ни разу не пробовал).

Почему всегда threadPoolExecutor.getActiveCount() <= maximumPoolSize/2?

Есть очередь задач, создана следующим образом:
LinkedBlockingQueue queue = new LinkedBlockingQueue(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(maximumPoolSize, maximumPoolSize, 50000L, TimeUnit.MILLISECONDS, queue);
Значение maximumPoolSize равно 200. Во время работы в queue попадает большое количество потоков (больше тысячи), но значение, возвращаемое методом threadPoolExecutor.getActiveCount() всегда меньше либо равно 100. Например, значения threadPoolExecutor.getActiveCount() и queue.size() логируются следующим образом:
logger.debug("Активные потоки: " + threadPoolExecutor.getActiveCount() + ". В очереди " + queue.size() + " потоков.");
и в итоге мы получаем следующую картину:
Активные потоки: 1. В очереди 0 потоков. Активные потоки: 2. В очереди 0 потоков. Активные потоки: 3. В очереди 0 потоков. Активные потоки: 4. В очереди 0 потоков. Активные потоки: 5. В очереди 0 потоков. ... Активные потоки: 86. В очереди 0 потоков. Активные потоки: 87. В очереди 1 потоков. Активные потоки: 88. В очереди 1 потоков. Активные потоки: 89. В очереди 1 потоков. Активные потоки: 90. В очереди 1 потоков. ... Активные потоки: 99. В очереди 1 потоков. Активные потоки: 100. В очереди 1 потоков. Активные потоки: 100. В очереди 2 потоков. Активные потоки: 100. В очереди 3 потоков. Активные потоки: 100. В очереди 4 потоков. ... Активные потоки: 100. В очереди 1874 потоков. Активные потоки: 100. В очереди 1875 потоков. Активные потоки: 100. В очереди 1876 потоков. Активные потоки: 100. В очереди 1877 потоков. Активные потоки: 100. В очереди 1878 потоков. Активные потоки: 100. В очереди 1879 потоков. Активные потоки: 100. В очереди 1880 потоков. Активные потоки: 100. В очереди 1881 потоков. Активные потоки: 100. В очереди 1882 потоков. Активные потоки: 100. В очереди 1883 потоков.
В документации написано, что метод threadPoolExecutor.getActiveCount() возвращает приблизительное количество потоков с активно выполняющимися задачами. Но почему максимальный порог этого приблизительного значения тут равен maximumPoolSize/2?
P.S. Это логи не с моего компьютера, у меня такую ситуацию воспроизвести не удалось - у меня в данном случае количество активных потоков равняется 200, как и ожидалось. Может ли это быть зависимость от количества процессоров/количества ядер процессоров/какой-либо конфигурации ПО?


Ответ

"Есть очередь задач, создана следующим образом: ... new ThreadPoolExecutor(maximumPoolSize, maximumPoolSize, .."
Вы уверены, что maximumPoolSize - это одна переменная, и она имеет одно значение? Полагаю, что причина в том, что corePoolSize равен 100, а maximumPoolSize равен 200 и у Вас происходит следующее?
При добавлении нового потока в пул выполняет следующие действия последовательно:
Проверяет достигнут ли предел потоков, задаваемых переменной corePoolSize если можно еще запустить поток - запускает его. Если количество потоков равно или больше corePoolSize переходит к следующему шагу. Проверяет не заполнена ли очередь и если в ней еще есть место добавляет туда поток. Если очередь заполнена переходит к следующему шагу. Пытается увеличить количество работающих потоков до значения maximumPoolSize если можно добавить еще поток, то он добавляется и в нем и запускается задание. Если количество работающих потоков равно maximumPoolSize то выбрасывается исключение java.util.concurrent.RejectedExecutionException или вызывается перехватчик отклонения задачи определяемый программистом.
В вашем случае в виду того, что очередь увеличивается без ограничений к третьему шагу алгоритм никогда не переходит. Но можно «изменить» порядок следования шагов 2 и 3. Для этого необходимо создать очередь в которой мы переопределим вызываемый ThreadPoolExecutor метод offer так, чтобы он возвращал всегда false (очередь заполнена). Теперь нам нужно задать собственный перехватчик RejectedExecutionHandler. В перехватчике мы добавляем полученный поток в очередь. Пример изложенного выше в виде кода:
LinkedBlockingQueue queue = new LinkedBlockingQueue (100){ public boolean offer(Runnable e) {return false;} };
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(100, 200, 1, TimeUnit.MINUTES, queue, new RejectedExecutionHandler() { public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { try { executor.getQueue().put(r); } catch (InterruptedException e) { e.printStackTrace(); } } });

Django. Как вывести тэги в шаблоне?

Подключил django-taggit к блогу. Хочу вывести определенный набор тегов под каждой статьей, используя {{ article.tags.names }}. В шаблоне - получаю список тэгов под статьей ['linux', 'python', 'english']. Как можно исправить это?
views.py
def index(request): lastet_articles_list = Article.objects.order_by('-pub_date') list_of_tags = [] for obj in lastet_articles_list: list_of_tags += obj.tags.names() list_of_tags = list(set(list_of_tags)) context = { 'lastet_articles_list': lastet_articles_list, 'list_of_tags': list_of_tags, } return render(request, 'article/index.html', context)
(list_of_tags здесь использую, чтобы просто вывести имеющиеся тэги на сайте)
index.html

{% if lastet_articles_list %} {% for article in lastet_articles_list %}
{{ article.title }}

{% endfor %} {% endif %}

models.py
from taggit.managers import TaggableManager
class Article(models.Model): title = models.CharField(max_length=50) pub_date = models.DateTimeField('date published') text = models.TextField() tags = TaggableManager()


Ответ

Все правильно, метод names возвращает массив имен тэгов. Выводите их по одному в вашем блоге.

Эксперимент, чтобы понять как это работает:
>>> from Tasks.models import Post >>> p = Post.objects.get(pk=1) >>> p.tags >>> p.tags.add('cat', 'dog') >>> p.save() >>> p.tags.names > >>> p.tags.names() ['cat', 'dog'] >>> for name in p.tags.names(): ... print(name) ... cat dog >>> p = Post() >>> p.title = 'Post 2' >>> p.text = 'second post' >>> p.save() >>> p.pk 2 >>> p.tags.add('cat') >>> p.save() >>> Post.objects.filter(tags__name__in=["cat"]) [, ] >>> Post.objects.filter(tags__name__in=["dog"]) []