How to access "context" from http request between two django apps - django

So i'm fairly new to django, html, and javascript.
In my first app, I have a button that when I click it, it invokes an $.ajax type "GET" function. In the script of the html template of app1, I have the following:
$('#btn_to_app2').click(function () {
if(Object.keys(name).length > 0){
$.ajax(
{
type:"GET",
url:"to_app2",
data:{
'name': JSON.stringify(name),
'place': JSON.stringify(place),
},
success: function (data) {
if(data["noted"] == 1){
window.location.href = "http://" + window.location.host + "/app2/";
}
}
}
)
}
The accessed url refers to a view function (defined in the url.py and views.py files of the corresponding app1).
def to_app2(request):
if request.is_ajax() and request.method == 'GET':
name = json.loads(request.GET['name'])
request.session['name'] = name
place = json.loads(request.GET['place'])
request.session['place'] = place
return JsonResponse({
'noted': 1,
})
The data obtained is send to the next app using a http request. In the views.py file of app2, I have the following bit of code:
def app2(request):
context = {
'name': request.session['name'],
'place': request.session['place']
}
return render(request, 'app2.html', context)
How can I now access the information contained in the "context" variable within the script of the html-file of app2?
I have tried the let name = {{ name | safe }}; framework, with quotes and without, but that doesn't seem to work.

Related

How to upload multiple files in Django using Dropzone js?

Info: I want to upload multiple files using Dropzone js in Django project. I have two models. One for the Post and the other would be for the File. My files model would have a foreignkey to the Post model. About my Views.py when the user is filling out the form post he has to complete the Files form too for the post.
Fine: When i submit the Ajax_file_uploads form instead of using Dropzone.js multiple selected files are attached with single Post instance.
Problem: If i try to upload multiple files using Dropzone.js Multiple articles are created along with multiple files when I submit the form.
Any help would be much appreciated!
models.py
class Post(models.Model):
title = models.CharField(max_length=100, blank=True)
content = models.TextField(blank=True)
class FileAttach(models.Model):
file = models.FileField(upload_to='uploads/')
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='file_attach', null=True)
views.py
def Ajax_file_uploads(request):
if request.method == "POST":
p_form = PostForm(request.POST or None)
form = AttachForm(request.POST or None, request.FILES)
if p_form.is_valid():
art = p_form.save(commit=False)
art.user = request.user
art.save()
files = request.FILES.getlist('file')
if form.is_valid():
for f in files:
file_instance = FileAttach(file=f, post=art)
file_instance.save()
return JsonResponse({"error": False, "message": "Uploaded Successfully"})
else:
return JsonResponse({"error": True, "errors": p_form.errors})
else:
p_form = PostForm()
form = AttachForm()
return render(request, "upload/api.html", {"p_form": p_form, "form": form})
create.html
<script>
Dropzone.autoDiscover = false;
// Dropzone.options.myDropzone = false;
// set the dropzone container id
var id = "#kt_dropzonejs_example_2";
// set the preview element template
var previewNode = $(id + " .dropzone-item");
previewNode.id = "";
var previewTemplate = previewNode.parent(".dropzone-items").html();
previewNode.remove();
var myDropzone = new Dropzone(id, { // Make the whole body a dropzone
// url: "/uploader/files/", // Set the url for your upload script location
url: '/uploader/multi/',
headers: { 'X-CSRFToken': '{{ csrf_token }}' },
timeout: 2000000,
parallelUploads: 100,
previewTemplate: previewTemplate,
// uploadMultiple: true,
maxFilesize: 200, // Max filesize in MB
maxFiles: 5, // Max filesize in MB
autoQueue: false, // Make sure the files aren't queued until manually added
previewsContainer: id + " .dropzone-items", // Define the container to display the previews
clickable: id + " .dropzone-select", // Define the element that should be used as click trigger to select files.
uploadprogress: function (file, progress, bytesSent) {
if (file.previewElement) {
var progressElement = file.previewElement.querySelector("[data-dz-uploadprogress]");
progressElement.style.width = progress + "%";
progressElement.querySelector(".dropzone-progress-total").textContent = progress.toFixed(0) + "%";
}
},
sending: function (file, xhr, formData) {
// Show the total progress bar when upload starts
$(id + " .progress-bar").css("opacity", "1");
// Add other form data
var data = $('#form-data').serializeArray();
$.each(data, function (key, el) {
formData.append(el.name, el.value);
});
},
// success: function (file) {
// file.previewElement.remove()
// }
init: function () {
$("#form-data").validate({
submitHandler: function (form) {
myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
}
});
},
});
myDropzone.on("addedfile", function (file) {
// Hookup the start button
$(document).find(id + " .dropzone-item").css("display", "");
$(id + " .dropzone-remove-all").css("display", "inline-block");
});
// Hide the total progress bar when nothing's uploading anymore
myDropzone.on("complete", function (progress) {
var thisProgressBar = id + " .dz-complete";
setTimeout(function () {
$(thisProgressBar + " .progress-bar, " + thisProgressBar + " .progress").css("opacity", "0");
}, 300)
});
// Setup the button for remove all files
document.querySelector(id + " .dropzone-remove-all").onclick = function () {
$(id + " .dropzone-remove-all").css("display", "none");
myDropzone.removeAllFiles(true);
};
// On all files removed
myDropzone.on("removedfile", function (file) {
if (myDropzone.files.length < 1) {
$(id + " .dropzone-remove-all").css("display", "none");
}
});
</script>
Dropzone submits each file separately via AJAX calls and when it does that it submits the file and all form inputs with each AJAX call. Based on the way your views.py file is written, this will cause a Post instance to be created and then a FileAttach instance to be created and associated with the Post instance.
I see three ways you could fix this:
Modify your views.py file to check for an existing Post instance before creating a new one. Because each file is uploaded asynchronously there is still a chance that the first Post instance would not be created before the second file upload looks for it and thus two Post instances would still be created.
Add some additional JavaScript to first make an AJAX call that creates the Post instance and returns its id value then allow Dropzone to make its AJAX calls that include the Post.id value. This answer outlines the opposite approach (upload files, then submit form); the concept just needs to be reversed.
Set the uploadMultiple option to true on the Dropzone object so that all the files (and the form) are submitted in one submission to the backend. I have not worked with this so not sure how you would handle that in views.py. My guess is that request.FILES would contain multiple file entries, but again I am not sure how your AttachForm would deal with that.
If it were me, I would go with option 2.

Show loading gif until the django view performs the data processing and renders the template with this data

I have a django project where the page has multiple nav links representing different agents. On clicking any nav link, the urls.py redirects to nav specific view and the view needs to perform some processing to get the data needed to render the template. However as this is syncrhonous rendering it takes a long while to load data (in the order of 15-20s).
Below is my urls.py:
from django.urls import path
from . import views
app_name = 'agent'
urlpatterns = [
path('agent1/', views.agent1, name='agent1'),
path('agent2/', views.agent2, name='agent2'),
path('agent3/', views.agent3, name='agent3'),
path('agent4/', views.agent4, name='agent4'),
]
My views method looks as below:
def agent1(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return render(request, 'manager/data.html', {'agent_data': agent_data, 'agent_details': agent_details})
I am using the {{ agent_data.name }}, {{ agent_data.code }}, {{ agent_data.qty }} and {{ agent_data.price }} along with data from agent_details dictionary in my html to populate a table's rows. How should I change my view method, so that it loads the data via AJAX (javascript) in order to show a loading gif in the meantime and also provide me the data so that I can populate the table. Could someone help me with the Ajax code and the steps as I am new to this technology and not finding any help going through the online tutorials.
So for this to work with ajax, you'll need some javascript in manager/data.html which knows the url to fetch data from.
As an example, I've got an ajax setup which checks a given email address isn't already in use;
(function($) {
$(document).ready(function() {
var validateEmailURL = $section_user_signup.data('ajax-email-url');
function validateEmailUnique() {
var valid = true;
clearError($email);
// Fetch unique status of the provided email
$.ajax({
async: false,
url: validateEmailURL,
method: 'POST',
type: 'POST',
dataType: 'json',
data: {
'email': $email.val(),
'csrftoken': $form.find('input[name="csrfmiddlewaretoken"]').val()
},
success: function (response) {
valid = true;
},
error: function (response) {
setError($email, response["responseJSON"]["error"]);
valid = false;
}
});
return valid;
}
});
})(window.jQuery);
This javascript uses the data attribute of a div for the URL to check;
<div data-ajax-email-url="{% url 'account_ajax_validate_email' %}">
The view which the ajax call goes to looks like this;
def ajax_check_email_unique(request, *args, **kwargs):
"""
Return an JsonResponse to identify if an email is unique.
"""
if not request.is_ajax():
return HttpResponseBadRequest()
if request.is_ajax and request.method == "POST":
email = request.POST.get('email')
if email_address_exists(email):
return JsonResponse(
{
"error":
"Email address already exists. Click "
f"here "
"to login"
},
status=400
)
return JsonResponse(
{"email": email},
status=200
)
# some error occurred
return JsonResponse({"error": ""}, status=400)
The important thing for any view which will be used by javascript is that you return a JsonResponse.
So if I was you, I'd setup a new view for ajax, and that makes your existing one really simple;
def agent1_ajax(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return JsonResponse({
"agent_data": agent_data, "agent_details": agent_details
}, status=200)
def agent1(request):
return render(request, 'manager/data.html', {})
And as far as a loading gif goes, you'd need an element that contains the gif and then you can bind to the ajax event to show/hide;
$(document).ajaxStart(function() {
$("#loading").show();
});
$(document).ajaxStop(function() {
$("#loading").hide();
});

Testing AJAX in Django

I want to test an AJAX call in my Django app.
What is does is adding a product to a favorite list. But I can't find a way to test it.
My views.py:
def add(request):
data = {'success': False}
if request.method=='POST':
product = request.POST.get('product')
user = request.user
splitted = product.split(' ')
sub_product = Product.objects.get(pk=(splitted[1]))
original_product = Product.objects.get(pk=(splitted[0]))
p = SavedProduct(username= user, sub_product=sub_product, original_product = original_product)
p.save()
data['success'] = True
return JsonResponse(data)
My html:
<form class="add_btn" method='post'>{% csrf_token %}
<button class='added btn' value= '{{product.id }} {{ sub_product.id }}' ><i class=' fas fa-save'></i></button
My AJAX:
$(".row").on('click', ".added", function(event) {
let addedBtn = $(this);
console.log(addedBtn)
event.preventDefault();
event.stopPropagation();
var product = $(this).val();
console.log(product)
var url = '/finder/add/';
$.ajax({
url: url,
type: "POST",
data:{
'product': product,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
},
datatype:'json',
success: function(data) {
if (data['success'])
addedBtn.hide();
}
});
});
The problem is that I pass '{{product.id }} {{ sub_product.id }}' into my views.
My test so far:
class Test_add_delete(TestCase):
def setUp(self):
self.user= User.objects.create(username="Toto", email="toto#gmail.com")
self.prod = Product.objects.create(
name=['gazpacho'],
brand=['alvalle'],
)
self.prod_2 = Product.objects.create(
name=['belvita'],
brand=['belvita', 'lu', 'mondelez'],
)
def test_add(self):
old_saved_products = SavedProduct.objects.count()
user = self.user.id
original_product = self.prod.id
sub_product = self.prod_2.id
response = self.client.post(reverse('finder:add', args=(user,))), {
'product': original_product, sub,product })
new_saved_products = SavedProducts.objects.count()
self.assertEqual(new_saved_products, old_saved_products + 1)
My test is not running and I get a SyntaxError 'product': original_product, sub_product. I know it's not the proper way to write it but my AJAX send the two ids with a space in between to the view.
If all you want to do is test if the data was actually saved, instead of just returning data['success'] = True you can return the whole entire new object... That way you can get back the item you just created from your API, and see all the other fields that may have been auto-gen (ie date_created and so on). That's a common thing you'll see across many APIs.
Another way to test this on a Django level is just to use python debugger
import pdb; pdb.set_trace() right before your return and you can just see what p is.
The set_trace() will stop python and give you access to the code scope from the command line. So just type 'l' to see where you are, and type(and hit enter) anything else that's defined, ie p which will show you what p is. You can also type h for the help menue and read the docs here

django upload a file from other python script

I want to save file from the client to the django project server's database from a script. I've tried to do this using a model and a view in the django project, and post request in the other python script, but it keeps return 403 error and not save the file and the data to the database.
models.py:
class ScreenRecord(models.Model):
record = models.FileField(default='output.avi', upload_to='records')
writeTime = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
views.py:
def getscreenrecords(request):
user = User.objects.filter(username=request.POST.get('user')).first()
k = ScreenRecord(record=request.FILES.get('record'), user=user)
k.save()
return HttpResponse('success ' + request.GET.__getitem__('user'))
url.py:
from . import views
urlpatterns = [
path('screenrecords/', views.getscreenrecords, name='getscreenrecords'),
]
python script to send the file:
url = 'http://127.0.0.1:8000/send/screenrecords/'
files = {'record': open('output.avi','rb')}
values = {'user': 'newUser'}
r = requests.post(url, files=files, data=values)
print(r)
what's wrong in my code or is there a way to do this better?
Django needs a CSRF token in POST requests by default.
Check this for more info on how to use it on your requests.
You need to pass csrf_token along with the data passed in your js, if you are doing it within the Django project, here is a sample code to do it.
<script>
var token = '{{csrf_token}}';
$("#id_username").change(function () {
console.log($(this).val());
var form = $(this).closest("form");
$.ajax({
headers: { "X-CSRFToken": token },
url: form.attr("data-validate-username-url"),
data: form.serialize(),
dataType: 'json',
success: function (data) {
if (data.is_taken) {
alert(data.error_message);
}
}
});
});
</script>

Django store to db using POST without django form

I'm new in Django.
Besides using Django form to access django database, I'm now trying to use POST request to do that. But it's doesn't work, there is no data store to database.
In views.py
def event_join(request):
print ("event_join")
if request.method == 'POST':
userID=request.GET['usrID']
join_id = e_id
usr = Usr.objects.get(usr_id=userID)
if join_id in usr.join_ids.split(','):
print "Joined already!"
else:
usr.join_ids += ',' + join_id
usr.save()
Event_member.objects.create(
e_id = join_id,
usr_id = userID,
star_to_emer = 0,
star_from_emer = 0,
)
return render(request, 'index.html', {
'unfinished_events': Event.objects.filter(finish=0)})
And button active function join
var join = function() {
console.log(userID);
$.post('/event_join', {usrID:userID}, function(data) {});
}
In urls.py - urlpatterns
url(r'^event_join/$', views.event_join, name='event_join'),
You're checking the GET parameters in a POST call. Modify your code at least to do:
userID = request.POST['usrID']
Next, you're posting to /event_join, but your urls.py is configured to handle the path with the trailing slash, ^event_join/$. Make them consistent.