I'm making a real-time chat app using django channels.
I want to delete an object from database using django channels(actually deleting a message from the group). How can this be done?
This is my backend code:
import json
from django.contrib.auth.models import User
from channels.generic.websocket import AsyncWebsocketConsumer
from asgiref.sync import sync_to_async
from .models import Room, Message
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
data = json.loads(text_data)
print(data)
message = data['message']
username = data['username']
room = data['room']
await self.save_message(username, room, message)
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username': username
}
)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
username = event['username']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
'username': username
}))
#sync_to_async
def save_message(self, username, room, message):
user = User.objects.get(username=username)
room = Room.objects.get(slug=room)
Message.objects.create(user=user, room=room, content=message)
Should i use javascript?
I tried to delete some objects by using Ajax but it didn't work.
Related
I'm a little unsure how I should be implementing an async await setup in Flask, specifically an extension called flask-mailing (not flask-mail)
I have a file called jobs.py which has this block at the bottom:
async def send_registration_email(uid, token):
message = Message(
subject="Flask-Mailing module",
recipients=["test#yahoo.com"],
body="This is the basic email body",
)
await mail.send_message(message)
return jsonify(status_code=200, content={"message": "email has been sent"})
In my views.py I have this:
#auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterUserForm()
if form.validate_on_submit():
user = User.create(
username=form.data['username'],
email=form.data['email'],
password=form.data['password'],
remote_addr=request.remote_addr,
)
s = URLSafeSerializer(current_app.secret_key)
token = s.dumps(user.id)
send_registration_email(user.id, token)
flash(
(
'Sent verification email to {email}'.format(
email=user.email
)
),
'success'
)
return redirect(url_for('home.index'))
return render_template('register.html', form=form)
The error I get is that no email is sent, and when I shut down the server I get this error:
RuntimeWarning: coroutine 'send_registration_email' was never awaited
Is it right that both the await and return should also be within the jobs.py or is it only the function send_registration_email that should be there?
I am looking to get a simple login sequence on fastapi:
following This tutorial
from fastapi import FastAPI, Depends, HTTPException
from starlette.config import Config
from starlette.requests import Request
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, RedirectResponse
from authlib.integrations.starlette_client import OAuth, OAuthError
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="!secret")
config = Config(".env")
oauth = OAuth(config)
CONF_URL = "https://accounts.google.com/.well-known/openid-configuration"
oauth.register(
name="google",
server_metadata_url=CONF_URL,
client_kwargs={"scope": "openid email profile"},
)
#app.get("/")
async def home(request: Request):
user = request.session.get("user")
if user is not None:
email = user["email"]
html = (
f"<pre>Email: {email}</pre><br>"
'documentation<br>'
'logout'
)
return HTMLResponse(html)
return HTMLResponse('login')
#app.get("/login", tags=["authentication"])
async def login(request: Request):
redirect_uri = request.url_for("auth")
return await oauth.google.authorize_redirect(request, redirect_uri)
#app.get("/auth")
async def auth(request: Request):
try:
token = await oauth.google.authorize_access_token(request)
except OAuthError as error:
return HTMLResponse(f"<h1>{error.error}</h1>")
user = token.get("userinfo")
if user:
request.session["user"] = dict(user)
return RedirectResponse(url="/")
#app.get("/logout", tags=["authentication"])
async def logout(request: Request):
request.session.pop("user", None)
return RedirectResponse(url="/")
# Try to get the logged in user
async def get_user(request: Request) -> Optional[dict]:
user = request.session.get("user")
if user is not None:
return user
else:
raise HTTPException(status_code=403, detail="Could not validate credentials.")
return None
# Endpoint to protect
#app.get("/other_endpoint/")
async def other_endpoint_function(
user: Optional[dict] = Depends(get_user), id: str
):
# Chek if other is authenticated
if user.is_authenticated:
function()
else:
# First redirect to the login page
RedirectResponse("login")
# Once logged in re-run the initial request
RedirectResponse(f"/other_endpoint/{id}")
I am looking to protect only the other_endpoint the homepage has a link to the login page.
check that the user is authenticated before running the function
If the user is authenticated, run function
If the user is not authenticated
redirect to the login page
re-run the function after authentication
So far, I have tried multiple implementations with RedirectResponse, but i ends up bypassing the authentication
i make a telegram bot for administrate telegram groups, but this filter just check admin status:
import aiogram
import functools
from aiogram.dispatcher.filters import BoundFilter
class CheckFilter(BoundFilter):
key = 'is_admin'
def __init__(self, is_admin):
self.is_admin = is_admin
async def check(self, message: types.Message):
member = await bot.get_chat_member(message.chat.id, message.from_user.id)
return member.is_chat_admin() == self.is_admin
dp.filters_factory.bind(CheckFilter)
i need to check administrator permissions. Example: for use command !ban you need to ban users permission.
bot.get_chat_member(chat_id, bot_id) will do the trick.
aiogram documentation says:
Returns: Returns a ChatMember object on success
And what Telegram API documentation says about ChatMember:
...
can_edit_messages Boolean Optional. Administrators only. True, if the administrator can edit messages of other users and can pin messages; channels only
can_delete_messages Boolean Optional. Administrators only. True, if the administrator can delete messages of other users
can_restrict_members Boolean Optional. Administrators only. True, if the administrator can restrict, ban or unban chat members
...
Here is an example:
#dp.message_handler(commands=['rights'])
async def myrights(message: types.Message):
member = await bot.get_chat_member(message.chat.id, TOKEN.split(":")[0])
for x in member:
print(x)
And its output (on my terminal) when I write /rights#BOTUSERNAME on the group:
('user', {'id': xxxxxxxxxx, 'is_bot': True, 'first_name': 'xxxxx', 'username': 'xxxxx'})
('status', 'administrator')
('can_be_edited', False)
('can_change_info', True)
('can_delete_messages', True)
('can_invite_users', True)
('can_restrict_members', True)
('can_pin_messages', True)
('can_promote_members', False)
I want to notify the client when my model is saved.
I started by creating a django-signal on post_save.
#receiver(post_save, sender=Scooter)
async def scooter_post_update(sender, instance, created, **kwargs):
# Notify client here
Next I created the AsyncConsumer class from django-channels and provided its routing.
// routing.py
application = ProtocolTypeRouter({
# Empty for now (http->django views is added by default)
'websocket': AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter(
[
path('scooters/', ScootersUpdateConsumer)
]
)
)
)
})
// consumers.py
class ScootersUpdateConsumer(AsyncConsumer):
async def websocket_connect(self, event):
print("Connected!", event)
await self.send({
"type": "websocket.accept"
})
async def send_message(self):
await self.send({
"type": "websocket.send",
'text': 'Oy, mate!'
})
async def websocket_receive(self, event):
print("Receive!", event)
async def websocket_disconnect(self, event):
print("Disconnected!", event)
Now my question is how can I call send_message() from the scooter_post_update() method.
The steps are really straightforward. You have to get the channel layer and just send a message with type key set as your listening method name:
import channels
from asgiref.sync import async_to_sync
#receiver(post_save, sender=Scooter)
def scooter_post_update(sender, instance, created, **kwargs):
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.send)(
{"type": "send_message", "data": data}
)
and any other stuff you want to send over channel.
Beware that all the data you pass must be serializable, so you have to take care that all your objects are serialized beforehand.
The mandatory part of the dictionary you pass to the send method is the type key (as mentioned before), which has to contain the method name which will be called on the consumer.
Moreover, you can use groups, so you can broadcast a message to a group of listeners:
import channels
from asgiref.sync import async_to_sync
#receiver(post_save, sender=Scooter)
def scooter_post_update(sender, instance, created, **kwargs):
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(
"group_name", {"type": "send_message", "data": data}
)
and on the consumer side:
class ScootersUpdateConsumer(AsyncConsumer):
async def websocket_connect(self, event):
await self.channel_layer.group_add("group_name", self.channel_name)
await self.send({
"type": "websocket.accept"
})
Note that in both cases, you use the async_to_sync wrapper which should be used when you call async code from sync scope.
I want to run a consumer which requires authenticated user in my channels application
channels disconnecting the consumer saying that user isn't authenticated
but the user is authenticated and his cookies are updated in the browser
I have followed channels documentation which authenticates user
class LikeConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
self.user = self.scope["user"]
# user = self.scope['user']
user=self.user
print(user)
if user.is_anonymous:
await self.close()
print("user is anonymous")
else:
await self.accept()
# user_group = await self._get_user_group(self.scope['user'])
await self.channel_layer.group_add("{}".format(user.id), self.channel_name)
print(f"Add {self.channel_name} channel to post's group")
print('connected')
# #database_sync_to_async
# def _get_user_group(self, user):
# if not user.is_authenticated:
# raise Exception('User is not authenticated.')
# else:
# print("user is not authenticated")
# return user
async def disconnect(self,close_code):
user = self.scope['user']
await self.channel_layer.group_discard("{}".format(user.id), self.channel_name)
print(f"Remove {self.channel_name} channel from post's group")
i'm not sure what exactly the mistake is
user is anonymous
WebSocket DISCONNECT /like/ [127.0.0.1:50710]