Django - 如何在发送消息后显示用户的个人资料图片?
Django - How to display user's profile picture with a message after its sent?
我有一个实时聊天应用程序,我试图在 Javascript 发送消息后显示带有消息的个人资料图片。这是我的代码...
Models.py - 这是我的消息和个人资料模型
class Message(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
room = models.CharField(max_length = 255)
content = models.TextField()
date_added = models.DateTimeField(auto_now_add = True)
class Meta:
ordering = ('date_added', )
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.png', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
Consumers.py
class ChatRoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
print(self.room_group_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']
username = text_data_json['username']
room = text_data_json['room']
await self.save_message(username, room, message)
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chatroom_message',
'message': message,
'username': username,
}
)
async def chatroom_message(self, event):
message = event['message']
username = event['username']
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)
Message.objects.create(author = user, room = room, content = message)
pass
这里是实时聊天的 Javascript 代码...
{{ request.user.username|json_script:"user_username" }}
{{ room_name|json_script:"room-name" }}
<script>
const user_username = JSON.parse(document.getElementById('user_username').textContent);
document.querySelector('#submit').onclick = function (e) {
const messageInputDom = document.querySelector('#input');
const message = messageInputDom.value;
if(message.trim() == ''){
}
else {
chatSocket.send(JSON.stringify({
'message': message,
'username': user_username,
'room': roomName,
}));
messageInputDom.value = '';
}
};
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://' +
window.location.host +
'/ws/chat/' +
roomName +
'/'
);
chatSocket.onmessage = function (e) {
const data = JSON.parse(e.data);
console.log(data)
if (data.message) {
document.querySelector('#chat-text').innerHTML += ('<img id="imageid" src="{{ message.author.profile.image.url }}">' + data.username + '<br>' + data.message + '<br>');
}
else {
}
}
</script>
问题在于 Javascript 代码中的 <img id="imageid" src="{{ message.author.profile.image.url }}">
。所以我的问题是,如何在不刷新页面的情况下在发送消息后显示用户的个人资料图片?
您可以使用 Ajax 只刷新网页的一部分,使用不同的 HTML 文档,包含 Django HTML 来显示个人资料图片。
这里不需要使用AJAX,你是通过WebSockets与服务器通信,你可以通过它获取你需要的数据。
看起来您正在使用 django-channels。检查 django-channels documentation about authentication。如果启用它,您将能够通过 self.scope['user']
.
访问当前用户
因此,您将可以执行以下操作:
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = self.scope['user'].username
profile_pic = self.scope['user'].profile.image.url # None handling required
room = text_data_json['room']
await self.save_message(username, room, message)
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chatroom_message',
'message': message,
'username': username,
'profile_pic': profile_pic
}
)
async def chatroom_message(self, event):
message = event['message']
username = event['username']
profile_pic = event['profile_pic']
await self.send(text_data=json.dumps({
'message': message,
'username': username,
'profile_pic': profile_pic
}))
# on the frontend
chatSocket.onmessage = function (e) {
const data = JSON.parse(e.data);
console.log(data)
if (data.message) {
document.querySelector('#chat-text').innerHTML += (`<img id="imageid" src="${data.profile_pic}">` + data.username + '<br>' + data.message + '<br>');
}
else {
}
请注意,您不能从异步上下文进行数据库调用,因为数据库连接器以同步方式工作。
当您获取个人资料图片时 self.scope['user'].profile.image.url
您实际上是在调用数据库以获取用户的个人资料。
在这种情况下,您需要做的是使用 database_sync_to_async
或 sync_to_async
装饰器进行数据库调用。
例如:
from channels.db import database_sync_to_async
@database_sync_to_async
def get_user_profile(self):
return self.scope['user'].profile
# and in receive method you call it
async def receive(self, text_data):
profile = await self.get_user_profile()
profile_pic = profile.image.url
....
我有一个实时聊天应用程序,我试图在 Javascript 发送消息后显示带有消息的个人资料图片。这是我的代码...
Models.py - 这是我的消息和个人资料模型
class Message(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
room = models.CharField(max_length = 255)
content = models.TextField()
date_added = models.DateTimeField(auto_now_add = True)
class Meta:
ordering = ('date_added', )
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.png', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
Consumers.py
class ChatRoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
print(self.room_group_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']
username = text_data_json['username']
room = text_data_json['room']
await self.save_message(username, room, message)
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chatroom_message',
'message': message,
'username': username,
}
)
async def chatroom_message(self, event):
message = event['message']
username = event['username']
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)
Message.objects.create(author = user, room = room, content = message)
pass
这里是实时聊天的 Javascript 代码...
{{ request.user.username|json_script:"user_username" }}
{{ room_name|json_script:"room-name" }}
<script>
const user_username = JSON.parse(document.getElementById('user_username').textContent);
document.querySelector('#submit').onclick = function (e) {
const messageInputDom = document.querySelector('#input');
const message = messageInputDom.value;
if(message.trim() == ''){
}
else {
chatSocket.send(JSON.stringify({
'message': message,
'username': user_username,
'room': roomName,
}));
messageInputDom.value = '';
}
};
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://' +
window.location.host +
'/ws/chat/' +
roomName +
'/'
);
chatSocket.onmessage = function (e) {
const data = JSON.parse(e.data);
console.log(data)
if (data.message) {
document.querySelector('#chat-text').innerHTML += ('<img id="imageid" src="{{ message.author.profile.image.url }}">' + data.username + '<br>' + data.message + '<br>');
}
else {
}
}
</script>
问题在于 Javascript 代码中的 <img id="imageid" src="{{ message.author.profile.image.url }}">
。所以我的问题是,如何在不刷新页面的情况下在发送消息后显示用户的个人资料图片?
您可以使用 Ajax 只刷新网页的一部分,使用不同的 HTML 文档,包含 Django HTML 来显示个人资料图片。
这里不需要使用AJAX,你是通过WebSockets与服务器通信,你可以通过它获取你需要的数据。
看起来您正在使用 django-channels。检查 django-channels documentation about authentication。如果启用它,您将能够通过 self.scope['user']
.
因此,您将可以执行以下操作:
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = self.scope['user'].username
profile_pic = self.scope['user'].profile.image.url # None handling required
room = text_data_json['room']
await self.save_message(username, room, message)
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chatroom_message',
'message': message,
'username': username,
'profile_pic': profile_pic
}
)
async def chatroom_message(self, event):
message = event['message']
username = event['username']
profile_pic = event['profile_pic']
await self.send(text_data=json.dumps({
'message': message,
'username': username,
'profile_pic': profile_pic
}))
# on the frontend
chatSocket.onmessage = function (e) {
const data = JSON.parse(e.data);
console.log(data)
if (data.message) {
document.querySelector('#chat-text').innerHTML += (`<img id="imageid" src="${data.profile_pic}">` + data.username + '<br>' + data.message + '<br>');
}
else {
}
请注意,您不能从异步上下文进行数据库调用,因为数据库连接器以同步方式工作。
当您获取个人资料图片时 self.scope['user'].profile.image.url
您实际上是在调用数据库以获取用户的个人资料。
在这种情况下,您需要做的是使用 database_sync_to_async
或 sync_to_async
装饰器进行数据库调用。
例如:
from channels.db import database_sync_to_async
@database_sync_to_async
def get_user_profile(self):
return self.scope['user'].profile
# and in receive method you call it
async def receive(self, text_data):
profile = await self.get_user_profile()
profile_pic = profile.image.url
....