I am new to Django and Javascript. I would like to create an edit section on a site, where people can edit a post that already exists. Similar to Facebook or Twitter.
When I click 'edit' on my post, in the console I am getting:
network.js:28 POST http://127.0.0.1:8000/edit_post/ 404 (Not Found)
I've tried many things but cannot get it to work. Any help is appreciated.
I think the issue might have to do with the use of 'post' and 'text.'
'text' is in my model, used to signify the text of the actual post.
views.py:
#login_required
#csrf_exempt
def edit_post(request):
if request.method == "POST":
post_id = request.POST.get('id')
new_post = request.POST.get('text')
try:
post = Post.objects.get(id=post_id)
if post.user == request.user:
post.text = new_post.strip()
post.save()
return JsonResponse({}, status=201)
except:
return JsonResponse({}, status=404)
return JsonResponse({}, status=400)
models.py
class User(AbstractUser):
pass
class Post(models.Model):
text = models.TextField(max_length=500, blank=True,
null=True)
username = models.ForeignKey('User',
on_delete=models.CASCADE, related_name='author',
null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(
User, blank=True, related_name="liked_user")
def __str__(self):
return self.user.username
Javascript file:
edit = document.querySelectorAll(".edit");
text_area = document.querySelectorAll(".textarea");
edit.forEach((element) => {
element.addEventListener("click", () => {
edit_handeler(element);
});
});
text_area.forEach((element) => {
element.addEventListener("keyup", (e) => {
if (e.keyCode == 13 && e.shiftKey) return;
if (e.keyCode === 13) edit_handeler(element);
});
});
function edit_post(id, post) {
form = new FormData();
form.append("id", id);
form.append("text", text.trim());
fetch("/edit_post/", {
method: "POST",
body: form,
}).then((res) => {
document.querySelector(`#post-content-${id}`).textContent = post;
document.querySelector(`#post-content-${id}`).style.display =
"block";
document.querySelector(`#post-edit-${id}`).style.display = "none";
document.querySelector(`#post-edit-${id}`).value = text.trim();
});
}
function edit_handeler(element) {
id = element.getAttribute("data-id");
edit_btn = document.querySelector(`#edit-btn-${id}`);
if (edit_btn.textContent == "Edit") {
document.querySelector(`#post-content-${id}`).style.display =
"none";
document.querySelector(`#post-edit-${id}`).style.display =
"block";
edit_btn.textContent = "Save";
edit_btn.setAttribute("class", "text-success edit");
} else if (edit_btn.textContent == "Save") {
edit_post(id, document.querySelector(`#post-edit-${id}`).value);
edit_btn.textContent = "Edit";
edit_btn.setAttribute("class", "text-primary edit");
}
}
Part of html file:
{% if user1 == user2 %}
<span class="text-primary edit" data-id="{{i.id}}"
id="edit-btn-{{i.id}}">Edit</span>
<br><br>
{% endif %}
<span id="post-content-{{i.id}}" class="post"
{{i.post}}</span>
<textarea data-id="{{i.id}}" id="post-edit-{{i.id}}"
style="display:none;" class="form-control textarea" row="3">{{i.text}}</textarea>
urls.py
urlpatterns = [
path("", views.index, name="index"),
path("make_post", views.make_post, name="make_post"),
path("fol/<str:user>", views.fol, name="fol"),
path("following_list/<str:username>", views.following_list,
name='following_list'),
path('like/', views.like),
path('edit_post/', views.edit_post),
path("profile/<str:username>", views.profile, name="profile"),
path("login", views.login_view, name="login"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
]
Related
I’m a beginner. I have tried everything in the Django E-commerce website course, but it does not work for me. I also tried documentation but I didn’t get any solution. I have this error when I go to /update_item/ and the data is not showing up in the terminal:
Expecting value: line 1 column 1 (char 0)
error screenshot
tutorial link
tutorial link
https://youtu.be/woORrr3QNh8
cart.js
var updateBtns = document.getElementsByClassName('update-cart')
for (i = 0; i < updateBtns.length; i++) {
updateBtns[i].addEventListener('click', function(){
var productId = this.dataset.product
var action = this.dataset.action
console.log('productId:', productId, 'Action:', action)
console.log('USER:', user)
})
}
function updateUserOrder(productId, action){
console.log('User is authenticated, sending data...')
var url = '/update_item/'
fetch(url, {
method:'POST',
headers:{
'Content-Type':'application/json',
'X-CSRFToken':csrftoken,
},
body:JSON.stringify({'productId':productId, 'action':action})
})
.then((response) => {
return response.json();
})
.then((data) => {
location.reload()
});
}
views.py
def updateItem(request):
data = json.loads(request.body)
productId = data['productId']
action = data['action']
print("Action",action)
print("Pordutcs:",productId)
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.quantitiy +1)
elif action == 'remove':
orderitem.quantity = (orderitem.quantity -1)
orderitem.save()
if orderitem.quantity <= 0:
orderitem.delete()
return JsonResponse("Item was added", safe=False)
store.html
{% extends 'store/main.html' %}
{% load static %}
{% block content %}
<div class="row">
{%for i in products %}
<!-- {{i.image.url}} -->
<div class="col-lg-4">
<!-- <img class="thumbnail" src="{{i.image.url}}" alt="sorry"> -->
<img class="thumbnail" src="static{{i.imageURL}}">
<!-- {% static 'my_app/example.jpg' %} -->
<div class="box-element product">
<h6><strong>{{i.name}}</strong></h6>
<hr>
<button data-product="{{i.id}}" data-action='add' class="btn btn-outline-secondary add-btn updatecart">Add
to Cart</button>
<a class="btn btn-outline-success" href="#">View</a>
<h4 style="display: inline-block; float:right"><strong>Rs {{i.price|floatformat:2}}</strong></h4>
</div>
</div>
{% endfor %}
<!-- <img src="static/images/robot.jpg"> -->
</div>
{% endblock content %}
I tried making a dummy django project with the code you provided to see if I counter such error.
Following JS code I used:
function updateUserOrder(){
console.log('User is authenticated, sending data...')
var url = '/update_item/'
fetch(url, {
method:'POST',
headers:{
'Content-Type':'application/json',
},
body:JSON.stringify({'productId':5, 'action':'Add'})
})
.then((response) => {
return response.json();
})
.then((data) => {
location.reload()
});
}
I gave dummy data to productId and action.
Then my views.py goes like this:
from django.shortcuts import render
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def updateItem(request):
data = json.loads(request.body)
productId = data['productId']
action = data['action']
print("Action",action)
print("Pordutcs:",productId)
return JsonResponse("Item was added", safe=False)
Not much with html part, it was just a click button to call the js code.
<button onclick="updateUserOrder()">Add</button>
And worked like a charm, following is the screenshot of my output in django server:
I would suggest you to try running with the same code.
If error is still there, try giving dummy data to productId and action in js code to make sure if there is a problem with the data in productId and action.
I assume you've solved this issue otherwise,...
if you're using Django version 4.0
change the url for the cart.js to update_item, cart.js should look like this
cart.js:
var updateBtns = document.getElementsByClassName('update-cart')
for (i = 0; i < updateBtns.length; i++) {
updateBtns[i].addEventListener('click', function(){
var productId = this.dataset.product
var action = this.dataset.action
console.log('productId:', productId, 'Action:', action)
console.log('USER:', user)
})
}
function updateUserOrder(productId, action){
console.log('User is authenticated, sending data...')
var url = 'update_item/'
fetch(url, {
method:'POST',
headers:{
'Content-Type':'application/json',
'X-CSRFToken':csrftoken,
},
body:JSON.stringify({'productId':productId, 'action':action})
})
.then((response) => {
return response.json();
})
.then((data) => {
location.reload()
});
}
then import csrf_exempt decorator , your views should look like this
views.py(after adding "from django.views.decorators.csrf import csrf_exempt" to the top of your file)
#csrf_exempt
def updateItem(request):
data = json.loads(request.body)
productId = data['productId']
action = data['action']
print("Action",action)
print("Pordutcs:",productId)
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.quantitiy +1)
elif action == 'remove':
orderitem.quantity = (orderitem.quantity -1)
orderitem.save()
if orderitem.quantity <= 0:
orderitem.delete()
return JsonResponse("Item was added", safe=False)
Then Clear Your Cache and try adding the item to cart again...it should work this time
I'm trying to build a follow and unfollow button, I want to show the button only For any other user who is signed in, a user should not be able to follow themselves.
How can I return from my views.py the currently logged-in user as a JSON format to my JS file?
I'm building the button in the JavaScript file and not in the template, I tried to return as JSON format but didn't succeed
views.py
def display_profile(request, profile):
try:
profile_to_display = User.objects.get(username=profile)
profile_to_display_id = User.objects.get(pk=profile_to_display.id)
except User.DoesNotExist:
return JsonResponse({"error": "Profile not found."}, status=404)
# Return profile contents
if request.method == "GET":
return JsonResponse(profile_to_display.profile.serialize(), safe=False)
else:
return JsonResponse({
"error": "GET or PUT request required."
}, status=400)
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
pass
class NewPost(models.Model):
poster = models.ForeignKey("User", on_delete=models.PROTECT, related_name="posts_posted")
description = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
likes = models.IntegerField(default=0)
def serialize(self):
return {
"id": self.id,
"poster": self.poster.username,
"description": self.description,
"date_added": self.date_added.strftime("%b %d %Y, %I:%M %p"),
"likes": self.likes
}
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
following = models.ManyToManyField(User, blank=True, related_name="following")
followers = models.ManyToManyField(User, blank=True, related_name="followers")
def serialize(self):
return {
"profileID": self.user.id,
"following": int(self.following.all().count()),
"followers": int(self.followers.all().count()),
}
index.js
function load_user_info(user_clicked_on){
document.querySelector('#page-view').style.display = 'none';
document.querySelector('#posts-view').style.display = 'none';
document.querySelector('#show-posts').style.display = 'none';
document.querySelector('#load-profile').style.display = 'block';
fetch(`/profile/${user_clicked_on}`)
.then(response => response.json())
.then(profile => {
const profile_element = document.createElement('div');
const followers = document.createElement('div');
const following = document.createElement('div');
const follow_button = document.createElement('button');
followers.innerHTML = 'Followers: ' + profile.followers;
following.innerHTML = 'Following: ' + profile.following;
if (profile.profileID == ?? ){
follow_button.innerHTML = 'Sameuser';
}else{
follow_button.innerHTML = 'Not same user';
}
profile_element.appendChild(followers);
profile_element.appendChild(following);
profile_element.appendChild(follow_button);
profile_element.classList.add('profile_element');
document.querySelector('#user-profile').appendChild(profile_element);
});
document.querySelector('#user-profile').innerHTML = `<h3>${user_clicked_on.charAt(0).toUpperCase() + user_clicked_on.slice(1)} Profile</h3>`;
}
I think all you need is the detail of the logged in user right?
may be you can get it from the request object by using
{{request.user}}
this returns the logged user instance
{{request.user.username}} {{request.user.email}}
this returns username and email
I dont know if you can directly use it in javascript , but you can kind of save this in template using a hidden field lik
<input type="hidden" value="{{request.user.username}}" id='user_detail'>
and grab it using id selector (getElementById, or jquery)
$('#user_detail').val()
I'm working on an assignment to use the fetch API to do some of the normal things we would have Python do in our views with JavaScript such as adding records or querying the database. One issue I'm running across is passing the normal properties we would see in Django, say a user or username, where it just shows up as a literal user id when I pull it from the sql database with the fetch API. With the views, html and JavaScript I have written now, how would I go about pulling the username with fetch in JavaScript that I can normally grab with a variable or view with a print statement in the Django console, instead of just viewing the user id from the database. I feel like I'm missing a step and I'm just not seeing it.
urls
app_name = "network"
urlpatterns = [
path("", views.index, name="index"),
path("login", views.login_view, name="login"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
# API Routes
path("addpost", views.post, name="post"),
path("<str:navbar_item>", views.viewposts, name="viewposts"),
]
models.py
class User(AbstractUser):
pass
class Profile(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
bio = models.TextField(null=True, blank=True)
# pics
website = models.CharField(max_length=225, null=True, blank=True)
follower = models.ManyToManyField(
User, blank=True, related_name="followed_user") # user following this profile
# profile user that follows this profile
following = models.ManyToManyField(
User, blank=True, related_name="following_user")
def __str__(self):
return f"{self.user}'s' profile id is {self.id}"
def following_users(self):
for username in self.following:
return username
def get_absolute_url(self):
return reverse("network:profile-detail", args=[str(self.id)])
class Post(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name="post_user")
body = models.TextField(max_length=1000)
timestamp = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField(User, blank=True, related_name="post_likes")
def __str__(self):
return f"{self.created_by} posted {self.body}"
def get_absolute_url(self):
return reverse("network:post-detail", args=[str(self.id)])
def total_likes(self):
return self.likes.count()
class Meta:
ordering = ["-timestamp"]
views.py
def index(request):
if request.user.is_authenticated:
return render(request, "network/index.html", {})
else:
return HttpResponseRedirect(reverse("network:login"))
#login_required
def post(request):
# Composing a new post must be done via POST
if request.method != "POST":
return JsonResponse({"error": "You must POST your request."}, status=404)
try:
data = json.loads(request.body)
body = data.get("body", "")
user = request.user
print(user)
post = Post(created_by=user, body=body)
# post = Post(created_by=Profile.objects.get(user=user), body=body)
post.save()
except AttributeError:
return JsonResponse({"error": "AttributeError thrown."}, status=500)
return JsonResponse({"message": "Post created."}, status=201)
#login_required
def viewposts(request, navbar_item):
if navbar_item == "viewposts":
posts = Post.objects.all()
posts = posts.order_by("-timestamp")
json_post = serialize("json", posts)
print(posts)
return HttpResponse(json_post, content_type="application/json")
else:
return JsonResponse({"error": "Invalid page."}, status=400)
index.html
{% extends "network/layout.html" %}
{% load static %}
{% block body %}
<div class="container p-5">
{% if error %}
{{ error }}
{% endif %}
<h1 class="display-4">All Posts</h1>
<div class="form-group border rounded p-4">
<h2 class="diplay-3">New Post</h2>
<form id="addpost" class="form-group pt-5">
{% csrf_token %}
<div class="form-group">
<textarea class="form-control" id="body" placeholder="Add post here..."></textarea>
</div>
<input type="submit" class="btn btn-primary"/>
</form>
</div>
<div id="all-posts" class="all-posts">
</div>
</div>
{% endblock %}
{% block script %}
<script src="{% static 'network/main.js' %}"></script>
{% endblock %}
JavaScript
// Post on index page # API Routes /addpost
const addPost = () => {
const addPostUrl = '/addpost';
const csrftoken = getCookie('csrftoken');
const body = document.querySelector('#body').value;
// body needs to be passed into an object before using the stringify method
const bodyObject = { body };
fetch(addPostUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify(bodyObject)
})
.then(response => response.json())
.then(result => {
console.log(result);
})
.catch(error => {
console.log(error);
});
return false;
};
// Load posts in index page # API Routes /navbar_item
function loadPosts(navItem, event) {
preventPageLoad(event);
const postUrl = `/${navItem}`;
// Send a GET request to the URL to retrieve all posts
fetch(postUrl)
.then(response => response.json())
.then(data => {
data.forEach(post => {
const { fields } = post;
const allPostsContainer = document.querySelector("#all-posts");
const element = document.createElement('div');
const postId = `#post-${fields.id}`;
element.style.textDecoration = 'none';
element.classList.add('HoverClass1');
element.setAttribute('id', `post-${fields.id}`);
element.classList.add('d-flex', 'flex-column' ,'justify-content-between', 'p-4', 'm-3', 'lead', 'border', 'rounded');
element.style.color = '#000000';
element.innerHTML =
// This is returning an id
`<div class="bd-highlight font-weight-bolder mr-5">${fields.created_by}</div>
<div class="bd-highlight">${fields.timestamp}</div>
<div class="flex-fill bd-highlight">${fields.body}</div>`;
console.log(fields);
allPostsContainer.append(element);
const linePost = document.querySelector(postId);
linePost.addEventListener('click', (event) => {
console.log(event);
});
});
})
.catch(error => {
console.log(error);
});
return false;
}
Images showing my admin console in Django versus the browser console and what fetch is pulling in JavaScript. You'll see in the admin console we can view the username, but in the browser console all I'm getting is the user id with fetch.
I figured out how to do this. I added a serialize method to the Post model to convert these properties to JSON.
def serialize(self):
return {
'id': self.id,
'created_by': self.created_by.username,
'body': self.body,
'timestamp': self.timestamp.strftime('%b %-d %Y, %-I:%M %p'),
'likes': self.total_likes()
}
Then in views.py, in my viewposts function, instead of my the HttpResponse, I used JsonResponse and passed the model's serialize method as an argument.
#login_required
def viewposts(request, navbar_item):
if navbar_item == "viewposts":
posts = Post.objects.all()
posts = posts.order_by("-timestamp")
return JsonResponse([post.serialize() for post in posts], safe=False)
else:
return JsonResponse({"error": "Invalid page."}, status=400)
This allowed me to not have to deconstruct anything in my JavaScript file. So I could pull any attributes from my query using dot notation directly off of the data model in fetch.
My views.py file
def change_profile_picture(request):
if not request.user.is_authenticated:
return JsonResponse({"status": "403", "msg": "Unauthorized request"})
if request.method == 'POST':
if request.is_ajax():
form = UploadPictureForm(request.POST)
if form.is_valid():
customuser = request.user.customuser
customuser.picture = form.cleaned_data['picture']
customuser.save()
return JsonResponse(
{
"status": "200",
"msg": "Successful",
"data": f"{customuser.picture.url}"
}
)
else:
return JsonResponse({"status": "400", "msg": "invalid form"})
else:
return JsonResponse({"status": "401", "msg": "Bad request"})
else:
return JsonResponse({"status": "403", "msg": "invalid request method"})
forms.py
class UploadPictureForm(forms.Form):
picture = forms.ImageField(
label="Profile picture"
)
Javascript code:
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;
}
$('.upload-btn').on('click', () => {
document.getElementById('id_picture').click();
})
$('.close-btn').on('click', () => {
document.getElementById('profile-picture').src = $('#profile-picture').data('target');
document.getElementById('submit-picture').style.visibility = 'hidden';
})
$('#submit-picture').on('click', () => {
var picture = document.querySelector('input[type="file"]');
var formData = new FormData();
formData.append('csrfmiddlewaretoken', getCookie('csrftoken'));
formData.append('picture', picture.files[0]);
fetch('/auth/change-profile-picture/', {
method: 'POST',
body: formData,
cache: 'default',
mode: 'cors',
credentials: 'include',
headers: {
"X-Requested-With": "XMLHttpRequest",
}
})
.then((res) => res.json())
.then((data) => {
if(data.status === '200') {
document.getElementById('profile-picture').src = data.data;
alert("done");
} else {
console.log(data.msg);
}
})
.catch((err) => console.log(err))
})
HTML template:
<div class="col-md-6 order-sm-12 my-5">
<p class="upload-btn"><i class="fa fa-camera"></i> Edit profile picture</p>
<p class="close-btn"><i class="fas fa-times"></i> Reset</p>
<img class="profile-img" id="profile-picture" data-target="{% if user.customuser.picture %}{{ user.customuser.picture.url }}{% endif %}" src="{% if user.customuser.picture %}{{ user.customuser.picture.url }}{% endif %}" />
{% if user == request.user %}
<input id="id_picture" style="visibility:hidden;" name="picture" type="file" accept="image/*" required onchange="document.getElementById('profile-picture').src = window.URL.createObjectURL(this.files[0]);document.getElementById('submit-picture').style.visibility = 'visible';">
<button class="btn vizew-btn w-100 my-3" style="visibility:hidden;" id="submit-picture">Change profile picture</button>
{% endif %}
</div>
The view function, change_profile_picture returns invalid form and I do not know why. So far, I've noticed that the picture file is not actually sent with the fetch api. I've also tried changing from using Javascript to submit to using normal HTML form tag, it still gives the same error. Changing inherited form class to ModelForm only populates the field with a default. How do I fix it?
I found out in the change_profile view, where I initialized the form with the data submitted, I did not populate the form with request.FILES like so:
<code>
if request.method == 'POST':
if request.is_ajax():
form = UploadPictureForm(request.POST, request.FILES)
<more code>
My model works good when page makes POST request but when it makes it makes ajax request it stops saving an image( I can not change image to another), rest fields works good. What can be the problem?
Here is my model:
class Contact(models.Model):
name = models.CharField(max_length=40, blank=False)
last_name = models.CharField(max_length=40, blank=False)
date_of_birth = models.DateField(blank=True, null=True)
bio = models.TextField(blank=True)
contacts = models.TextField(blank=True)
email = models.EmailField(blank=True)
jabber = models.CharField(max_length=40)
skype = models.CharField(max_length=40)
other_contacts = models.TextField(blank=True)
photo = models.ImageField(upload_to='photo/', null=True)
Ajax function:
$(function(){
var form = $('#contact_form');
form.submit(function(e){
$('#sendbutton').attr('disabled', true)
$('#ajax-progress-indicator').show();
$('#ajaxwrapper').load('/1/edit/' + '#ajaxwrapper',
form.serializeArray(), function(responseText, responseStatus) {
$('sendbutton').attr('disabled', false)
$('#ajax-progress-indicator').hide();
});
e.preventDefault();
});
});
Urls:
urlpatterns = patterns(
'',
# Examples:
url(r'^$', 'apps.hello.views.home', name='home'),
)
from .settings import MEDIA_ROOT, DEBUG
if DEBUG:
# serve files from media folder
urlpatterns += patterns('',
url(r'^uploads/(?P<path>.*)$', 'django.views.static.serve', {
'document_root': MEDIA_ROOT}))
Form:
class ContactForm(forms.ModelForm):
"""
The model for Contact model edit
"""
# other_contacts = forms.CharField(widget=PreformattedTextWidget)
class Meta:
model = Contact
fields = ['name', 'last_name', 'date_of_birth', 'bio', 'contacts',
'email', 'jabber', 'skype', 'other_contacts', 'photo']
widgets = {
'skype': forms.Textarea(attrs={'placeholder': 'Enter your skype ID'}),
# 'other_contacts': PreformattedTextWidget()
}
View class:
class ContactUpdateView(UpdateView):
model = Contact
form_class = ContactForm
template_name = 'edit.html'
def get_success_url(self):
return reverse('home')
def post(self, request, *args, **kwargs):
if request.POST.get('cancel_button'):
return HttpResponseRedirect(
u'%s?status_message=Canceled!'
%reverse('home'))
else:
return super(ContactUpdateView, self).post(request, *args,
**kwargs)
def get_ajax(self, request, *args, **kwargs):
return render(request, 'home.html')