Django-Angularjs dynamic rating app - django

I trying to create a Angularjs rating app with django. I'm using the rating mechanism from angular-bootstrap. http://angular-ui.github.io/bootstrap/
I've created a controller which can handle ratings for only one object but i would like to create an app witch can rate any object on my website no matter where they are.
To identify each object, I using:
data-id="{{ id }}" data-app-label="{{ app_label }}" data-model="{{ model }}"
My template:
<div ng-app="ratingApp">
<div ng-controller="RatingCtrl">
<div class="rating-input">
<div rating id="rating" ng-model="rate" max="max" readonly="isReadonly" on-leave="overStar = null" data-id="{{ id }}" data-app-label="{{ app_label }}" data-model="{{ model }}" ng-click="postRating(value)"></div>
<div class="menge thanks" ng-hide="showMessage"><span>{$ menge $}</span></div>
<div class="message"><div class="ng-hide thanks" ng-show="showMessage">{$ message $}</div></div>
</div>
</div>
</div>
My controller:
ratingControllers.controller('RatingCtrl', ['$scope', '$http', '$element', '$timeout',
function ($scope, $http, $element, $timeout) {
var messageTimer = false,
displayDuration = 2000;
$scope.message = "Thanks!";
$scope.showMessage = false;
$scope.doGreeting = function () {
if (messageTimer) {
$timeout.cancel(messageTimer);
}
$scope.showMessage = true;
messageTimer = $timeout(function () {
$scope.showMessage = false;
}, displayDuration);
};
$scope.max = 6;
$scope.isReadonly = false;
var id = $element.find("#rating").attr("data-id");
var app_label = $element.find("#rating").attr("data-app-label");
var model = $element.find("#rating").attr("data-model");
var request = $http.post("/rating/get-rating/", {id: id, app_label: app_label, model: model});
request.success(function (data) {
$scope.rate = data.sterne;
$scope.menge = data.menge;
});
$scope.postRating = function (value) {
var request = $http.post("/rating/set-rating/", {id: id, app_label: app_label, model: model, sterne: $scope.rate});
request.success(function (data) {
$scope.doGreeting();
if (data.message) {
alert(data.message);
}
});
};
}
]);
Any idea how can i make this more reusable to rate any object on the website?

Related

Why images are not submitted when using ajax call in django?

I am using a modelForm to create post objects via ajax. The images field are part of the form but not passed to the fields of the Meta class because that will allow to save the post first and add the images uploaded after that. My issue is if I do use a regular view(without ajax) the request.FILES are being submitted correctly but when I use via ajax those files are not part of the request.files which renders an empty <MultiValueDict: {}> I don't really know why.
Here is my code.
def post(self, request, *args, **kwargs):
form = PostForm(request.POST or None, request.FILES or None)
result = {}
files = request.FILES
print(files)
if is_ajax(request=request) and form.is_valid():
print("the request is ajax and the form is valid")
title = form.cleaned_data.get("content", "")
print("Title ", title)
post_instance = form.save(commit=False)
post_instance.author = request.user
result['success'] = True
return JsonResponse(result)
$.ajax({
url: $("#CreatePostModal").attr("data-url"),
data:$("#CreatePostModal #createPostForm").serialize(),
method: "post",
dataType: "json",
success: (data) => {
if (data.success) {
setTimeout(() => {
$(e.target).next().fadeOut();
ResetForm('createPostForm', 'PreviewImagesContainer')
$("#CreatePostModal").modal('hide')
$(e.target.nextElementSibling).fadeOut()
alertUser("Post", "has been created successfully!")// alerting the user
}, 1000)
console.log(data.title)
} else {
$("#createPostForm").replaceWith(data.formErrors);
$("#PreviewImagesContainer").html("");
$("#CreatePostModal").find("form").attr("id", "createPostForm");
$(e.target.nextElementSibling).fadeOut()
};
$(e.target).prop("disabled", false);
},
error: (error) => {
console.log(error)
}
})
});
Here is the form file
class PostForm(forms.ModelForm):
images = forms.ImageField(
required=False,
widget=forms.ClearableFileInput(attrs={
'multiple': True,
})
)
class Meta:
model = Post
fields = ("content",)
widgets = {
"content": forms.Textarea(attrs={"placeholder": "Tell us something today....", "rows": 5, "label": ""})
}
again the imagefield has a manytomany relationship with the post model.
What I am doing wrong?
Here is the modal where I am rendering the form itself with crispy form
<!-- create post modal -->
<div class="modal fade" id="CreatePostModal" data-url="{% url 'post-list-view' %}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Creating Post</h5>
<button type="button" class="close" data-bs-dismiss="modal" aria-label="Close">
×
</button>
</div>
<div class="modal-body">
<div class="container-fluid">
{% crispy form %}
<div id="PreviewImagesContainer">
</div>
</div>
</div>
<div class="modal-footer float-right">
<button type="button" class="btn btn-sm btn-dark" data-bs-dismiss="modal">Cancel</button>
<button type="submit" form="createPostForm" id="createPostBtn" class="btn btn-sm btn-primary">Post</button>
<span class="loading-icon"><i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
</div>
</div>
</div>
<!-- end of create post modal -->
I finally solved it using the FormData of javascript by looping through of the files that I am getting from the input then append it to the data of the FormData.
let imageFiles = []
$("#CreatePostModal").on('change', (e) => {
$(postImagesPreviewContainer).html("")
if ($(e.target).attr("id") !== "id_images") return;
var filenames = "";
for (let i = 0; i < e.target.files.length; i++) {
filenames += (i > 0 ? ", " : "") + e.target.files[i].name;
}
e.target.parentNode.querySelector('.custom-file-label').textContent = filenames;
//why is the this element returning the document and not the target itself
// check the length of the files to know what template to make
const files = e.target.files
const numberOfImages = files.length
let gridColumnSize;
if (numberOfImages > 5 | numberOfImages === 0) return;
var row = document.createElement("div")
row.setAttribute("class", "post-images")
for (file of files) {
const postImageChild = document.createElement("div");
postImageChild.setAttribute("class", "post-images__child_down")
const reader = new FileReader();
reader.onload = () => {
img = document.createElement("img")
img.setAttribute("src", reader.result)
img.onload = (e) => {
// here i will process on resizing the image
const canvas = document.createElement("canvas")
const max_width = 680
const scaleSize = max_width / e.target.width
canvas.width = max_width
canvas.height = e.target.height * scaleSize
var ctx = canvas.getContext("2d") // setting the context of the canvas
ctx.drawImage(e.target, 0, 0, canvas.width, canvas.height)
const encodedSource = ctx.canvas.toDataURL(e.target, 'image/png', 1)
const processedImg = document.createElement("img") // create a processed image and return it.
processedImg.src = encodedSource
$(postImageChild).append(processedImg)
imageFiles.push(processedImg)
}
}
$(row).prepend(postImageChild)
$(postImagesPreviewContainer).append(row);
reader.readAsDataURL(file)
}
After getting and resizing all the images I made the ajax call like that:
$("#CreatePostModal").on("click", (e) => {
if ($(e.target).attr("id") !== "createPostBtn") return;
e.preventDefault();
e.target.setAttribute("disabled", true);
$(e.target.nextElementSibling).fadeIn()
var form = $("#createPostForm")[0]
var data = new FormData(form); // getting the form data
console.log("this is the data", data)
for (var i = 0; i < imageFiles.length; i++) { // appending images to data
data.append('images', imageFiles[i]);
};
$.ajax({
url: $("#CreatePostModal").attr("data-url"),
data: data, //$("#CreatePostModal #createPostForm").serialize(),
method: "post",
processData: false,
cache: false,
contentType: false,
dataType: "json",
success: (data) => {
if (data.success) {
setTimeout(() => {
$(e.target).next().fadeOut();
ResetForm('createPostForm', 'PreviewImagesContainer')
$("#CreatePostModal").modal('hide')
$(e.target.nextElementSibling).fadeOut()
alertUser("Post", "has been created successfully!")// alerting the user
}, 1000)
} else {
$("#createPostForm").replaceWith(data.formErrors);
$("#PreviewImagesContainer").html("");
$("#CreatePostModal").find("form").attr("id", "createPostForm");
$(e.target.nextElementSibling).fadeOut()
};
$(e.target).prop("disabled", false);
},
error: (error) => {
console.log(error)
}
})
});
and finally getting the images from request.FILES in the views.py file.
def post(self, request, *args, **kwargs):
form = PostForm(request.POST or None, request.FILES or None)
result = {}
files = request.FILES.getlist("images")
if is_ajax(request=request) and form.is_valid():
post_obj = form.save(commit=False)
post_obj.author = request.user
print(post_obj)
post_obj.save()
for file in files:
new_file = Files(image=file)
new_file.save()
post_obj.images.add(new_file)
post_obj.save()
result['success'] = True
return JsonResponse(result)

Image is not passing to view (cropperjs, django, ajax)

I have an user profile where he can crop his image, and so after cropping, and printing data in view nothing is passed to view
Form
<form method="post" action="change_photo/" id="avatar_changing_form">
{% csrf_token %}
<label for="upload_image">
{% if item.image%}
<img src="{{item.image.url}}" alt="" style="max-height:300px">
{%else%}
<img src="{%static 'avatar_sample.png' %}" id="uploaded_image"
class="img-responsive img-circle" />
{%endif%}
<div class="overlay">
<div class="text">Click to Change Profile Image</div>
</div>
<!-- <input type="file" name="image" class="image" id="upload_image" style="display:none" /> -->
{{imageForm.image}}
</label>
</form>
JS & Ajax
$(document).ready(function(){
const imageForm = document.getElementById('avatar_changing_form')
const confirmBtn = document.getElementById('crop')
const input = document.getElementById('upload_image')
const csrf = document.getElementsByName('csrfmiddlewaretoken')
var $modal = $('#modal');
var image = document.getElementById('sample_image');
var cropper;
$('#upload_image').change(function (event) {
var files = event.target.files;
var done = function (url) {
image.src = url;
$modal.modal('show');
};
if (files && files.length > 0) {
reader = new FileReader();
reader.onload = function (event) {
done(reader.result);
};
reader.readAsDataURL(files[0]);
}
});
$modal.on('shown.bs.modal', function () {
cropper = new Cropper(image, {
aspectRatio: 1,
viewMode: 3,
preview: '.preview'
});
}).on('hidden.bs.modal', function () {
cropper.destroy();
cropper = null;
});
$('#crop').click(function () {
cropper.getCroppedCanvas().toBlob((blob) => {
console.log('confirmed')
const fd = new FormData();
fd.append('csrfmiddlewaretoken', csrf[0].value)
fd.append('file', blob, 'my-image.png');
$.ajax({
type: 'POST',
url: imageForm.action,
enctype: 'multipart/form-data',
data: fd,
success: function (response) {
console.log('success', response)
$modal.modal('hide');
$('#uploaded_image').attr('src', fd);
},
error: function (error) {
console.log('error', error)
},
cache: false,
contentType: false,
processData: false,
})
});
});
})
View
def change_photo(request):
if request.user.is_authenticated and Guide.objects.filter(user = request.user).exists():
item = Guide.objects.get(user=request.user)
if request.method == "POST":
Photoform = ChangeImageForm(request.POST or None, request.FILES or None, instance = item)
if Photoform.is_valid():
print(Photoform.cleaned_data['image'])
Photoform.save()
return HttpResponseRedirect('/profile/')
form
class ChangeImageForm(ModelForm):
class Meta:
model = Guide
fields = ['image']
def __init__(self, *args, **kwargs):
super(ChangeImageForm, self).__init__(*args, **kwargs)
self.fields['image'].widget = FileInput(attrs={
'name':'image',
'class':'image',
'id':'upload_image',
'style':'display:none'
})
When i print image field from cleaned data in terminal displays "none", and when i load image through admin everything working good, can pls someone tell me where the problem is?

Upload Image File along with regular Form Data

I am using django with VueJS. The data is updating properly in my database.
I need to accomplish two things:
Post the correct content to the field image_file.
Get the downloaded image file pasted onto the servers folder which is media/shop/images
My attempted code is as below:
models.py
...
image_file = models.ImageField(upload_to='shop/images/', blank=True, null=True)
urls.py
...
urlpatterns += [
url(r'^Post-Items-Axios$', myviews.Post_Items_Axios, name='Post-Items-Axios'),
]
views.py
#api_view(['GET', 'POST', 'PUT', 'DELETE'])
def Post_Items_Axios(request):
if request.method == 'POST':
data_itemfullhd = request.data['Item Name']
data_image_file = request.data['Item Image File']
td_items, created = Md_Items.objects.get_or_create(
itemfullhd = data_itemfullhd)
td_items.imagefl = data_imagefl
td_items.image_file = data_image_file
td_items.save()
data = { 'data_itemfullhd': data_itemfullhd }
return Response(data)
bm_home.html
<template id="product-edit">
<div>
<h2>Product details</h2>
<form method="post" enctype="multipart/form-data">{% csrf_token %}
<div class="form-group">
<label for="edit-name">Item Name</label>
<input class="form-control" id="edit-name" v-model="product.itemfullhd" required/>
</div>
<!-- Upload single Image files -->
<div class="form-group">
<label for="edit-imagefile">Image</label>
<input type="file" id="edit-imagefile" #change="onFileChanged" required/>
</div>
<button type="submit" class="btn btn-primary" #click.prevent="updateProduct">Save</button>
<a class="btn btn-dark"><router-link to="/product-list">Cancel</router-link></a>
</form>
</div>
</template>
Vue template
var ProductEdit = Vue.extend({
template: '#product-edit',
data: function () {
return {
product: findProduct(this.$route.params.product_id),
selectedImage: null,
};
},
methods: {
onFileChanged (event) {
this.selectedImage = event.target.files[0]
this.product.image_file = this.selectedImage.name
},
updateProduct: function () {
let product = this.product;
products[findProductKey(product.id)] = {
id: product.id,
itemfullhd: product.itemfullhd,
image_file: product.image_file,
};
const data = {
"Item Name": product.itemfullhd,
"Item Image File": product.image_file,
}
// const formData = new FormData()
// formData.append('image_file', this.selectedImage, this.selectedImage.name)
// axios.post('/biggmount_home/Post-Items-Axios', formData, data)
axios.post('/biggmount_home/Post-Items-Axios', data)
router.push('/product-list');
},
}
});
The changes below have given me the result that I was looking to achieve:
Vue template
var ProductEdit = Vue.extend({
template: '#product-edit',
data: function () {
return {
product: findProduct(this.$route.params.product_id),
selectedImage: null,
};
},
methods: {
onFileChanged (event) {
this.selectedImage = event.target.files[0]
},
updateProduct: function () {
let product = this.product;
const formData = new FormData()
formData.append('Item Image', this.selectedImage, this.selectedImage.name)
formData.append('Item Name', product.itemfullhd)
axios.post('/biggmount_home/Post-Items-Axios', formData)
router.push('/product-list');
},
}
});
views.py
#api_view(['GET', 'POST', 'PUT', 'DELETE'])
def Post_Items_Axios(request):
if request.method == 'POST':
data_itemfullhd = request.data['Item Name']
td_items, created = Md_Items.objects.get_or_create(
itemfullhd = request.data['Item Name'],
)
td_items.image_file = request.data['Item Image']
td_items.save()
return HttpResponse('Success!')

Ember template not updating when new record is saved [duplicate]

In my app, a user can enter a description of a friend or upvote a description that is already present. Both methods (createDescription and upvoteDescription) persist in the database. upvoteDescription changes the DOM, but createDescription does not. It may be because I'm passing a parameter in the model, but I can't get around that -- the api needs it.
//descriptions route
App.DescriptionsRoute = Ember.Route.extend({
...
model: function () {
var store = this.get('store'),
friend = this.modelFor('friend');
return store.find('description', {friend_id: friend.id});
}
})
//descriptions controller
App.DescriptionsController = Ember.ArrayController.extend({
...
actions: {
createDescription: function () {
var name = this.get('name'),
friend_id = this.get('controllers.friend').get('id'),
store = this.get('store'),
description = store.createRecord('description', {
name: name,
friend_id: friend_id
});
description.save();
},
upvoteDescription: function (description) {
var count = description.get('count');
description.set('count', count + 1);
description.save();
}
}
});
//descriptions template
{{input value=name action="createDescription" type="text"}}
{{#each controller}}
<div {{bind-attr data-name=name data-count=count }} {{action upvoteDescription this}}>
<div>{{name}}</div>
<span>{{count}}</span>
</div>
{{/each}}
find (by query) doesn't actively make sure it has the records that match the query, so you'll have to manually inject it into the results.
createDescription: function () {
var name = this.get('name'),
friend_id = this.get('controllers.friend').get('id'),
store = this.get('store'),
description = store.createRecord('description', {
name: name,
friend_id: friend_id
});
description.save();
this.pushObject(description);
},
or you can use a live record array (filter/all)
model: function () {
var store = this.get('store'),
friend = this.modelFor('friend');
store.find('description', {friend_id: friend.id});
return store.filter('description', function(record){
return record.get('friend_id') == friend.id;
});
}

Ember template not updating from arraycontroller

In my app, a user can enter a description of a friend or upvote a description that is already present. Both methods (createDescription and upvoteDescription) persist in the database. upvoteDescription changes the DOM, but createDescription does not. It may be because I'm passing a parameter in the model, but I can't get around that -- the api needs it.
//descriptions route
App.DescriptionsRoute = Ember.Route.extend({
...
model: function () {
var store = this.get('store'),
friend = this.modelFor('friend');
return store.find('description', {friend_id: friend.id});
}
})
//descriptions controller
App.DescriptionsController = Ember.ArrayController.extend({
...
actions: {
createDescription: function () {
var name = this.get('name'),
friend_id = this.get('controllers.friend').get('id'),
store = this.get('store'),
description = store.createRecord('description', {
name: name,
friend_id: friend_id
});
description.save();
},
upvoteDescription: function (description) {
var count = description.get('count');
description.set('count', count + 1);
description.save();
}
}
});
//descriptions template
{{input value=name action="createDescription" type="text"}}
{{#each controller}}
<div {{bind-attr data-name=name data-count=count }} {{action upvoteDescription this}}>
<div>{{name}}</div>
<span>{{count}}</span>
</div>
{{/each}}
find (by query) doesn't actively make sure it has the records that match the query, so you'll have to manually inject it into the results.
createDescription: function () {
var name = this.get('name'),
friend_id = this.get('controllers.friend').get('id'),
store = this.get('store'),
description = store.createRecord('description', {
name: name,
friend_id: friend_id
});
description.save();
this.pushObject(description);
},
or you can use a live record array (filter/all)
model: function () {
var store = this.get('store'),
friend = this.modelFor('friend');
store.find('description', {friend_id: friend.id});
return store.filter('description', function(record){
return record.get('friend_id') == friend.id;
});
}