Likes feature in Django - python

I'm trying to implement a Like feature for my web app. Here is the code:
Model:
class Like(models.Model):
user = models.ManyToManyField(User, related_name='likes')
doctor = models.ForeignKey(Doctor)
date = models.DateTimeField(auto_now_add=True)
total_likes = models.IntegerField(default=0)
View:
def like(request):
vars = {}
if request.method == 'POST':
user = request.user
slug = request.POST.get('slug', None)
doctor = get_object_or_404(Doctor, slug=slug)
liked, created = Like.objects.create(Doctor=doctor)
try:
user_liked = Like.objects.get(Doctor=doctor, user=user)
except:
user_liked = None
if user_liked:
user_liked.total_likes -= 1
liked.user.remove(request.user)
user_liked.save()
else:
liked.user.add(request.user)
liked.total_likes += 1
liked.save()
return HttpResponse(simplejson.dumps(vars),
mimetype='application/javascript')
URL:
url(r'^like/(?P<id>\d+)/$', views.like, name='like'),
Template:
<input type="button" id="like" name="{{doctor_slug}}" value="Like" />
<script>
$('#like').click(function(){
$.ajax({
type: "POST",
url: "{% url like %}",
data: {'slug': $(this).attr('name'), 'csrfmiddlewaretoken': '{{csrf_token}}'},
dataType: "text",
success: function(response) {
alert('You liked this')
},
error: function(rs, e) {
alert(rs.responseText);
}
});
})
</script>
When I open my template page, I get the error NoReverseMatch at /docprofile/1/
'url' requires a non-empty first argument. The syntax changed in Django 1.5, see the docs. I looked at the docs but couldn't find anything that I could use here. The problem seems to be in the template at "url: "{% url like %}",". This is inside Ajax.

As the error says, you should see the docs. You need to pass the path to the view function or the name of the url to the url template tag as a string.
<script>
$('#like').click(function(){
$.ajax({
type: "POST",
url: "{% url 'like' %}",
data: {'slug': $(this).attr('name'), 'csrfmiddlewaretoken': '{{csrf_token}}'},
dataType: "text",
success: function(response) {
alert('You liked this')
},
error: function(rs, e) {
alert(rs.responseText);
}
});
})
</script>

Related

Django -> 'WSGIRequest' object has no attribute 'data' -> Error in json.loads(request.data)

I saw a similar question but the answer is rather vague. I created a function based view for updateItem. I am trying to get json data to load based on my request. but am getting the error -> object has no attribute 'data'
Views.py file:
def updateItem(request):
data = json.loads(request.data)
productId = data['productId']
action = data['action']
print("productID", productId, "action", action)
customer = request.user.customer
product = Product.objects.get(id=productId)
order, created = Order.objects.get_or_create(customer=customer,complete=False)
orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)
if action == 'add':
orderItem.quantity = (orderItem.quantity + 1)
elif action == 'remove':
orderItem.quantity = (orderItem.quantity - 1)
orderItem.save()
if orderItem.quantity <= 0:
orderItem.delete()
return JsonResponse("Item was added!", safe=False)
JS File:
function updateUserOrder(productId, action) {
console.log('User is logged in...');
let url = '/update_item/';
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({ productId: productId, action: action }),
})
.then((res) => {
return res.json();
})
.then((data) => {
console.log('data', data);
});
}
urls python file:
urlpatterns = [
path("",views.store,name="store"),
path("cart/",views.cart,name="cart"),
path("checkout/",views.checkout,name="checkout"),
path("update_item/",views.updateItem,name="update_item"),
]
The Error seems to also occur in my fetch function in the JS file. With my method POST. Can't find a solution, what am I doing wrong here?
The main problem is that you are trying to access 'request.data', there is no such attribute. What you want is to retrieve data from the POST request.
(Also, note that good practice is to have your views and variable names in snake_case form, whereas camelCase is used for classes):
def updateItem(request):
data = json.loads(request.POST.get('data'))
...
return JsonResponse("Item was added!", safe=False)
Although, to complete my answer, I must say that I had problems with your JS function, with the csrf token not being properly attached. My test solution:
views.py
from django.shortcuts import render
from django.http import JsonResponse
import json
def update_item(request):
return render(request, 'update_item.html', {})
def update_item_ajax(request):
data = json.loads(request.POST.get('data'))
print(data)
...
return JsonResponse({'message': '"Item was added!"'}, safe=False)
# output of update_item_ajax print
{'productId': 1, 'action': 'myaction'}
urls.py
from django.urls import path
from core import views
app_name = 'core'
urlpatterns = [
path('update/item/', views.update_item, name='update-item'),
path('update/item/ajax/', views.update_item_ajax, name='update-item-ajax'),
]
update_item.html
{% extends 'base.html' %}
{% block content %}
<button onclick="updateUserOrder(1, 'action')"> update item </button>
{% endblock %}
{% block script %}
<script>
function updateUserOrder(productId, action) {
console.log('User is logged in...');
let url = "{% url 'core:update-item-ajax' %}";
var payload = {
productId: productId,
action: action
};
var data = new FormData();
data.append( 'data' , JSON.stringify( payload ) );
data.append('csrfmiddlewaretoken', '{{ csrf_token }}');
fetch(url,
{
method: 'POST',
body: data,
})
.then(function(res){ return res.json(); })
.then(function(data){ console.log(data); });
}
</script>
{% endblock %}
Try:
data = json.loads(request.body)
Because the request doesn't have data, since you pass the data as body: JSON.stringify({ productId: productId, action: action }),

Django Error NoReverseMatch at /invest/(?P1\d+)/

So I am trying to update a value in django. But when clicking on submit it is throwing me a error.
NoReverseMatch at /invest/(?P1\d+)/
Reverse for 'invest' with keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried: ['invest/\\(\\?P(?P<pk>[^/]+)\\\\d\\+\\)/$']
Request Method: POST
Request URL: http://127.0.0.1:8000/invest/(%3FP1%5Cd+)/
Django Version: 3.1.7
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'invest' with keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried: ['invest/\\(\\?P(?P<pk>[^/]+)\\\\d\\+\\)/$']
Exception Location: C:\Users\Harsh-PC\AppData\Roaming\Python\Python37\site-packages\django\urls\resolvers.py, line 685, in _reverse_with_prefix
Python Executable: C:\Program Files\Python37\python.exe
Python Version: 3.7.9
Python Path:
['E:\\Colosseum 2021\\django_colo',
'C:\\Program Files\\Python37\\python37.zip',
'C:\\Program Files\\Python37\\DLLs',
'C:\\Program Files\\Python37\\lib',
'C:\\Program Files\\Python37',
'C:\\Users\\Harsh-PC\\AppData\\Roaming\\Python\\Python37\\site-packages',
'C:\\Users\\Harsh-PC\\AppData\\Roaming\\Python\\Python37\\site-packages\\win32',
'C:\\Users\\Harsh-PC\\AppData\\Roaming\\Python\\Python37\\site-packages\\win32\\lib',
'C:\\Users\\Harsh-PC\\AppData\\Roaming\\Python\\Python37\\site-packages\\Pythonwin',
'C:\\Program Files\\Python37\\lib\\site-packages']
Server time: Mon, 22 Mar 2021 09:21:50 +0000
I am calling invest page from brand.html page in this manner
<a href="{% url 'invest' pk=marking.id %}";><button type="button" class="btn btn-primary" data-toggle="modal" >Invest</button></a>
I am passing the value properly in my html code
<script>
$(document).on('submit', '#post-form',function(e){
// console.log("Amount="+$('input[name="amount"]').val());
e.preventDefault();
// getting the value entered
amount = $('input[name="amount"]').val();
product_title = $('input[name="product_title"]').val();
console.log("Product title="+product_title);
console.log("Amount entered by user="+amount);
$.ajax({
type:'POST',
url:"{% url 'invest' pk=context.id %}",
data:{
product_title: product_title,
amount: amount,
csrfmiddlewaretoken: '{{ csrf_token }}',
action: 'post'
},
success: function(xhr, errmsg, err){
window.location = "brand"
},
error : function(xhr,errmsg,err) {
$('#results').html("<div class='alert-box alert radius' data-alert>Oops! We have encountered an error: "+errmsg+
" <a href='#' class='close'>×</a></div>");
console.log(xhr.status + ": " + xhr.responseText);
}
});
});
</script>
urls.py
path('invest/(?P<pk>\d+)/', views.invest, name='invest'),
views.py
def invest(request, pk):
# fetching the current event details
event_data = MarketingMaestro.objects.get(pk = pk)
context = {
'id' : event_data.id,
'product_title' : event_data.product_title,
'total_value' : event_data.total_value,
'total_votes' : event_data.total_votes,
}
# fetching user details
user_data = MarketingInvesment.objects.get(emailID = request.user.email)
user_details = {
'name' : user_data.name,
'emailID' : user_data.emailID,
'remaining_amount' : user_data.remaining_amount
}
total_value_db = event_data.total_value
# updating the user amount
# try:
# checking if the user exist in UserProfile through the logged in email id
if request.POST.get('action') == 'post':
response_data = {}
name = request.user.username
emailID = request.user.email
# remaining amount from db
remaining_amount = user_data.remaining_amount
product_title = request.POST.get('product_title')
# getting user amount
user_amount = request.POST.get('amount')
user_amount_final = int(user_amount)
user_amount_db = total_value_db + user_amount_final
final_amount = remaining_amount - user_amount_final
response_data['name'] = name
response_data['emailID'] = emailID
response_data['remaining_amount'] = remaining_amount
response_data['product_title'] = product_title
# updating the current logged in user values
user_data = MarketingInvesment.objects.get(emailID = request.user.email)
if(user_data.emailID == request.user.email):
MarketingInvesment.objects.filter(emailID = request.user.email).update(
name = name,
emailID = emailID,
remaining_amount = final_amount,
product_title = product_title,
total_investment = user_amount_db
)
return render(request, 'invest.html')
return render(request, 'invest.html', {'context' : context, 'user_details' : user_details})
I think you should use url when working with URL regex, because path doesn't support regex here is the docs for path(). you can use regex in url like this:
from django.conf.urls import url
url(r'^invest/(?P<pk>[\w-]+)/', views.invest, name='invest'),
...
and I think you didn't add marking object in your context
SOLUTION
urls.py
path('investing/<int:pk>/', views.investing, name='investing'),
path('invest/<int:pk>/', views.invest, name='invest'),
views.py Just had to include JsonResponse as I was returning render which is wrong.
return JsonResponse(
{
'message': "success"
}

Sending data from a Django template to views.py via Ajax

I am sending some data from html template to views.py via ajax.From the id sent to views.py I am creating sql records. However I was wondering if there is any way to send data from views.py to template to notify that the data is added to sql.
code-
$('#tn1').click(function(){
var msg='';
alert('inside alert');
if ($('textarea#message') != "") {
var message = $('#notesarea').val();
alert(message);
msg=message;
}
$.ajax({
url: 'post_note',
data: {
'note': msg
},
success: function (data) {
alert(data)
}
});
views.py
def post_note(request,id):
post_id = request.GET['note']
print(post_id)
//sql insertion code,once its done i want to notify to the front end..print some alert message.
return render(request, './profile.html')
You should use something like JSONResponse in your view, then you data will appear in success function
success: function (data) {alert(data)}
You can do this using JQuery Ajax in the template and by creating an "API view" in your views.py that is basically just a regular view that returns a JSONResponse after checking to verify the request is Ajax. As an example of the "API" option, using JQuery:
In your views.py file (using the GET method which you should only use if the note is short, will fit into the URL bar, and if you don't have major security concerns, otherwise see the POST example at bottom):
from django.http import JsonResponse
def post_note_api(request):
data = {}
if request.GET.get('post_note', None) is not None:
post_note = request.GET.get('post_note')
# save the note and indicate success
data['result'] = True
data['message'] = "Note posted successfully"
...
if request.is_ajax():
return JsonResponse(data)
else:
return HttpResponseBadRequest()
In your urls.py:
...
path('/api/post_note/', post_note_api, name="post_note_api"),
...
In your template (if you are using the GET method):
<script type="text/javascript">
$("#tn1").click(function(){
var message = $("#myTextArea").val();
$.ajax({ url: '{% url 'post_note_api' %}?post_note=' + message,
type: "GET",
dataType: "json",
cache: false
}).done(function(data) {
if (data.result === true){
alert(data.message);
}
});
});
});
</script>
If you are using the POST method instead of GET (which is probably the better option here):
<script type="text/javascript">
$("#tn1").click(function(){
var csrfToken = $( "input[name='csrfmiddlewaretoken']");
var message = $("#myTextArea").val();
$.ajax({ url: '{% url 'post_note_api' %}',
type: "POST",
dataType: "json",
data: {'post_note':message, 'csrfmiddlewaretoken':csrfToken.val()},
cache: false
}).done(function(data) {
if (data.result === true){
alert(data.message);
}
});
});
});
</script>
For the POST method, in your view just change request.GET.get('post_note') ... to request.POST.get('post_note') ... like so:
from django.http import JsonResponse
def post_note_api(request):
data = {}
if request.POST.get('post_note', None) is not None:
post_note = request.POST.get('post_note')
# save the note and indicate success
data['result'] = True
data['message'] = "Note saved successfully"
...
if request.is_ajax():
return JsonResponse(data)
else:
return HttpResponseBadRequest()
When you are sending data via POST don't forget to pass along your CSRF token as in the example above. This assumes you have a form on the page you can get it from, otherwise you can use something like this to get it:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
If you don't want to deal with the CSRF token, you can mark the view with the #csrf_exempt decorator and remove the 'csrfmiddlewaretoken' data element from the Ajax call in the template, but it may not be ideal or the most secure. An example of that:
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
#csrf_exempt()
def post_note_api(request):
...
Now, without knowing more, this is basically just pseudocode (plus I just wrote this off the top of my head so it may have errors). If you post more details I can update my answer, but I think this should get you started.
I have used below for sending data from HTML to views.py and then return a success response back to HTML. Hope this can be helpful:)
HTML Code:
<button class="button primary fit small" onclick="saveContent()">Save</button>
Javascript Code:
<script>
function saveContent(){
var code =editor.getSession().getValue();
var URL = "{% url 'save' %}";
var data = {'code': code};
$.post(URL, data, function(response){ // This is the main function that will help you
if(response === 'success'){ window.location.reload(); }
else{ alert('Error! :('); }
});
}
</script>
View.py:
def save_content(request):
if request.method == 'POST':
if 'code' in request.POST:
file_data = request.POST['code']
return HttpResponse('success')

Ajax call to DRF api "You cannot access body after reading from request's data stream"

Hi i'm trying to use Ajax to send a POST request(include json data) to my Django Rest Framework api after user click the save button on a form. I tried with postman and it worked but when i called it with ajax it raised RawPostDataException error
My api in views.py:
class FilterView(APIView):
authentication_classes = [SessionAuthentication, JWTAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
login_user = request.user
paginator = ListPagination()
queryset = Filter.objects.filter(user=login_user).all().order_by('-pk')
context = paginator.paginate_queryset(queryset, request)
serializer = FilterSerializer(context, many=True)
return paginator.get_paginated_response(serializer.data)
def post(self, request):
login_user = request.user
received_json_data=json.loads(request.body)
valid_ser = FilterSerializer(data=received_json_data)
if valid_ser.is_valid():
post_data = received_json_data["data"]
filter = Filter.objects.create(data=post_data, user=login_user)
filter.save()
return JsonResponse({'code':'200','data': filter.id}, status=200)
else:
return JsonResponse({'code':'400','errors':valid_ser.errors}, status=400)
My Serializer:
class FilterSerializer(serializers.ModelSerializer):
user = serializers.IntegerField(required=False, source="user.id")
def validate_data(self, data):
if type(data) is not dict:
raise serializers.ValidationError("invalid JSON object")
class Meta:
model = Filter
fields = ('__all__')
extra_kwargs = {'data': {'required': True}}
And my ajax request in html:
$('#save').click( function(event) {
event.preventDefault();
var type = $('#type').val();
var price_min = $('#price_min').val();
var price_max = $('#price_max').val();
var size_min = $('#size_min').val();
var size_max = $('#size_max').val();
var city = $('#city').val();
var district = $('#district').val();
var ward = $('#ward').val();
var street = $('#street').val();
var frontend_min = $('#front_end_min').val();
var frontend_max = $('#front_end_max').val();
var road = $('#road').val();
var floor = $('#floor').val();
var bedroom = $('#bedroom').val();
var living_room = $('#living_room').val();
var toilet = $('#toilet').val();
var direction = $('#direction').val();
var balcony = $('#balcony').val();
var filter_data = {
'type': type,
'price_min': price_min,
'price_max': price_max,
'size_min': size_min,
'size_max': size_max,
'city': city,
'district': district,
'ward': ward,
'street': street,
'frontend_min': frontend_min,
'frontend_max': frontend_max,
'road': road,
'floor': floor,
'bedroom': bedroom,
'living_room': living_room,
'toilet': toilet,
'direction': direction,
'balcony': balcony,
}
// console.log(filter_data);
$.ajax({
type: "POST",
url: "/api/filters/",
beforeSend: function (xhr) {
xhr.setRequestHeader('X-CSRFToken', '{{csrf_token}}');
},
data: JSON.stringify({"data": filter_data}),
success: function (data) {
if (data.code == 200) {
alert("added filter");
}
}
});
return false;
});
When i call using Ajax it return the following error on my django server:
raise RawPostDataException("You cannot access body after reading from request's data stream")
web_1 | django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
I tried and it worked on my POSTMAN i don't know why it doesn't work on the ajax call
I read about this error and has alot of suggestions using request.data instead of request.body but when i used it in my views , at Ajax call it return:
TypeError: the JSON object must be str, bytes or bytearray, not
'QueryDict'
and on POSTMAN it return the error:
"detail": "Unsupported media type \"text/plain\" in request."
I'm kinda stuck on how to make this work on both POSTMAN and Ajax call, any helps would be nice
i fixed it by changing request.body to request.data and also changed my ajax code
received_json_data= request.data
and ajax code:
$.ajax({
type: "POST",
url: "/api/filters/",
beforeSend: function (xhr) {
xhr.setRequestHeader('X-CSRFToken', '{{csrf_token}}');
},
contentType: "application/json",
dataType: 'json',
data: JSON.stringify({"data": filter_data}),
success: function (data) {
if (data.code == 200) {
alert("added filter");
}
}
});

Django - Filtering objects using ajax post call data returns nothing

I have the following Ajax POST call:
$.ajax({
type: "POST",
url: "{% url 'meds:prescription' %}",
data: {selected:'selected' , csrfmiddlewaretoken: "{{ csrf_token }}"},
success: function(result) {
window.location = "{% url 'meds:prescription' %}";
}
});
Where selected is an array of ids for example [5, 9, 17]
And the following view:
class PrescriptionView(generic.ListView):
template_name = 'meds/prescription.html'
context_object_name = 'meds'
model = Medicament
def post(self, request, **kwargs):
selected_ids = self.request.POST.getlist('selected[]')
meds = self.get_queryset().filter(id__in=selected_ids)
return render(request, self.template_name, {'meds': meds})
def get_queryset(self):
ids = self.request.POST.getlist('selected[]')
return Medicament.objects.filter(id__in=ids)
def get_context_data(self, **kwargs):
ids = self.request.POST.getlist('selected[]')
context = super(PrescriptionView, self).get_context_data(**kwargs)
context.update({
'meds': Medicament.objects.filter(id__in=ids),
'date': datetime.now()
})
return context
What I'm trying to do is just to be redirected to the prescription template with the objects filtered using the data from the post call, but instead my template is just empty, I'm not sure what I'm doing wrong..
You are sending a string selected:'selected' not an array. Remove single quotes around selected in your ajax handler:
data: {selected:'selected' , csrfmiddlewaretoken: "{{ csrf_token }}"},
should be:
data: {selected: selected, csrfmiddlewaretoken: "{{ csrf_token }}"},

Categories

Resources