Django channels 2 async retrieve and save data to database - python

I'm trying to asynchronously get object from the database, update it, and then save it with Django Channels 2. The retrieval works, but I'm having problems with the saving.
I already tried with
await database_sync_to_async(game.save)()
as well as moving the actual saving in another method, like this
#database_sync_to_async
def save(self, item):
item.save()
class GameController(AsyncWebsocketConsumer):
#property
#database_sync_to_async
def game(self):
return Game.objects.get(uuid=self.game_uuid)
async def save_connected(self):
game = await self.game
if not game.data:
game.data = {
'connected': False
}
else:
game.data['connected'] = True
await database_sync_to_async(game.save)()
But I always get this error: Object of type coroutine is not JSON serializable
or the data is not saved at all.

Related

Current user in Flask context processor

I'm using the context_processor decorator in Flask to define a variable, but I want the value of the variable to be dependent on the current user. I am using Turbo-Flask so that the variable's value automatically updates every few seconds. These are my codes:
def generate_dataframe(user):
if 'id' not in dir(user):
return pd.DataFrame()
return pd.DataFrame(['User Loaded'])
#app.context_processor
def inject_load():
return {'df': generate_dataframe(current_user)}
#turbo.user_id
def get_user_id():
if current_user.is_authenticated:
return current_user.id
else:
return None
#app.before_first_request
def before_first_request():
threading.Thread(target=update_table).start()
def update_feed():
with app.app_context():
while True:
time.sleep(1)
turbo.push(turbo.replace(render_template('feed.html'),'feed'))
When I checked, df is always an empty dataframe even if I'm already logged in.
I want it to be similar to a news feed where the page will automatically refresh/update based on your friends' posts, for example.

How to use async function based views in DRF?

Since Django now supports async views, I'm trying to change my code base which contains a lot of function based views to be async but for some reason its not working.
#api_view(["GET"])
async def test_async_view(request):
...
data = await get_data()
return Response(data)
When I send a request to this endpoint, I get an error saying:
AssertionError: Expected a Response, HttpResponse or
HttpStreamingResponse to be returned from the view, but received a
<class 'coroutine'>
Does DRF not support async views yet? Is there an alternative I can do to get this working?
As of now, DRF doesn't support async "api views". Here is an open issue (#7260) in the DRF community and it is still in the discussion stage.
But, Django providing a decorator/wrapper which allow us to convert our sync views/function to async using sync_to_async(...) wrapper.
Example,
#sync_to_async
#api_view(["GET"])
def sample_view(request):
data = get_data()
return Response(data)
Note that, here, sample_view(...) and get_data(...) are sync functions.
I think you can Use this decorator in DRF
import asyncio
from functools import wraps
def to_async(blocking):
#wraps(blocking)
def run_wrapper(*args, **kwargs):
return asyncio.run(blocking(*args, **kwargs))
return run_wrapper
Example of usage
#to_async
#api_view(["GET"])
async def sample_view(request):
...

A Coroutine is returning a coroutine after await

I'm writing tests for a fastAPI on django with an ASGI server (adapted this tutorial). My fastAPI side of the test keeps returning errors and I'm trying in vain to fix it.
My need is about creating a user to test the API.
#sync_to_async
def _create_user(self, username, email):
try:
return User.objects.create(username=username, email=email)
except:
return None
async def setUp(self):
task = asyncio.create_task(self._create_user(username="user", email="email#email.com"))
self.user = await task
Running this test, it turn out that self.user is a coroutine and it's impossible to access the attributes I expect.
How to solve this ?
Update :
Removed async for _create_user(self, username, email).
According to the docs https://docs.djangoproject.com/en/3.1/topics/async/#asgiref.sync.sync_to_async
decorator sync_to_async should decorates sync functions. (see example)
I have an answer,.......................
Simply Create a normal synchronize function and call with with sync_to_async() function
This function is used to get the user_object
def get_user_object(mobile_no):
user_object = CustomUser.objects.get(mobile_no=mobile_no)
return user_object
This method is running in async so we need to call a sync function to async to get rid of error......
from channels.db import database_sync_to_async
async def connect(self):
username = await database_sync_to_async(get_user_object)(mobile_no="+9999999999")
print(user_object)

Receiving "'hmset' with mapping of length 0" error

I want to store my session data on redis dataset. I have set SESSION_ENGINE = 'redis' in settings.py.
Code for redis.py
#redis.py
from django.contrib.sessions.backends.base import SessionBase
from django.utils.functional import cached_property
from redis import Redis
class SessionStore(SessionBase):
#cached_property
def _connection(self):
return Redis(
host='127.0.0.1',
port='6379',
db=0,
decode_responses=True
)
def load(self):
return self._connection.hgetall(self.session_key)
def exists(self, session_key):
return self._connection.exists(session_key)
def create(self):
# Creates a new session in the database.
self._session_key = self._get_new_session_key()
self.save(must_create=True)
self.modified = True
def save(self, must_create=False):
# Saves the session data. If `must_create` is True,
# creates a new session object. Otherwise, only updates
# an existing object and doesn't create one.
if self.session_key is None:
return self.create()
data = self._get_session(no_load=must_create)
session_key = self._get_or_create_session_key()
self._connection.hmset(session_key, data)
self._connection.expire(session_key, self.get_expiry_age())
def delete(self, session_key=None):
# Deletes the session data under the session key.
if session_key is None:
if self.session_key is None:
return
session_key = self.session_key
self._connection.delete(session_key)
#classmethod
def clear_expired(cls):
# There is no need to remove expired sessions by hand
# because Redis can do it automatically when
# the session has expired.
# We set expiration time in `save` method.
pass
I am receiving 'hmset' with mapping of length 0 error on accessing http://localhost:8000/admin in django.
After removing SESSION_ENGINE='redis' I am not receiving this error.
From redis documentation:
As per Redis 4.0.0, HMSET is considered deprecated. Please use HSET in new code.
I have replaced this line in save() method:
self._connection.hmset(session_key, data)
with:
self._connection.hset(session_key, 'session_key', session_key, data)
On making the change, it works as expected.

How to handle tornado user logged out state properly with coroutine

Simple handler:
#gen.coroutine
def get(self):
current_user = yield self.get_current_user()
if not current_user:
self.redirect('/')
id = current_user['id']
...
Function for getting current user:
#gen.coroutine
def get_current_user(self):
cookie = self.get_secure_cookie('user')
if not cookie:
return
I have a simple handler. It checks if the user is logged in and redirects if current_user is None. The problem arises when the user is not logged in: the line id = current_user['id'] is still evaluated because get_current_user() is a coroutine and it will throw an exception like TypeError: 'NoneType' object is not subscriptable
How would you properly handle this if I want to keep get_current_user() as a coroutine?
You don't need to use coroutine decorator in get_current_user() and call it with yield since it's a very fast function.
Switching context with coroutine in this case likely make more work than get_current_user().

Categories

Resources