Uploading crop image and form using cropperjs in Django - javascript

I am using https://github.com/fengyuanchen/cropper/blob/master/README.md as the cropper function. However, I want to submit field objects (in this case the title) and the cropped image. But I got an error on the admin. And of course, I have done the makemigrations and migrate before running the server
Error Picture
admin.py
from django.contrib import admin
from .models import Image
# Register your models here.
class ImageAdmin(admin.ModelAdmin):
pass
admin.site.register(Image, ImageAdmin)
models.py
from django.db import models
# Create your models here.
class Image(models.Model):
title = models.CharField(max_length=10)
image = models.ImageField(upload_to='images')
def __str__(self):
return str(self.pk)
forms.py
from django import forms
from .models import Image
class ImageForm(forms.Form):
image = forms.ImageField()
title = forms.CharField(
max_length=10,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Title",
},
),
required=True
)
views.py
from django.shortcuts import render, redirect
from .models import Image
from .forms import ImageForm
from django.http import JsonResponse
from django.http import HttpResponseRedirect
def main_view(request):
form = ImageForm()
if request.method == "POST":
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
addimage = Image(
title=form.cleaned_data['title'],
image = form.cleaned_data['image'],
)
addimage.save()
else:
form = ImageForm()
context = {'form': form}
return render(request, 'photo_list.html', context)
photo_list.html
{% extends "base.html" %}
{% block javascript %}
<script>
console.log("Hello");
const imageBox = document.getElementById("image-box");
const confirmButton = document.getElementById("confirm-button")
const input = document.getElementById("id_image");
const csrf = document.getElementsByName("csrfmiddlewaretoken")
const imageForm = document.getElementById("image-form")
input.addEventListener("change", () => {
console.log("change")
const img_data = input.files[0]
const url = URL.createObjectURL(img_data)
imageBox.innerHTML = `<img src="${url}" id="image" width="500px">`
var $image = $('#image');
$image.cropper({
aspectRatio: 16 / 9,
crop: function (event) {
console.log(event.detail.x);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
}
});
// Get the Cropper.js instance after initialized
var cropper = $image.data('cropper');
confirmButton.addEventListener('click', () => {
cropper.getCroppedCanvas().toBlob((blob) => {
const fd = new FormData()
fd.append('csrfmiddlewaretoken', csrf[0].value)
fd.append('image', blob, 'my-image.png')
console.log("append pass")
$.ajax({
type: "POST",
url: imageForm.action,
enctype: 'multipart/form-data',
data: fd,
success: function (response) {
console.log(response)
},
error: function (error) {
console.log(error)
},
cache: false,
contentType: false,
processData: false,
})
})
})
});
</script>
{% endblock %}
{% block page_content %}
<form action="/cropimage/" id="image-form" method="POST">
{% csrf_token %}
{{form}}
{% comment %} <button class="btn" id="confirm-button"> confirm </button> {% endcomment %}
<input class="btn btn-lg btn-primary btn-block" type="submit" value="Submit" id="confirm-button">
</form>
<div id="image-box" class="mb-3"> </div>
{% endblock %}
I was following this tutorial https://www.youtube.com/watch?v=oWd7SAuCIRM
Basically, I am making an album of images recorded with title, but this time with cropperjs. Any solutions or suggestions would be appreciated :).

Ahh silly me, after a week of finding solutions, I finally realize that I did not put and after deleting some of the database cache.
fd.append('title', $("input[name='title']").val())
on the getCroppedCanvas()
For those who had the same problem, please..
Ref :
How to append more data in FormData for django?
OperationalError, no such column. Django
https://developer.mozilla.org/en-US/docs/Web/API/FormData/append

Related

JAVASCRIPT: After sending Item to Database (POST) the window.location.reload() does not work. How to refresh the Data in HTML-Template?

I recode a Tutorial on Youtube.
Django, Python, HTML an Javascript.
Everthing works fine exept the window.location.reload() function.
I try some workarounds with
windows.reload(true),
window.href = window.href
location = self.location
and some more.
I have a hunch that the reload is executed before or during the code before the reload. But I do not know.
The goal is to send the data from the input to the database and only then refresh the page.
This ist the Code from the tutorial:
index.html (shortened)
<body>
<header>
<h1>Shoppinglist</h1>
<div id="input-field">
<label for="item-input">Was möchtest du einkaufen?</label>
<input type="text" name="item" id="item-input">
</div>
<button id="btn" onclick="addItem()">+</button>
</header>
<div id="item-table">
{% for row in all_items %}
<div class="list-item">
<input type="checkbox"> {{row.name}}
</div>
{% endfor %}
</div>
<script>
function addItem(){
let itemName = document.getElementById("item-input").value;
let formData = new FormData();
let token = '{{csrf_token}}';
formData.append('itemName', itemName);
formData.append('csrfmiddlewaretoken', token);
fetch('/mylist/', {
method: 'POST',
body: formData
});
window.location.reload();
};
</script>
</body>
</html>
views.py
from django.shortcuts import render
from .models import ShoppingItem
# Create your views here.
def mylist(request):
if request.method == 'POST':
print('Received date: ', request.POST['itemName'])
ShoppingItem.objects.create(name = request.POST['itemName'])
all_items = ShoppingItem.objects.filter(done = 0)
return render(request, 'index.html', {'all_items':all_items})
models.py
from django.db import models
from datetime import date
#Create your models here.
class ShoppingItem(models.Model):
creatDate = models.DateField (default=date.today)
name = models.CharField (max_length =200)
done = models.BooleanField(default=False)
def __str__(self):
return '(' + str(self.id) +') ' +self.name
Try this:
async function addItem() {
let itemName = document.getElementById("item-input").value;
let formData = new FormData();
let token = "{{csrf_token}}";
formData.append("itemName", itemName);
formData.append("csrfmiddlewaretoken", token);
await fetch("/mylist/", {
method: "POST",
body: formData,
});
window.location.reload();
}

How do I upload a webcam image using JavaScript ajax to a Django site?

I am creating a livestreaming site using Django and I need to upload an image using an ajax post request to a model form on the django site. I am working with the following code:
Models:
from django.db import models
from django.contrib.auth.models import User
class Camera(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='camera')
image = models.ImageField(upload_to='live/', null=True, blank=True)
Views:
from .models import Camera
from .forms import CameraForm
#login_required
#csrf_exempt
def golive(request):
cameras = Camera.objects.filter(user=request.user)
camera = None
if cameras.count() == 0:
camera = Camera.objects.create(user=request.user)
camera.save()
else:
camera = cameras.first()
if request.method == 'POST':
print(request.FILES)
form = CameraForm(request.POST, request.FILES, instance=camera)
if form.is_valid():
form.save()
print("Working")
return redirect('live:live')
return render(request, 'live/golive.html', {'object': request.user.camera, 'form': CameraForm()})
#login_required
def live(request, username):
profile = get_object_or_404(Profile, user__username=username, identity_verified=True, vendor=True)
cameras = Camera.objects.filter(user=profile.user)
return render(request, 'live/live.html', {'profile': profile, 'camera': cameras.first()})
Templates:
{% extends 'base.html' %}
{% block content %}
<h1>Go live</h1>
<div id="container">
<video autoplay="true" id="video">
</video>
<canvas id="canvas" width="1028" height="728">
</canvas>
<button id="capture"></button>
</div>
{% endblock %}
{% block javascript %}
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
function capture(){
ctx.drawImage(video, 0, 0, 1024, 728);
ctx.save();
canvas.toBlob(function(blob){
console.log(blob);
var fileOfBlob = new File([blob], 'camera.png');
var formData = new FormData();
formData.append('image', fileOfBlob, 'camera.png');
$.ajax({
url: window.location.pathname,
type: 'POST',
data: formData,
success: function (response) {
console.log("Captured image.")
},
cache: false,
contentType: false,
processData: false
});
},'image/png');
}
setInterval(capture, 10000);
function startup() {
navigator.mediaDevices.getUserMedia({video: true, audio: false})
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.log("An error occurred: " + err);
});
}
startup();
{% endblock %}
Forms:
from django import forms
from .models import Camera
class CameraForm(forms.ModelForm):
class Meta:
model = Camera
fields = ('image',)
The webcam renders on the page just fine but when I try to render
{{ camera.image.url }}
I see that the image has not been uploaded. How do I upload a blob image using ajax to a django site? Is there something I am missing? This is the error I am getting:
django.http.multipartparser.MultiPartParserError: Invalid boundary in multipart: None
This is sort of out of the box, but this is the solution I came up with. I wrote my own basic codec using only blobs, which uses a setInterval to collect images at a short interval and uploads them to the server every 5 seconds. I then load the images and split them, using another setInterval to display them. This is the code:
Models:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Camera(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='camera')
src = models.TextField(default="", null=True, blank=True)
last_frame = models.DateTimeField(default=timezone.now)
Views:
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from users.models import Profile
from django.contrib import messages
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt
import datetime
from django.utils import timezone
from django.core.paginator import Paginator
from django.utils.decorators import method_decorator
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.decorators import user_passes_test
from feed.tests import identity_verified
from vendors.tests import is_vendor
from django.core.exceptions import PermissionDenied
from django.http import StreamingHttpResponse
from django.http import HttpResponse
from django.core.files.storage import FileSystemStorage
import uuid
import imghdr
import os
from django.conf import settings
from .models import Camera
from django.core.files import File
from django.core.files.base import ContentFile
from django.core.files.temp import NamedTemporaryFile
from django import forms
import traceback
from urllib.request import urlopen
#login_required
#user_passes_test(identity_verified, login_url='/verify/', redirect_field_name='next')
#user_passes_test(is_vendor)
#csrf_exempt
def golive(request):
cameras = Camera.objects.filter(user=request.user)
camera = None
if cameras.count() == 0:
camera = Camera.objects.create(user=request.user)
camera.save()
else:
camera = cameras.first()
if request.method == 'POST':
try:
p = ''
for key, value in request.POST.items():
value = value.split('\n')
p = p + value[2] + value[3] + '*'
p = p.replace(' ', '+')
camera.last_frame = timezone.now()
camera.src = p
camera.save()
except:
print(traceback.format_exc())
return HttpResponse(status=200)
return render(request, 'live/golive.html', {'object': request.user.camera})
#login_required
#user_passes_test(identity_verified, login_url='/verify/', redirect_field_name='next')
#csrf_exempt
def live(request, username):
profile = get_object_or_404(Profile, user__username=username, identity_verified=True, vendor=True)
cameras = Camera.objects.filter(user=profile.user)
return render(request, 'live/live.html', {'profile': profile, 'camera': cameras.first()})
#login_required
#user_passes_test(identity_verified, login_url='/verify/', redirect_field_name='next')
#csrf_exempt
def last_frame(request, username):
profile = get_object_or_404(Profile, user__username=username, identity_verified=True, vendor=True)
cameras = Camera.objects.filter(user=profile.user)
return render(request, 'live/lastframe.html', {'profile': profile, 'camera': cameras.first()})
#login_required
#user_passes_test(identity_verified, login_url='/verify/', redirect_field_name='next')
#csrf_exempt
def frame(request, username):
profile = get_object_or_404(Profile, user__username=username, identity_verified=True, vendor=True)
cameras = Camera.objects.filter(user=profile.user)
return render(request, 'live/liveframe.html', {'profile': profile, 'camera': cameras.first()})
Templates:
-- The template for viewing the live feed:
{% extends 'base.html' %}
{% block content %}
<h1>#{{ profile.user.username }}'s Live Feed</h1>
<div id="lastFrame"></div>
<div id="container">
<canvas id="videoCanvas" width="512" height="364">
</canvas>
<iframe src="/chat/{{ profile.user.username }}/?hidenavbar=t" width="100%" height="500px">
</iframe>
{% endblock %}
{% block javascript %}
var canvas = document.getElementById("videoCanvas");
var lastFrame = document.getElementById("lastFrame");
if(window.innerWidth > window.innerHeight) {
canvas.width = parseInt(window.innerWidth * 0.90);
canvas.height = parseInt(canvas.width * 364/512);
} else {
canvas.width = parseInt(window.innerWidth * 0.60);
canvas.height = parseInt(canvas.width * 364/512);
}
let xhr = new XMLHttpRequest();
let xhr2 = new XMLHttpRequest();
function loadListener2(){
if (xhr2.readyState === xhr2.DONE) {
if(!xhr2.responseText.includes("Error 500")){
lastFrame.innerHTML = xhr2.responseText;
}
}
}
function loadListener(){
if (xhr.readyState === xhr.DONE) {
var data = xhr.responseText.substring(0, xhr.responseText.length)
data = data.replace(' ', '+');
data = data.replace(' ', '+');
data = data.replace('\n','');
if(!data.includes("Error 500")){
var images = data.split("*");
var count = 0;
var interval = setInterval(function(){
canvas.renderImage(images[count]);
count++;
}, {{ record_interval }});
setTimeout(function(){
clearInterval(interval);
}, 5000);
}
}
}
function render() {
xhr.addEventListener("load", loadListener);
xhr.open("POST", window.location.pathname + "frame/", true);
xhr.send(null);
xhr2.addEventListener("load", loadListener2);
xhr2.open("POST", window.location.pathname + "frame/last/", true)
xhr2.send(null);
}
setInterval(render, 5000);
HTMLCanvasElement.prototype.renderImage = function(blob){
var ctx = this.getContext('2d');
var img = new Image();
img.onload = function(){
ctx.drawImage(img, 0, 0, canvas.getBoundingClientRect().width, canvas.getBoundingClientRect().height);
ctx.save();
};
img.src = blob;
};
render();
{% endblock %}
-- The template for transmitting it
{% extends 'base.html' %}
{% block content %}
<h1>Go live</h1>
<div id="container">
<video autoplay="true" id="video" width="100%">
</video>
<canvas id="canvas" width="512" height="364" style="visibility: hidden;">
</canvas>
</div>
{% endblock %}
{% block javascript %}
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var image = document.getElementById('image');
var form = document.getElementById('input-form');
var ctx = canvas.getContext('2d');
var data;
function drawImage(){
ctx.drawImage(video, 0, 0, 512, 364);
ctx.save();
data = canvas.toDataURL('image/png').replace(' ', '+')
data = data.replace('\n','');
}
function capture(){
//console.log(data);
var dataArray = "";
var formData = new FormData();
var count = 0;
var recordInterval = setInterval(function(){
drawImage();
dataArray = dataArray + data + "*";
count++;
}, {{ record_interval }});
setTimeout(function(){
formData.append("image", new Blob([dataArray], {'type': 'image/png'}), "image.png");
clearInterval(recordInterval);
$.ajax({
url: window.location.pathname,
type: 'POST',
data: formData,
success: function (response) {
console.log("Captured image.")
},
cache: false,
processData: false
});
}, 5000);
}
setInterval(capture, 5000);
function startup() {
navigator.mediaDevices.getUserMedia({video: true, audio: false})
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.log("An error occurred: " + err);
});
}
startup();
{% endblock %}
I know I'm using the .replace several times, but this is because the image needs to be in URL format so I need to replace newlines with '', spaces with +, and then the blob will render correctly. Pasting a printed blob into the browser to test works well too! But for best practice, I am using the replace in multiple places to format the blob correctly.

Django URLs not working properly in template

I am making social network website where user can post and like the post. On one of my templates, image of heart (which represents if post is liked or not) is not loading because of incorrect path: this is the incorrect one: other_profile/static/network/nolike.png. It should be: static/network/nolike.png. other_profile is the name and path of my template. Same happens in case of other API fetch calls on my website. URL should not begin with other_profile. And this happens only on other_profile HTML page.
The code:
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("profile", views.profile, name="profile"),
path("login", views.login_view, name="login"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
path("Post/<str:post_id>", views.likes, name="likes"),
path("follows/<str:user_id>", views.follows, name="follows"),
path("following", views.following, name="following"),
path("other_profile/<str:user_id>", views.other_profile, name="other_profile")
]
views.py snippet
import json
from urllib.request import Request
from django.contrib.auth import authenticate, login, logout
from django.db import IntegrityError
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, render
from django.urls import reverse
from .forms import NewPostForm, likeForm, followForm
from .models import User, Post, Likes, Follows
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.core.exceptions import ObjectDoesNotExist
from datetime import datetime
def other_profile(request, user_id):
followform = followForm(request.POST or None)
if request.method == "POST":
try:
Follows.objects.filter(following__in=[user_id]).get(following=request.user).delete()
except Follows.DoesNotExist:
if followform.is_valid():
follows = followform.save(commit=False)
follows.user = User.objects.get(id=user_id)
follows.save()
follows.following.add(request.user)
posts = Post.objects.filter(user = user_id).order_by('-date')
return render(request, "network/otherprofile.html", {
"posts":posts, "followform":followform})
otherprofile.html
{% extends "network/layout.html" %}
{% block body %}
TODO
{% if user.is_authenticated %}
<form method="POST">{% csrf_token %}
{{ followform }}
<button id="follow">Follow</button>
</form>
<div id="posts">
{% for post in posts %}
{{ post.user.username }}
<div class="name" id = "{{ post.user.id }}"><div>{{ post.date }}</div>{{ post.post }}</div>
<div id ="heart"></div>
{% endfor %}
</div>
{% endif %}
<script>
document.querySelector("#follow").addEventListener('click', () => {
var requestfollow = new Request(
`follows/${document.querySelector(".name").id}`,
{
method: 'POST',
}
);
fetch(requestfollow).then((response) => response.json()).then((data) => {
if (data.user_id == post.user.id) {
document.querySelector("#follow").innerHTML="Unfollow"
}})})
</script>
{% endblock %}
js file
document.addEventListener('DOMContentLoaded', function() {
var heart = new Image();
heart.id = "main";
heart.style.animationPlayState = 'paused';
var allButtons = document.querySelector('#posts').childElementCount;
var content = document.querySelectorAll('.name');
var button = document.querySelectorAll('#heart');
var csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
for (i = 0; i < allButtons / 2 - 1; i++) {
function listen(i) {
var requestPOST = new Request(
`/Post/${content[i].id}`,
{
method: 'POST',
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin' // Do not send CSRF token to another domain.
}
);
var requestGET = new Request(
`/Post/${content[i].id}`,
{
method: 'GET',
}
);
var heart = new Image();
heart.style.animationPlayState = 'paused';
heart.id = "main";
fetch(requestGET).then((response) => response.json()).then((data) => {
console.log(data.like)
if (data.like == true) {
heart.src = "static/network/like.png"
}
else{
heart.src = "static/network/nolike.png"
}
button[i].appendChild(heart);
})
button[i].addEventListener('click', () =>
fetch(requestPOST).then((response) => {
if (response.ok) {
response.json().then((data) => {
console.log(data.like)
if (data.like == true) {
heart.src = "static/network/like.png"
}
else{
heart.src = "static/network/nolike.png"
}
button[i].appendChild(heart);
heart.style.animationPlayState = 'running';
})}
else {
api(i)
console.log("not ok")
}}))}listen(i)}})
I also have a js file in static files. If that is needed let me know.
Help would be greatly appreciated!
the django way of handling static files is to use the static tag
{% static 'network/nolike.png' %}
but please be aware to setup static stuff in settings.py
https://docs.djangoproject.com/en/4.0/howto/static-files/
heart.src = "/static/network/like.png"
with the slash "/" at the beginning. Or better:
heart.src = "{% static 'network/like.png' %}"

Why Django modelform is submitted with enter key

I'm using Django modelform and it's submitted by pressing the enter key. This is what I wanted, but I don't know why it works. I didn't add any JS codes related to keydown but other codes to practice Ajax. Also, I found out that when there's only one input inside the form, it's submitted with the enter key, but my form has two inputs.
What I'm doing is to add a comment on a post like instagram. I used Ajax to create a comment instance.
models.py
class Comment(models.Model):
parent_post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments")
author = models.CharField(max_length=10)
content = models.CharField(max_length=100)
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
exclude = ["parent_post"]
HTML
{% for post in posts %}
<form method="POST" data-id="{{post.id}}" class="d-flex align-items-center w-100 mt-2">
{% load widget_tweaks %} <!-- this is a django package for easy CSS -->
{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.author.errors }}
{{ form.author|add_class:"form__author"|attr:"placeholder:name" }}
</div>
<div class="fieldWrapper w-100">
{{ form.content.errors }}
{{ form.content|add_class:"form__content"|attr:"placeholder:content" }}
</div>
<button class="btn btn-sm btn-warning w-auto">Write</button>
</form>
{% endfor %}
JS (Here I didn't put the codes after getting JSON response from views.py)
const forms = document.querySelectorAll("form");
forms.forEach((value) => {
const post_id = Number(value.getAttribute("data-id"));
value.addEventListener("submit", (e) => {
e.preventDefault(); // prevents reload, still submits form data but views.py does nothing for this data
writeComment(post_id);
});
const requestWrite = new XMLHttpRequest();
const writeComment = (post_id) => {
const form = document.querySelector(`form[data-id="${post_id}"]`);
const author = form.querySelector(".form__author");
const content = form.querySelector(".form__content");
const url = "/write/";
if (author.value && content.value) {
requestWrite.open("POST", url, true);
requestWrite.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded"
);
requestWrite.send(
JSON.stringify({
post_id: post_id,
author: author.value,
content: content.value,
})
);
author.value = null;
content.value = null;
}
};
views.py
#csrf_exempt
def write(request):
req = json.loads(request.body)
post_id = req["post_id"]
author = req["author"]
content = req["content"]
comment = Comment.objects.create(parent_post=get_object_or_404(Post, id=post_id), author=author, content=content)
return JsonResponse({"post_id":post_id, "comment_id":getattr(comment, "id"), "author":author, "content":content})
Thank you!

Passing Django model properties to JavaScript with the Fetch API

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.

Categories

Resources