I currently have the following template,
{% extends "123/123-base.html" %}
{% block main %}
<script type="text/javascript">
$(document).ready(function() {
$("#button").click(function() {
var host = $("#hostinput").val();
var record = $("#recordinput").val();
$.ajax({
url : "/lookup_ajax",
type : "POST",
dataType: "json",
data : {
hostinput : host,
recordinput : record,
csrfmiddlewaretoken: '{{ csrf_token }}'
},
success : function(json) {
$('#mainsection').append( "response" + json.response );
},
error : function(xhr,errmsg,err) {
alert(xhr.status + ": " + xhr.responseText);
}
});
return false;
});
});
</script>
<div id="mainsection">
<div id="maininput" class="input-append">
<form method="post" name="inputlookup" action="/lookup_ajax">
{% csrf_token %}
<input class="span2" id="hostinput" name="hostinput" type="text">
<select title="Record" id="recordinput" name="recordinput" >
<option value="A">A</option>
<option value="MX">MX</option>
<option value="Cname">Cname</option>
</select>
<button id="button" class="btn" type="submit">Lookup</button>
</form>
</div>
<div id="mainouput">
</div>
</div>
{% endblock %}
However the response I receieve back from the server I want to loop over using the Django template tags, like this,
{% extends "123/123-base.html" %}
{% block main %}
{% if error %}
{{ error }}
{% else %}
<ul>
<table class="table table-style table-striped">
<thead>
<tr>
<th>HOSTNAME</th>
<th>TTL</th>
<th>CLASS</th>
<th>TYPE</th>
<th>DETAILS</th>
</tr>
</thead>
{% for answer in response %}
<tr>
{% for field in answer %}
<td>{{ field }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</ul>
{% endif %}
{% endblock %}
Any ideas on how this should be done ?
Thanks,
It looks to me like you're returning a JSON object for your AJAX call. If that's correct then django doesn't come into it for displaying the result. You'd need to change your success function to something like the following:
success: function(json){
// Table header
var table = $('<table>').addClass('table table-style table-striped');
var thead = $('<thead>');
var headrow = $('<tr>');
var head1 = $('<th>').text('HOSTNAME');
var head2 = $('<th>').text('TTL');
var head3 = $('<th>').text('CLASS');
var head4 = $('<th>').text('TYPE');
var head5 = $('<th>').text('DETAILS');
$(headrow).append(head1, head2, head3, head4, head5);
$(thead).append(headrow);
$(table).append(thead);
// table body
var tbody = $('<tbody>');
num_answers = json.length
for (i = 0; i < num_answers; i++) {
var row = $('<tr>');
var cell1 = $('<td>').text(json[i][0]);
var cell2 = $('<td>').text(json[i][1]);
var cell3 = $('<td>').text(json[i][2]);
var cell4 = $('<td>').text(json[i][3]);
var cell5 = $('<td>').text(json[i][4]);
$(row).append(cell1, cell2, cell3, cell4, cell5);
$(tbody).append(row);
}
$(table).append(row);
$('#mainsection').append(table);
}
Related
Have a good day!
I don't know much about Flask
I try to depict insert several (two) objects on the page and embed add the ability (so that you can) update these objects.
There is a main element on the page - the choice of a date range.
When you select a start date and an end date and click the button in the same way (two objects should be updated).
The first object is a table. The table (object) is updated.
The second object is a graph. This object is not updated.
What can be done to update the second object (chart)?
I would be extremely grateful for any help.
It's HTML
<div class="row">
<div class="col-md-3">
<input type="text" name="From" id="From" class="form-control" placeholder="From Date"/>
</div>
<div class="col-md-3">
<input type="text" name="to" id="to" class="form-control" placeholder="To Date"/>
</div>
<div class="col-md-6">
<input type="button" name="range" id="range" value="Range" class="btn btn-success"/>
</div>
</div>
<div id="purchase_order"></div>
<hr>
<div class="row" style="align-content: center">
<div class="text" style="align-content: center">
</div>
<div class="outer-wrapper" style="align-content: center">
<div class="table-wrapper" id="table-wrapper" style="align-content: center">
<table>
<thead>
{% for col in column_names %}
<th>{{col}}</th>
{% endfor %}
</thead>
<tbody>
{% for row in row_data %}
<tr>
{% for cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-md-1">
</div>
<div class="col-md-10">
<div>
<canvas id="myChart" width="800px" style="align-content: center"></canvas>
</div>
</div>
<div class="col-md-1">
</div>
</div>
it's script
<script>
$(document).ready(function (){
$.datepicker.setDefaults({
dateFormat: 'yy-mm-dd'
});
$(function (){
$("#From").datepicker();
$("#to").datepicker();
});
$('#range').click(function (){
var From = $('#From').val();
var to = $('#to').val();
if (From != '' && to != '')
{
$.ajax({
url:"/range",
method:"POST",
data:{From:From, to:to},
success:function (data)
{
$('#table-wrapper').html(data);
$('#table-wrapper').append(data.htmlresponse);
}
});
$.ajax({
url:"/range2",
method:"POST",
data:{From:From, to:to},
success:function (data)
{
$('#myChart').html(data);
$('#myChart').append(data.htmlresponse2);
}
});
}
else
{
alert("Please Select the Date")
}
});
});
</script>
it's Flask code
#app.route('/', methods=['GET','POST'])
#app.route('/index')
def home_page(): # put application's code here
if request.method=="POST":
upload_excel=request.files['upload_excel']
if upload_excel.filename != '':
filepath=os.path.join(app.config["UPLOAD_FOLDER"],upload_excel.filename)
upload_excel.save(filepath)
data=pd.read_excel(upload_excel)
data.to_sql('kotel', con=db.engine, if_exists="append", index=False)
return print(data)
df = pd.read_sql('select * from kotel', con=db.engine)
df['date'] = df['date'].dt.round('2min')
y_data = df['tnv'].tolist()
x_data = df['date'].tolist()
print(y_data)
type(y_data)
print(x_data)
df_graph = df.copy()
df_graph.drop(df_graph.columns[[0, 1, 2]], axis=1, inplace=True)
print(df_graph)
# df['date'] = pd.to_datetime(df['date'], format="%Y.%d.%B %H")
return render_template('index new.html', column_names=df.columns.values, row_data=list(df.values.tolist()), column_names_graph=df_graph.columns.values, os_y = y_data, os_x = x_data)
#app.route("/range", methods=["POST","GET"])
def range():
if request.method == 'POST':
From = request.form['From']
to = request.form['to']
df = pd.read_sql('select * from kotel', con=db.engine)
df['date'] = pd.to_datetime(df['date'])
df = df.loc[(df['date'] >= From) & (df['date'] <= to)]
df['date'] = df['date'].dt.round('2min')
return jsonify({'htmlresponse': render_template('response.html', column_names=df.columns.values, row_data=list(df.values.tolist()))}), df
#app.route("/range2", methods=["POST","GET"])
def range2():
df_new = pd.read_sql('select * from table_1', con=db.engine)
y_data = df_new['temper'].tolist()
x_data = df_new['rashod'].tolist()
return jsonify({'htmlresponse2': render_template('response2.html', os_y = y_data, os_x = x_data)})
it's extended html 1 add
<table>
<thead>
{% for col in column_names %}
<th>{{col}}</th>
{% endfor %}
</thead>
<tbody>
{% for row in row_data %}
<tr>
{% for cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
it's extended html 2 add
<script>
const labels = [{% for item in os_x %}
"{{ item }}",
{% endfor %}];
const data = {
labels: labels,
datasets: [{
label: 'My First dataset',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: [{% for item in os_y %}
{{ item }},
{% endfor %}],
}]
};
const config = {
type: 'line',
data: data,
options: {}
};
</script>
<script>
const myChart = new Chart(
document.getElementById('myChart'),
config
);
</script>
I would really like to be able (so that I can) take a value variable from a form field (dropdown ) and send it to the Flask code so that I can use it in the updated page.
image screen
image screen
I am trying to get the specific pk of the selected object when the user accepts a delivery. My problem is that I'm getting only the first object's pk in the list every time. I want to get the pk of the selected object.
views:
#login_required(login_url="/signin/?next=/driver/")
def deliveries_available_page(request):
deliveries = Delivery.objects.filter(
status_of_delivery__in=[Delivery.DELIVERY_POSTED]
)
#When driver accept delivery then status of delivery changes to delivering
if request.method == 'POST':
delivery = get_object_or_404(Delivery, pk=request.POST.get('receipt_number'))
if delivery:
delivery.status_of_delivery = Delivery.DELIVERY_DELIVERING
delivery.driver = request.user.driver
messages.success(request, 'Delivery Accepted')
delivery.save()
return redirect(reverse('driver:deliveries_available'))
return render(request, 'driver/deliveries_available.html', {
"GOOGLE_API_MAP": settings.GOOGLE_API_MAP,
"del": deliveries
})
HTML:
<div class="d-flex flex-column h-100" style="padding-bottom: 50px">
<div id="map"></div>
{% if del %}
{% for d in del %}
<div class="card" id="delivery-popup">
<div class="card-body p-2">
<div class="details">
<div class="p-2">
<strong id="address"></strong>
<div>
<strong id="show-info"></strong>
</div>
<div>
<strong id="show-distance"></strong>
<strong id="show-duration"></strong>
</div>
<div>
<strong id="show-price"></strong>
<strong id="show-id"></strong>
</div>
<div>
<form method="POST">
{% csrf_token %}
<button type="submit" class="btn btn-primary" name="accept">Accept</button>
<input type="hidden" value="{{ d.receipt_number }}" name="receipt_number">
</form>
</div>
{% if messages %}
{% for m in messages %}
{% if m.tags %}
<script>alert("{{ m }}")</script>
{% endif %}
{% endfor %}
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
I need the specific pk so when user accept delivery, then the right delivery is accepted and removed from the map. Any help would be appreciated. Thanks.
I can get the specific pk by creating new url and passing it in the url but i want user to accept delivery on the map page.
script added to html
<script>
//Google map and using api to display deliveries on map and click event to show delivery details
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 53.350140, lng: -6.266155 },
zoom: 13,
});
fetch("{% url 'driver:available_deliveries' %}").then(response => response.json()).then(json => {
console.log(json);
for (let d = 0; d < json.deliveries.length; d++) {
const delivery = json.deliveries[d];
const position = { lat: delivery.delivery_address_latitude, lng: delivery.delivery_address_longitude };
const show_on_map = new google.maps.Marker
({
position,
map,
});
new google.maps.InfoWindow({
content: "<small><strong>" + delivery.address + "<medium><br>€" + delivery.price
}).open(map, show_on_map);
show_on_map.addListener("click", () => {
PopUpDelivery(delivery);
})
}
})
}
function PopUpDelivery(delivery) {
$("#delivery-popup").css("display", "block");
$("#address").html(delivery.address);
$("#show-distance").html(delivery.distance + " Km");
$("#show-duration").html(delivery.duration + " Mins");
$("#show-price").html("€ " + delivery.price);
$("#show-info").html("Info : " + delivery.information);
$("#show-id").html("Pk : " + delivery.receipt_number)
}
Keep id unique in html document
The id in an HTML document must be unique within each page source:
The id global attribute defines an identifier (ID) which must be
unique in the whole document. Its purpose is to identify the element
when linking (using a fragment identifier), scripting, or styling
(with CSS).
What I think may have occurred is that since you have many cards in each page, all with the same id's, you were just getting the first one. While your solution works, I think it might be better to give each id a unique value, which you can do simply by appending the receipt_number, or any unique field to the id names. With this, you may not need the function getTag. Here is what I mean:
html
<div class="d-flex flex-column h-100" style="padding-bottom: 50px">
<div id="map"></div>
{% if del %}
{% for d in del %}
<div class="card" id="delivery-popup{{ d.receipt_number }}">
<div class="card-body p-2">
<div class="details">
<div class="p-2">
<strong id="address{{ d.receipt_number }}"></strong>
<div>
<strong id="show-info{{ d.receipt_number }}"></strong>
</div>
<div>
<strong id="show-distance{{ d.receipt_number }}"></strong>
<strong id="show-duration{{ d.receipt_number }}"></strong>
</div>
<div>
<strong id="show-price{{ d.receipt_number }}"></strong>
<strong id="show-id{{ d.receipt_number }}"></strong>
</div>
<div>
<form method="POST">
{% csrf_token %}
<button type="submit" class="btn btn-primary" name="accept">Accept</button>
<input type="hidden" value="{{ d.receipt_number }}" name="receipt_number">
</form>
</div>
{% if messages %}
{% for m in messages %}
{% if m.tags %}
<script>alert("{{ m }}")</script>
{% endif %}
{% endfor %}
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
script
<script>
//Google map and using api to display deliveries on map and click event to show delivery details
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 53.350140, lng: -6.266155 },
zoom: 13,
});
fetch("{% url 'driver:available_deliveries' %}").then(response => response.json()).then(json => {
console.log(json);
for (let d = 0; d < json.deliveries.length; d++) {
const delivery = json.deliveries[d];
const position = { lat: delivery.delivery_address_latitude, lng: delivery.delivery_address_longitude };
const show_on_map = new google.maps.Marker
({
position,
map,
});
new google.maps.InfoWindow({
content: "<small><strong>" + delivery.address + "<medium><br>€" + delivery.price
}).open(map, show_on_map);
show_on_map.addListener("click", () => {
PopUpDelivery(delivery);
})
}
})
}
function PopUpDelivery(delivery) {
let pk = delivery.receipt_number
$("#delivery-popup"+pk).css("display", "block");
$("#address"+pk).html(delivery.address);
$("#show-distance"+pk).html(delivery.distance + " Km");
$("#show-duration"+pk).html(delivery.duration + " Mins");
$("#show-price"+pk).html("€ " + delivery.price);
$("#show-info"+pk).html("Info : " + delivery.information);
$("#show-id"+pk).html("Pk : " + delivery.receipt_number)
}
Solved:
Got the specific pk, when i click on a delivery it gets the right pk, code below if anyone has the same problem:
script
function PopUpDelivery(delivery) {
$("#delivery-popup").css("display", "block");
$("#address").html(delivery.address);
$("#show-distance").html(delivery.distance + " Km");
$("#show-duration").html(delivery.duration + " Mins");
$("#show-price").html("€ " + delivery.price);
$("#show-info").html("Info : " + delivery.information);
$("#show-id").html("Pk : " + delivery.receipt_number);
var input_tag = getTag('getpk');
input_tag.value = delivery.receipt_number;
}
function getTag(id){
return document.getElementById(id);
}
html
<button type="submit" class="btn btn-primary" name="accept">Accept</button>
<input type="hidden" id="getpk" value="" name="receipt_number">
It seems that my request.session isn't saved or is somehow modified in other ways, despite using request.session.modified = True after changing the value.
Here is the debug output:
This should run first.
This should run second.
I have set main category to 2 (Shoes)
Main category is 1 (Books)
The code below should display a different ModelForm based on selected category, however when I select, for instance, Books, ShoesModelForm is displayed and vice-versa. This currently works as follows: whenever a value in a combobox is changed, two AJAX requests are fired. First one calls a view (load_categories) that renders another if a category has children, otherwise returns an HttpResponse which stops attaching the listener. Second one calls a view (load_modelform) that renders a specific ModelForm if selected category is root node (main category).
mappings = {
'1': BookProductForm,
'2': ShoesProductForm
}
def load_categories(request):
print("This should run first.")
category_id = request.GET.get('category')
request.session['category'] = category_id
request.session.modified = True
if Category.objects.get(id=category_id).is_root_node():
request.session['main_category'] = category_id
request.session.modified = True
print(f"I have set main category to {request.session['main_category']} ({Category.objects.get(id=category_id).name})")
subcategories = Category.objects.get(id=category_id).get_children()
if subcategories:
return render(request, 'products/category_dropdown_list_options.html', {'subcategories': subcategories})
return HttpResponse('leaf_node')
def load_modelform(request):
print("This should run second.")
main_category = request.session['main_category']
print(f"Main category is {main_category} ({Category.objects.get(id=main_category).name})")
form = mappings[main_category]
return render(request, 'products/category_modelform.html', {'form': form})
#login_required
def product_create_view(request):
if request.method == 'POST':
category = request.session.get('main_category')
create_product_form = mappings[category](request.POST)
if create_product_form.is_valid():
create_product_form.save()
return render(request, 'products/product_create.html', {
'categories': Category.objects.filter(parent=None)
})
What might be going on here?
#EDIT:
I'm including my template with jQuery code that contains Ajax requests:
{% extends 'pages/base.html' %}
{% load static %}
{% block cssfiles %}
<link rel="stylesheet" type="text/css" href="{% static 'products/css/create.css' %}">
{% endblock %}
{% block content %}
<h1>Create a product</h1>
<div class='modelform'>
<form method='POST' id='productForm' data-products-url="{% url 'products:ajax_load_categories' %}" data-modelform-url="{% url 'products:ajax_load_modelform' %}">
{% csrf_token %}
<div class='categories'>
<label>Category</label>
<select>
{% for category in categories %}
<option value="{{ category.pk }}">{{ category.name }}</option>
{% endfor %}
</select>
</div>
<div class='the-rest'>
{{ form.non_field_errors }}
{% for field in form %}
{% ifnotequal field.name 'category' %}
{{ field.label_tag }} {{ field }}
{{ field.errors }}
{% endifnotequal %}
{% endfor %}
</div>
<input type="submit" name="" value="Submit">
</form>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
var $r_ = function() {
var url = $("#productForm").attr("data-products-url");
var categoryId = $(this).val();
var toRemove = $(this).nextAll('select');
$.ajax({
url: url,
data: {
'category': categoryId
},
success: function (data) {
if (data != 'leaf_node') {
toRemove.remove();
$(".categories").append(data);
}
else {
toRemove.remove();
}
}
});
var url2 = $('#productForm').attr('data-modelform-url');
setTimeout(function() {
$.ajax({
url: url2,
data: {
'category': categoryId
},
success: function (data) {
if (data != 'dont_change_modelform') {
$('.the-rest').empty();
$('.the-rest').append(data);
}
}
});
}, 100);
}
$(document).on('change', 'select', $r_);
</script>
{% endblock %}
This is the code of my base.html
{% comment %}
As the developer of this package, don't place anything here if you can help it
since this allows developers to have interoperability between your template
structure and their own.
Example: Developer melding the 2SoD pattern to fit inside with another pattern::
{% extends "base.html" %}
{% load static %}
<!-- Their site uses old school block layout -->
{% block extra_js %}
<!-- Your package using 2SoD block layout -->
{% block javascript %}
<script src="{% static 'js/ninja.js' %}" type="text/javascript"></script>
{% endblock javascript %}
{% endblock extra_js %}{% endcomment %}
This is code of dialogs.html
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block css %}
{{ block.super }}
<link href="{% static 'django_private_chat/css/django_private_chat.css' %}" rel="stylesheet" type="text/css" media="all">
{% endblock css %}
{% block content %}
<input id="owner_username" type="hidden" value="{{ request.user.username }}">
<div class="container">
<div class="col-md-3">
<div class="user-list-div">
<ul>
{% for dialog in object_list %}
<li>
{% if dialog.owner == request.user %}
{% with dialog.opponent.username as username %}
<a href="{% url 'dialogs_detail' username %}" id="user-{{ username }}"
class="btn btn-danger">{% trans "Chat with" %} {{ username }}</a>
{% endwith %}
{% else %}
{% with dialog.owner.username as username %}
<a href="{% url 'dialogs_detail' username %}" id="user-{{ username }}"
class="btn btn-danger">{% trans "Chat with" %} {{ username }}</a>
{% endwith %}
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-3 col-md-offset-9">
<span class="pull-right" hidden id="typing-text">
<strong>{{ opponent_username }} {% trans "is typing..." %}</strong>
</span>
</div>
<p>
{{ opponent_username }}
</p>
<p class="text-success" id="online-status" style="display: none">{% trans "Online" %}</p>
<p class="text-danger" id="offline-status" style="display: none">{% trans "Offline" %}</p>
<div class="messages-container">
<div id="messages" class="messages">
{% for msg in active_dialog.messages.all %}
<div class="row {% if msg.read %}msg-read{% else %}msg-unread{% endif %}"
data-id="{{ msg.id }}">
<p class="{% if msg.sender == request.user %}pull-left{% else %}pull-right{% endif %}">
<span class="username">{{ msg.sender.username }}:</span>
{{ msg.text }}
<span class="timestamp">– <span
data-livestamp="{{ msg.get_formatted_create_datetime }}">{{ msg.get_formatted_create_datetime }}</span></span>
</p>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="row">
<div class="add-message">
<div class="form-group">
<textarea id="chat-message" class="form-control message"
placeholder="{% trans 'Write a message' %}"></textarea>
</div>
<div class="form-group clearfix">
<input id="btn-send-message" type="submit" class="btn btn-primary pull-right send-message"
style="margin-left: 10px;" value="{% trans 'Send' %}"/>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/scrollmonitor/1.2.0/scrollMonitor.js"
integrity="sha256-BseZlDlA+yL4qu+Voi82iFa5aaifralQEXIjOjaXgeo=" crossorigin="anonymous"></script>
<script>
var base_ws_server_path = "{{ ws_server_path }}";
$(document).ready(function () {
var websocket = null;
var monitor = null;
function initReadMessageHandler(containerMonitor, elem) {
var id = $(elem).data('id');
var elementWatcher = containerMonitor.create(elem);
elementWatcher.enterViewport(function () {
var opponent_username = getOpponnentUsername();
var packet = JSON.stringify({
type: 'read_message',
session_key: '{{ request.session.session_key }}',
username: opponent_username,
message_id: id
});
$(elem).removeClass('msg-unread').addClass('msg-read');
websocket.send(packet);
});
}
function initScrollMonitor() {
var containerElement = $("#messages");
var containerMonitor = scrollMonitor.createContainer(containerElement);
$('.msg-unread').each(function (i, elem) {
if ($(elem).hasClass('opponent')){
initReadMessageHandler(containerMonitor, elem);
}
});
return containerMonitor
}
function getOpponnentUsername() {
return "{{ opponent_username }}";
}
// TODO: Use for adding new dialog
function addNewUser(packet) {
$('#user-list').html('');
packet.value.forEach(function (userInfo) {
if (userInfo.username == getUsername()) return;
var tmpl = Handlebars.compile($('#user-list-item-template').html());
$('#user-list').append(tmpl(userInfo))
});
}
function addNewMessage(packet) {
var msg_class = "";
if (packet['sender_name'] == $("#owner_username").val()) {
msg_class = "pull-left";
} else {
msg_class = "pull-right";
}
var msgElem =
$('<div class="row msg-unread" data-id="' + packet.message_id + '">' +
'<p class="' + msg_class + '">' +
'<span class="username">' + packet['sender_name'] + ': </span>' +
packet['message'] +
' <span class="timestamp">– <span data-livestamp="' + packet['created'] + '"> ' + packet['created'] + '</span></span> ' +
'</p> ' +
'</div>');
$('#messages').append(msgElem);
scrollToLastMessage()
}
function scrollToLastMessage() {
var $msgs = $('#messages');
$msgs.animate({"scrollTop": $msgs.prop('scrollHeight')})
}
function generateMessage(context) {
var tmpl = Handlebars.compile($('#chat-message-template').html());
return tmpl({msg: context})
}
function setUserOnlineOffline(username, online) {
var elem = $("#user-" + username);
if (online) {
elem.attr("class", "btn btn-success");
} else {
elem.attr("class", "btn btn-danger");
}
}
function gone_online() {
$("#offline-status").hide();
$("#online-status").show();
}
function gone_offline() {
$("#online-status").hide();
$("#offline-status").show();
}
function flash_user_button(username) {
var btn = $("#user-" + username);
btn.fadeTo(700, 0.1, function () {
$(this).fadeTo(800, 1.0);
});
}
function setupChatWebSocket() {
var opponent_username = getOpponnentUsername();
websocket = new WebSocket(base_ws_server_path + '{{ request.session.session_key }}/' + opponent_username);
websocket.onopen = function (event) {
var opponent_username = getOpponnentUsername();
var onOnlineCheckPacket = JSON.stringify({
type: "check-online",
session_key: '{{ request.session.session_key }}',
username: opponent_username
});
var onConnectPacket = JSON.stringify({
type: "online",
session_key: '{{ request.session.session_key }}'
});
console.log('connected, sending:', onConnectPacket);
websocket.send(onConnectPacket);
console.log('checking online opponents with:', onOnlineCheckPacket);
websocket.send(onOnlineCheckPacket);
monitor = initScrollMonitor();
};
window.onbeforeunload = function () {
var onClosePacket = JSON.stringify({
type: "offline",
session_key: '{{ request.session.session_key }}',
username: opponent_username,
});
console.log('unloading, sending:', onClosePacket);
websocket.send(onClosePacket);
websocket.close();
};
websocket.onmessage = function (event) {
var packet;
try {
packet = JSON.parse(event.data);
console.log(packet)
} catch (e) {
console.log(e);
}
switch (packet.type) {
case "new-dialog":
// TODO: add new dialog to dialog_list
break;
case "user-not-found":
// TODO: dispay some kind of an error that the user is not found
break;
case "gone-online":
if (packet.usernames.indexOf(opponent_username) != -1) {
gone_online();
} else {
gone_offline();
}
for (var i = 0; i < packet.usernames.length; ++i) {
setUserOnlineOffline(packet.usernames[i], true);
}
break;
case "gone-offline":
if (packet.username == opponent_username) {
gone_offline();
}
setUserOnlineOffline(packet.username, false);
break;
case "new-message":
if (packet['sender_name'] == opponent_username || packet['sender_name'] == $("#owner_username").val()) {
addNewMessage(packet);
if (packet['sender_name'] == opponent_username) {
initReadMessageHandler(monitor, $("div[data-id='" + packet['message_id'] + "']"));
}
} else {
flash_user_button(packet['sender_name']);
}
break;
case "opponent-typing":
var typing_elem = $('#typing-text');
if (!typing_elem.is(":visible")) {
typing_elem.fadeIn(500);
} else {
typing_elem.stop(true);
typing_elem.fadeIn(0);
}
typing_elem.fadeOut(3000);
break;
case "opponent-read-message":
if (packet['username'] == opponent_username) {
$("div[data-id='" + packet['message_id'] + "']").removeClass('msg-unread').addClass('msg-read');
}
break;
default:
console.log('error: ', event)
}
}
}
function sendMessage(message) {
var opponent_username = getOpponnentUsername();
var newMessagePacket = JSON.stringify({
type: 'new-message',
session_key: '{{ request.session.session_key }}',
username: opponent_username,
message: message
});
websocket.send(newMessagePacket)
}
$('#chat-message').keypress(function (e) {
if (e.which == 13 && this.value) {
sendMessage(this.value);
this.value = "";
return false
} else {
var opponent_username = getOpponnentUsername();
var packet = JSON.stringify({
type: 'is-typing',
session_key: '{{ request.session.session_key }}',
username: opponent_username,
typing: true
});
websocket.send(packet);
}
});
$('#btn-send-message').click(function (e) {
var $chatInput = $('#chat-message');
var msg = $chatInput.val();
if (!msg) return;
sendMessage($chatInput.val());
$chatInput.val('')
});
setupChatWebSocket();
scrollToLastMessage();
});
</script>
{% endblock %}
You should copy both 'dialogs.html' and 'base.html' from:
\your_virtualenvs\your_project\Lib\site-packages\django_private_chat\templates\django_private_chat\
into:
\your_project\templates\django_private_chat\
in order to modify them accordingly.
And add this to your settings if you din't already:
TEMPLATES = [
{
...
'DIRS': [ os.path.join( BASE_DIR, 'templates' ) ],
...
}
]
Reference:
https://django-private-chat.readthedocs.io/en/latest/readme.html#example-project
I'm working on a project in which I need to implement private chat for user, so one user can communicate with another user privately, for that purpose I'm using django-private-chat but I'm confused in how to implement this in my application?
Here's what I have done so far:
1): Install websockets
2): Install django-private-chat
3): Add it in INSTALLED_APPS
4): Then add in main urls.py like:
From project.urls.py:
path('', include('jobexpertapp.urls')),
url(r'', include('django_private_chat.urls')),
From myapp.urls.py:
re_path(r'^users/$', views.UserListView.as_view(), name='user_list'),
5): Add a view for that:
class UserListView(LoginRequiredMixin, generic.ListView):
model = get_user_model()
# These next two lines tell the view to index lookups by username
slug_field = 'username'
slug_url_kwarg = 'username'
template_name = 'jobexpertapp/users.html'
login_url = '/login'
6): Then add two templates: (a): users.html (b): dialogs.html
From users.html:
{% extends "base.html" %}
{% block content %}
<h3>Here you can see users avaliable for chat, excluding yourself (chat with yourself is possible thought):</h3>
<div class="container">
{% for user in object_list %}
{% if user != request.user %}
<p>{{ user.get_full_name }}</p>
<a href="{% url 'dialogs_detail' user.username %}" class="btn btn-primary">
Chat with {{ user.username }}
</a>
{% endif %}
{% endfor %}
</div>
{% endblock %}
From dialogs.html:
{# This template is here just to demonstrate how to customize the default one. #}
{# It has none to very few changes #}
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block css %}
{% endblock css %}
{% block content %}
<input id="owner_username" type="hidden" value="{{ request.user.username }}">
<div class="container">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans "Chat list" %}</h3>
</div>
<div class="panel-body">
<div class="user-list-div">
<ul style="list-style-type: none;">
{% for dialog in object_list %}
<li>
{% if dialog.owner == request.user %}
{% with dialog.opponent.username as username %}
<a href="{% url 'dialogs_detail' username %}" id="user-{{ username }}"
class="btn btn-danger">{% trans "Chat with" %} {{ username }}</a>
{% endwith %}
{% else %}
{% with dialog.owner.username as username %}
<a href="{% url 'dialogs_detail' username %}" id="user-{{ username }}"
class="btn btn-danger">{% trans "Chat with" %} {{ username }}</a>
{% endwith %}
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
<div class="col-md-9">
<div class="well well-lg">
<div class="row">
<div class="col-md-3 col-md-offset-9">
<span class="pull-right" hidden id="typing-text">
<strong>{{ opponent_username }} {% trans "is typing..." %}</strong>
</span>
</div>
<p>
{{ opponent_username }}
</p>
<p class="text-success" id="online-status" style="display: none">{% trans "Online" %}</p>
<p class="text-danger" id="offline-status" style="display: none">{% trans "Offline" %}</p>
<div class="messages-container">
<div id="messages" class="messages">
{% for msg in active_dialog.messages.all %}
<div class="row {% if msg.read %}msg-read{% else %}msg-unread{% endif %} {% if msg.sender != request.user %}opponent{% endif %}"
data-id="{{ msg.id }}">
<p class="{% if msg.sender == request.user %}pull-left{% else %}pull-right{% endif %}">
<span class="username">{{ msg.sender.username }}:</span>
{{ msg.text }}
<span class="timestamp">– <span
data-livestamp="{{ msg.get_formatted_create_datetime }}">{{ msg.get_formatted_create_datetime }}</span></span>
</p>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="row">
<div class="add-message">
<div class="form-group">
<textarea id="chat-message" class="form-control message"
placeholder="{% trans 'Write a message' %}"></textarea>
</div>
<div class="form-group clearfix">
<input id="btn-send-message" type="submit" class="btn btn-primary pull-right send-message"
style="margin-left: 10px;" value="{% trans 'Send' %}"/>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/scrollmonitor/1.2.0/scrollMonitor.js"
integrity="sha256-BseZlDlA+yL4qu+Voi82iFa5aaifralQEXIjOjaXgeo=" crossorigin="anonymous"></script>
<script>
var base_ws_server_path = "{{ ws_server_path }}";
$(document).ready(function () {
var websocket = null;
var monitor = null;
function initReadMessageHandler(containerMonitor, elem) {
var id = $(elem).data('id');
var elementWatcher = containerMonitor.create(elem);
elementWatcher.enterViewport(function () {
var opponent_username = getOpponnentUsername();
var packet = JSON.stringify({
type: 'read_message',
session_key: '{{ request.session.session_key }}',
username: opponent_username,
message_id: id
});
$(elem).removeClass('msg-unread').addClass('msg-read');
websocket.send(packet);
});
}
function initScrollMonitor() {
var containerElement = $("#messages");
var containerMonitor = scrollMonitor.createContainer(containerElement);
$('.msg-unread').each(function (i, elem) {
if ($(elem).hasClass('opponent')){
initReadMessageHandler(containerMonitor, elem);
}
});
return containerMonitor
}
function getOpponnentUsername() {
return "{{ opponent_username }}";
}
// TODO: Use for adding new dialog
function addNewUser(packet) {
$('#user-list').html('');
packet.value.forEach(function (userInfo) {
if (userInfo.username == getUsername()) return;
var tmpl = Handlebars.compile($('#user-list-item-template').html());
$('#user-list').append(tmpl(userInfo))
});
}
function addNewMessage(packet) {
var msg_class = "";
if (packet['sender_name'] == $("#owner_username").val()) {
msg_class = "pull-left";
} else {
msg_class = "pull-right";
}
var msgElem =
$('<div class="row msg-unread" data-id="' + packet.message_id + '">' +
'<p class="' + msg_class + '">' +
'<span class="username">' + packet['sender_name'] + ': </span>' +
packet['message'] +
' <span class="timestamp">– <span data-livestamp="' + packet['created'] + '"> ' + packet['created'] + '</span></span> ' +
'</p> ' +
'</div>');
$('#messages').append(msgElem);
scrollToLastMessage()
}
function scrollToLastMessage() {
var $msgs = $('#messages');
$msgs.animate({"scrollTop": $msgs.prop('scrollHeight')})
}
function generateMessage(context) {
var tmpl = Handlebars.compile($('#chat-message-template').html());
return tmpl({msg: context})
}
function setUserOnlineOffline(username, online) {
var elem = $("#user-" + username);
if (online) {
elem.attr("class", "btn btn-success");
} else {
elem.attr("class", "btn btn-danger");
}
}
function gone_online() {
$("#offline-status").hide();
$("#online-status").show();
}
function gone_offline() {
$("#online-status").hide();
$("#offline-status").show();
}
function flash_user_button(username) {
var btn = $("#user-" + username);
btn.fadeTo(700, 0.1, function () {
$(this).fadeTo(800, 1.0);
});
}
function setupChatWebSocket() {
var opponent_username = getOpponnentUsername();
websocket = new WebSocket(base_ws_server_path + '{{ request.session.session_key }}/' + opponent_username);
websocket.onopen = function (event) {
var opponent_username = getOpponnentUsername();
var onOnlineCheckPacket = JSON.stringify({
type: "check-online",
session_key: '{{ request.session.session_key }}',
username: opponent_username
{# Sending username because the user needs to know if his opponent is online #}
});
var onConnectPacket = JSON.stringify({
type: "online",
session_key: '{{ request.session.session_key }}'
});
console.log('connected, sending:', onConnectPacket);
websocket.send(onConnectPacket);
console.log('checking online opponents with:', onOnlineCheckPacket);
websocket.send(onOnlineCheckPacket);
monitor = initScrollMonitor();
};
window.onbeforeunload = function () {
var onClosePacket = JSON.stringify({
type: "offline",
session_key: '{{ request.session.session_key }}',
username: opponent_username,
{# Sending username because to let opponnent know that the user went offline #}
});
console.log('unloading, sending:', onClosePacket);
websocket.send(onClosePacket);
websocket.close();
};
websocket.onmessage = function (event) {
var packet;
try {
packet = JSON.parse(event.data);
console.log(packet)
} catch (e) {
console.log(e);
}
switch (packet.type) {
case "new-dialog":
// TODO: add new dialog to dialog_list
break;
case "user-not-found":
// TODO: dispay some kind of an error that the user is not found
break;
case "gone-online":
if (packet.usernames.indexOf(opponent_username) != -1) {
gone_online();
} else {
gone_offline();
}
for (var i = 0; i < packet.usernames.length; ++i) {
setUserOnlineOffline(packet.usernames[i], true);
}
break;
case "gone-offline":
if (packet.username == opponent_username) {
gone_offline();
}
setUserOnlineOffline(packet.username, false);
break;
case "new-message":
var username = packet['sender_name'];
if (username == opponent_username || username == $("#owner_username").val()){
addNewMessage(packet);
if (username == opponent_username) {
initReadMessageHandler(monitor, $("div[data-id='" + packet['message_id'] + "']"));
}
} else {
if ($("#user-"+username).length == 0){
var new_button = $(''+
'<a href="/'+ username + '"' +
'id="user-'+username+'" class="btn btn-danger">{% trans "Chat with" %} '+username+'</a>');
$("#user-list-div").find("ul").append()
}
flash_user_button(username);
}
break;
case "opponent-typing":
var typing_elem = $('#typing-text');
if (!typing_elem.is(":visible")) {
typing_elem.fadeIn(500);
} else {
typing_elem.stop(true);
typing_elem.fadeIn(0);
}
typing_elem.fadeOut(3000);
break;
case "opponent-read-message":
if (packet['username'] == opponent_username) {
$("div[data-id='" + packet['message_id'] + "']").removeClass('msg-unread').addClass('msg-read');
}
break;
default:
console.log('error: ', event)
}
}
}
function sendMessage(message) {
var opponent_username = getOpponnentUsername();
var newMessagePacket = JSON.stringify({
type: 'new-message',
session_key: '{{ request.session.session_key }}',
username: opponent_username,
message: message
});
websocket.send(newMessagePacket)
}
$('#chat-message').keypress(function (e) {
if (e.which == 13 && this.value) {
sendMessage(this.value);
this.value = "";
return false
} else {
var opponent_username = getOpponnentUsername();
var packet = JSON.stringify({
type: 'is-typing',
session_key: '{{ request.session.session_key }}',
username: opponent_username,
typing: true
});
websocket.send(packet);
}
});
$('#btn-send-message').click(function (e) {
var $chatInput = $('#chat-message');
var msg = $chatInput.val();
if (!msg) return;
sendMessage($chatInput.val());
$chatInput.val('')
});
setupChatWebSocket();
scrollToLastMessage();
});
</script>
{% endblock %}
You are making it too complex,you don't need to write any view or anything.After You have installed django-private-chat using pip install django-private-chat and added it to installed apps.
Add following lines to your settings .py file
CHAT_WS_SERVER_HOST = 'localhost'
CHAT_WS_SERVER_PORT = 5002
CHAT_WS_SERVER_PROTOCOL = 'ws'
Next thing you need to do is all its url to your main project's urls.py.
It should look something like this , URLS.py
from django_private_chat import urls as django_private_chat_urls
urlpatterns = [
#other urls of ur project
path('', include('django_private_chat.urls')),
]
Next step is front-end part,hope u have an static directory of your project,if not create an static folder in path same as of your manage.py file of your project.
Now copy contents of static folder of django-private-chat from enviroment.It's path would be your_venv_name/lib/pythonX.X/site-packages/django_private_chat/static.Copy all the contents of this static folder to your project's static folder.It should look somehing like this your_project/static/django_private_chat/static
Next thing is your dailog.html page. If you don't have templates folder in your project already created,create one.Then In that templates folder make an 'base.html' file, contents of your base.html file would be something like this:
base.html
<html><head></head><body>
{% block content %}
{% endblock %}
{%block js%}
{%endblock%}
{% block extra_js %}{% endblock extra_js %}
</body>
Next in your templates folder,create dailogs.html file,an copy whatever you have written in dailogs.html in your question,just copy this inside the {%block css%}
{{ block.super }}
<link href="{% static 'django_private_chat/css/django_private_chat.css' %}" rel="stylesheet" type="text/css" media="all">
Now in One terminal window runserver and in another terminal windows from same directory of where your runserver is running. Execute
python manage.py run_chat_server
go to browser and login into your django project.then go to http://127.0.0.1:8000/dialogs/
you'll find the users to chat with.