I am making a chat app with django channels and it has 2 consumers. One is for the team chat and the other for personal. I am adding history for the chat. There are two models. "Chat" and "Chat2". Chat is for team and Chat2 for personal. The history is working with the team chat and the messages are added to the "Chat" model. But with personal it is added to "Chat" instead of "Chat2".
Here are the models:
class Chat(models.Model):
team_id = models.ForeignKey(Team,on_delete=models.CASCADE,null=True)
username = models.CharField(max_length=200,null=True)
message = models.CharField(max_length=200,null=True)
profile_pic = models.ImageField(upload_to='chat/',null=True)
class Chat2(models.Model):
username = models.CharField(max_length=200,null=True)
username2 = models.CharField(max_length=200,null=True)
message = models.CharField(max_length=200,null=True)
profile_pic = models.ImageField(upload_to='chat/',null=True)
room_name1 = models.CharField(max_length=200,null=True)
And here are the two consumers(The consumers are in the same file but I add it separately for cleaner code):
class ChatConsumer(AsyncWebsocketConsumer):
#sync_to_async
def get_profile(self):
return self.scope['user'].profile
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = text_data_json['profile_name']
team = text_data_json['team']
profile = await self.get_profile()
profile_pic = profile.profile_pic.url
await self.save_messages(message, team, username, profile_pic)
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username':username,
'team':team,
'profile_pic':profile_pic
}
)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
team = event['team']
username = event['username']
profile_pic = event['profile_pic']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
'team':team,
'username':username,
'profile_pic':profile_pic,
'type':'chat'
}))
#sync_to_async
def save_messages(self,message,team,username,profile_pic):
team1 = Team.objects.filter(team_name=team).first()
chat = Chat(team_id=team1,message=message,username=username,profile_pic=profile_pic)
chat.save()
The other consumer:
class Chat2Consumer(AsyncWebsocketConsumer):
#sync_to_async
def get_profile(self):
return self.scope['user'].profile
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = text_data_json['profile_name']
room_name = text_data_json['room_name']
profile = await self.get_profile()
profile_pic = profile.profile_pic.url
await self.save_messages1(message,username,profile_pic)
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username':username,
'profile_pic':profile_pic,
}
)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
username = event['username']
profile_pic = event['profile_pic']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
'username':username,
'profile_pic':profile_pic,
'type':'chat'
}))
#sync_to_async
def save_messages(self,message,username,profile_pic,username2):
Chat2.objects.create(message=message,username=username,profile_pic=profile_pic)
And routing.py
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
re_path(r'ws/chat2/(?P<room_name>\w+)/$', consumers.Chat2Consumer.as_asgi()),
]
Related
Here is the Flask app
import jwt
from datetime import datetime, timedelta
from flask import Flask, request, jsonify, make_response
from flask_socketio import SocketIO, send
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from functools import wraps
app = Flask(__name__)
app.config['SECRET_KEY'] = 'myrandomsecretkey'
print(app.config['SECRET_KEY'])
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
def token_required(f):
#wraps(f)
def decorated(*args, **kwargs):
token = None
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
if not token:
return jsonify({'message': 'Token is missing!'}), 401
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
current_user = User.query.filter_by(public_id=data['public_id']).first()
except:
return jsonify({'message': 'Token is invalid!'}), 401
return f(current_user, *args, **kwargs)
return decorated
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
first_name = db.Column(db.String(30))
last_name = db.Column(db.String(40))
first_name = db.Column(db.String(30), nullable=False)
date_joined = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
password = db.Column(db.String(80))
def __repr__(self):
return f"User('{self.first_name}', '{self.last_name}')"
#app.route('/user_registration', methods=['POST'])
def create_user():
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user = User(first_name=data['first_name'],
last_name=data['last_name'], password=hashed_password, username=data['username'])
db.session.add(new_user)
db.session.commit()
return jsonify({'message': 'new_user_created'})
#app.route('/login', methods=['POST'])
def login():
auth = request.authorization
if not auth.password:
return make_response('Authentication credentials were not provided', 418)
user = User.query.filter_by(username=auth.username).first()
if not user:
return jsonify({'message': 'No user found'})
if check_password_hash(user.password, auth.password):
token = jwt.encode({'username': user.username, 'exp': datetime.utcnow() +
timedelta(minutes=30)}, app.config['SECRET_KEY'])
print(token)
return jsonify({'token': token})
return jsonify({'message': 'No user found'})
#app.route('/user', methods=['GET'])
#token_required
def get_all_users(current_user):
print(current_user)
if not current_user.admin:
return jsonify({'message': 'Cannot perform that function!'})
users = User.query.all()
output = []
for user in users:
user_data = {}
user_data['username'] = user.username
user_data['first_name'] = user.first_name
user_data['last_name'] = user.last_name
output.append(user_data)
return jsonify({'users': output})
After logging in I get the token and when I use in request in Postamn I put it in headers, the key is x-access-token and put the generated token as value but every this I get this error message
"message": "Token is invalid!"
I copied the the authorization part from a tutorial and they were decoding the token before returning it like this
return jsonify({'token' : token.decode('UTF-8')})
when I decode it it returns error saying that I can't decode a string.
This is the tutorial from which I got most parts
https://www.youtube.com/watch?v=WxGBoY5iNXY
So what's the poblem here?
I'm a newbie to channels and I made a chatroom application by following their official documentation. Now I'm trying to save the chat messages. All I know is I can create a model but Idk how to save it from the consumers.py into my database. I added username along with the message. A little help would be appreciated.
My Consumers.py:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
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
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
username = self.scope["user"].first_name
name = self.scope['user'].username
text_data_json = json.loads(text_data)
message = text_data_json['message']
message = (username + '(' + name + ')' + ':\n' + message)
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
async def chat_message(self, event):
username = self.scope["user"].username
message = event['message']
name = self.scope["user"].username
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
"username": username,
"name": name
}))
My model to save the msgs:
class Message(models.Model):
author = models.ForeignKey(User, related_name='messages', on_delete=models.CASCADE)
context = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
I tried to follow this tutorial as well:
https://www.youtube.com/watch?v=xrKKRRC518Y
For the users who still have this issue:
Since the WebSocket being used is AsyncWebsocketConsumer, we have to use database_sync_to_async. Why so? Because we do not want to leave open connections to the database.
This is how you will do it:
#database_sync_to_async
def create_chat(self, msg, sender):
Message.objects.create(sender=sender, msg=msg)
This is a simple example to demonstrate how to successfully use database_sync_to_async:
import json
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncWebsocketConsumer
from .models import Message
class ChatConsumer(AsyncWebsocketConsumer):
#database_sync_to_async
def create_chat(self, msg, sender):
return Message.objects.create(sender=sender, msg=msg)
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, close_code):
await self.channel_layer.group_discard(self.room_group_name, self.channel_name)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
sender = text_data_json['sender']
await self.channel_layer.group_send(self.room_group_name, {
'type': 'chat_message',
'message': message,
'sender': sender
})
async def chat_message(self, event):
message = event['message']
sender = event['sender']
new_msg = await self.create_chat(sender, message) # It is necessary to await creation of messages
await self.send(text_data=json.dumps({
'message': new_msg.message,
'sender': new_msg.sender
}))
What's wrong with just creating the message regularly?
message = Message.objects.create(context=message, author=self.scope['user'])
hello i learning about Django rest api, I am learning through someone else's code, but I don't know how to make Login.
my code :
model:
class User(models.Model):
class Meta:
db_table = "users"
created_at = models.DateTimeField(default = timezone.now)
updated_ay = models.DateTimeField(auto_now= True)
email = models.CharField(max_length = 128, unique= True)
password = models.CharField(max_length = 255)
active = models.BooleanField(default=False)
token = models.CharField(max_length= 255, null = True)
nickname = models.CharField(max_length = 255, null = True)
serializer:
class UserSerializer(serializers.ModelSerializer):
email = serializers.EmailField()
class Meta:
model = User
fields = '__all__'
def to_internal_value(self, data):
ret = super(UserSerializer, self).to_internal_value(data)
# cipher = AESSipher()
# ret['password'] = cipher.encrypt_str(ret['password'])
return ret
def to_representation(self, obj):
ret = super(UserSerializer, self).to_representation(obj)
print(ret)
return ret
def validate_email(self, value):
if User.objects.filter(email=value).exists():
raise serializers.ValidationError("Email already exists")
return value
def validate_password(self, value):
if len(value) < 8:
raise serializers.ValidationError("The password must be at least %s characters long. " % 8)
return value
def create(self, validate_data):
user = User.objects.create(
email = validate_data['email'],
password = validate_data['password'],
)
user.active = False
user.save()
message = render_to_string('user/account_activate_email.html', {
'user': user,
'domain' : 'localhost:8000',
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user)
})
mail_subject = 'sign up mail.'
to_email = 'mymail#gmail.com'
email = EmailMessage(mail_subject, message, to=[to_email])
email.send()
return validate_data
views:
class SignUp(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class UserActivate(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk = uid)
except(TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
try:
if user is not None and account_activation_token.check_token(user, token):
user.active = True
user.token = token
user.save()
return Response(user.email + 'email active', status=status.HTTP_200_OK)
else:
return Response('Expired Link', status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
print(traceback.format_exc())
Since I want to handle log-in status in the Mobile Client section,
I want to get only user model information when I log-in.
When Enter Email and Password in Client and I want to get user information.
How to make log-in? Do you know anyone?
Here is the link to the article: custom user authentication; it should help you
https://wsvincent.com/django-rest-framework-user-authentication-tutorial/
Hi you can use Django packages for this. for example you can use rest-auth package this package have all the things that you need for log in log out and reset password and all you need is that follow the rest auth documentation :
. https://pypi.org/project/django-rest-auth/
. https://django-rest-auth.readthedocs.io/en/latest/
i am new in django, i require login with user and password or facebook, i am using rest framework for api endpoints. ¿How i can do it?
i try with:
django-rest-framework-social-oauth2 but don't work for my because i needs save additional info from user after first enter.
I expect have 2 endpoint one sending user and password and another sending facebook auth token
Here's sample code for user login for Django Rest Framework:
class Login(APIView):
#csrf_exempt
def dispatch(self, *args, **kwargs):
return super(Login, self).dispatch(*args, **kwargs)
#staticmethod
def post(request):
request_data = JSONParser().parse(request)
if 'email' in request_data and 'password' in request_data:
try:
validate_email(request_data['email'])
except ValidationError:
return JsonResponse({'result': 'E-mail is invalid'}, status=400)
user = authenticate(email=request_data['email'], password=request_data['password'])
if user is not None:
if user.is_active:
try:
token = Token.objects.get(user=user)
except Token.DoesNotExist:
token = Token.objects.create(user=user)
return JsonResponse({'result': 'success', 'token': token.key, 'id': user.id}, status=200)
return JsonResponse({'result': 'E-mail or password is incorrect'}, status=400)
return JsonResponse({'result': 'E-mail or password is empty'}, status=400)
Here's sample for FB login code for Django Rest Framework (from my test project):
class FbLogin(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,)
#csrf_exempt
def dispatch(self, *args, **kwargs):
return super(FbLogin, self).dispatch(*args, **kwargs)
#staticmethod
def post(request):
request_data = JSONParser().parse(request)
if 'access_token' in request_data:
response = requests.get(
url='https://graph.facebook.com/v2.5/me/',
params={
'access_token': request_data['access_token'],
'fields': 'email,first_name,last_name',
},
)
json_response = json.loads(response.text)
if 'error' not in json_response:
response_photo = requests.get(
url='https://graph.facebook.com/v2.5/%s/picture' % json_response['id'],
params={
'redirect': 'false',
'type': 'large',
},
)
response_photo_json = json.loads(response_photo.text)
response_friends = requests.get(
url='https://graph.facebook.com/v2.5/me/friends/',
params={
'access_token': request_data['access_token'],
'limit': 300,
},
)
generated_password = get_random_string(10, '0123456789abcdefghijklmnopqrstuvwxyz')
try:
json_response_email = json_response['email']
except:
first_name = json_response['first_name'].lower()
last_name = json_response['last_name'].lower()
id = json_response['id']
json_response_email = first_name + last_name + id + '#facebook.com'
try:
current_user = User.objects.get(email=json_response_email)
current_user.set_password(generated_password)
current_user.save()
except User.DoesNotExist:
new_user = User.objects.create_user(email=json_response_email,
password=generated_password)
new_user.provider_id = json_response['id']
new_user.provider_type = 'facebook'
if 'first_name' in json_response:
new_user.first_name = json_response['first_name']
if 'last_name' in json_response:
new_user.last_name = json_response['last_name']
new_user.save()
photo_name = urlparse(response_photo_json['data']['url']).path.split('/')[-1].split('?')[-1]
photo_content = urllib.request.urlretrieve(response_photo_json['data']['url'])
new_user.profile_photo.save(photo_name, File(open(photo_content[0], 'rb')), save=True)
user = authenticate(email=json_response_email, password=generated_password)
try:
token = Token.objects.get(user=user)
except Token.DoesNotExist:
token = Token.objects.create(user=user)
if user is not None:
if user.is_active:
fullname = json_response['first_name'] + ' ' + json_response['last_name']
return JsonResponse({'result': 'success', 'token': token.key, 'name': fullname}, status=200)
return JsonResponse({'result': 'User access token is incorrect'}, status=400)
I have a view, It will send requests to http://127.0.0.1:80/o/token/
I want to know can I directly call the view /o/token/ and get the result ?
And do not need to import requests to send it
class GetAccessToken(APIView):
def post(self, request, *args, **kwargs):
msg ={}
return Response(msg, status=status.HTTP_200_OK)
def get_access_token(self, username, password, client_id, client_secret, scope="read write"):
url = "http://127.0.0.1:80/o/token/"
payload = {
'grant_type': 'password',
'username': username,
'password': password,
'scope': scope
}
auth = HTTPBasicAuth(client_id, client_secret)
try:
response = requests.post(url, auth=auth, data=payload, verify=False, timeout=TIMEOUT)
except Exception as err:
print err