Display likers username in html template - python

I have added a user liking system, where a user can like posts. I have added this successfully, however can't seem to display the usernames of the people who liked the post. This is my current implementation.
models.py
class Post(models.Model):
likes = models.ManyToManyField(User, related_name='likes')
#property
def total_likes(self):
return self.likes.count()
views.py
def likepost(request, pk):
if request.method == 'POST':
user = request.user
post = get_object_or_404(Post, pk=pk)
if post.likes.filter(id=user.id).exists():
post.is_liked = True
post.likes.remove(user)
post.save()
else:
post.is_liked = False
post.likes.add(user)
post.save()
return redirect('home')
home.html
{% for post in posts.all %}
<form id="like__post" method="POST" action="{% url 'likepost' post.id %}">
{% csrf_token%}
<input type="hidden">
</form>
<div style="position: absolute; bottom:80px; left: 85px;">
{% if post.is_liked == True %}
<img src="{% static 'heartred.png' %}" width="35px" height="35px">
{% else %}
<img src="{% static 'heart.png' %}" width="33px" height="33px">
{% endif %}
{% endfor %}
<ul> {{ post.likes }} </ul>
So far. In my template i get this: auth.User.None

likes is a many to many field, you need to iterate over it.
<ul>
{% for like in post.likes.all %}
<li>{{ like.username }}</li>
{% endfor %}
</ul>

Related

The 'header_image' attribute has no file associated with it

When I create a new post with an image, everything is fine, but if I edit it, I want to delete the image using the "clear" button, then this error appears, and if I change, then nothing changes, but there are no errors
here is models.py
`
from django.db import models
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
body = models.TextField()
header_image = models.ImageField(blank=True, null=True, upload_to="images/", default='#') #new
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', args=[str(self.id)])`
here is views.py
`
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Post
class BlogListView(ListView):
model = Post
template_name = 'home.html'
class BlogDetailView(DetailView):
model = Post
template_name = 'post_detail.html'
class BlogCreateView(CreateView):
model = Post
template_name = 'post_new.html'
fields = ['title', 'author', 'body', 'header_image']
class BlogUpdateView(UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body', 'header_image']
class BlogDeleteView(DeleteView):
model = Post
template_name = 'post_delete.html'
success_url = reverse_lazy('home')
#property
def image_url(self):
"""
Return self.photo.url if self.photo is not None,
'url' exist and has a value, else, return None.
"""
if self.image:
return getattr(self.photo, 'url', None)
return None`
post_base.html
`{% load static %}
<html>
<head>
<title>Django blog</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400"
rel="stylesheet">
<link href="{% static 'css/base.css' %}" rel="stylesheet">
</head>
<body>
<div>
<header>
<div class="nav-left">
<h1>Django blog</h1>
<h2>Admin</h2>
</div>
<div class="nav-right">
+ New Blog Post
</div>
</header>
{% if user.is_authenticated %}
<p>Hi {{ user.username }}!</p>
{% else %}
<p>You are not logged in.</p>
Log In<br>
<p>Sign up</p>
{% endif %}
{% block content %}
{% endblock content %}
</div>
</body>
</html>`
post_detail.html
`
{% extends 'base.html' %}
{% block content %}
<div class="post-entry">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
<p>+ Edit Blog Post</p>
<p>+ Delete Blog Post</p>
<img src="{{ post.header_image.url|default_if_none:'#' }}">
{{ post.body|safe }}
{% endblock content %}`
post_new.html
`
{% extends 'base.html' %}
{% block content %}
<h1>New post</h1>
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
{% endblock content %}`
post_edit.html
`
{% extends 'base.html' %}
{% block content %}
<h1>Edit post</h1>
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update" />
</form>
{% endblock content %}`
enctype='multipart/form-data' means that no characters will be encoded. that is why this type is used while uploading files to server. So multipart/form-data is used when a form requires binary data, like the contents of a file, to be uploaded.
You forgot to add enctype='multipart/form-data' in your post_edit.html form and that's the reason your files aren't sent to Django. Following code should work.
post_edit.html
{% extends 'base.html' %}
{% block content %}
<h1>Edit post</h1>
<form action="" method="post" enctype='multipart/form-data'>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update" />
</form>
{% endblock content %}

How do I access images uploaded through a formset in Django?

I cannot seem to find a way to iterate through the files uploaded by the formset, although the files are uploaded as the code indicates.
When I load the page it shows 1-3 "img src=(unkown)" tags. It shows 3 when I upload 2 images and 1 when I upload 1 image.
Template view-post:
{% block body %}
<div class="container">
<div class="col-md-8">
<p>
<h1>{{ post.title }}</h1>
<h4>{{ post.body }}</h4>
<p>
Posted by {{ post.user }} on {{ post.created_date }}
{% if not post.created_date == post.updated_date %}
and updated on {{ post.updated_date }}
{% endif %}
</p>
</p>
{% for img in images %}
<img src="{{ img.url }}" width=360>
{{ img.url }}
<br>
{% endfor %}
</div>
<div class="col-4-md">
{% if post.cover_image %}
<img src="{{ post.cover_image.url }}" width=240>
{% endif %}
</div>
</div>
{% endblock %}
Here I use filter, not get, so it should give me a list of the images connected via foreignkey to the right post. The post here is displayed as it should be, along with the internal cover image and text in the post.
View view-post:
class ViewPost(TemplateView):
template_name = 'posts/view_post.html'
context_object_name = 'view_post'
def get(self, request, pk):
post = Post.objects.get(pk=pk)
images = Images.objects.filter(post=post)
args = {'post': post, 'images': images}
return render(request, self.template_name, args)
Template make-post:
{% block body %}
<div class="container">
<div class="col-md-8">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ form.errors }}
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
<button type="submit">Submit</button>
</form>
</div>
</div>
{% endblock %}
View make-post:
class MakePost(DetailView):
template_name = 'posts/make_post.html'
ImageFormSet = modelformset_factory(Images, form=ImageForm, extra=3)
def get(self, request):
form = PostForm()
formset = self.ImageFormSet(queryset=Images.objects.none())
args = {'form': form, 'formset': formset}
return render(request, self.template_name, args)
def post(self, request):
form = PostForm(request.POST, request.FILES)
formset = self.ImageFormSet(request.POST, request.FILES,
queryset=Images.objects.none())
if form.is_valid() and formset.is_valid():
post = form.save(commit=False)
post.user = request.user
post.cover_image = form.cleaned_data['cover_image']
post.title = form.cleaned_data['title']
post.body = form.cleaned_data['body']
post.save()
for imgform in formset:
image = imgform.cleaned_data.get('image')
photo = Images(post=post, post_image=image)
photo.save()
return redirect('home_space:home')
else:
return redirect('home_space:home')
Function to name files and image Model:
def get_image_filename(instance, filename):
title = instance.post.title
slug = slugify(title)
return "post_images/%s-%s" % (slug, filename)
class Images(models.Model):
post = models.ForeignKey(Post, default=None, on_delete=models.CASCADE)
post_image = models.ImageField(upload_to=get_image_filename,
verbose_name='Image')
In your template, you are accessing url of Images model instance. But, the url property is in ImageField class instance. Use img.post_image.url.
Example:
{% for img in images %}
<img src="{{ img.post_image.url }}" width=360>
{{ img.post_image.url }}
<br>
{% endfor %}

Django: can't access OneToOneField after rendering TemplateView Form

I am new to Django and don't understand what really is causing this:
I have a Model Company which has an OneToOneField, creator.
# models.py
class Company(models.Model):
class Meta:
verbose_name = 'Company'
verbose_name_plural = 'Companies'
creator = models.OneToOneField(User, related_name="company", on_delete=models.CASCADE, unique=False, null=True)
name = models.CharField(max_length=50)
I have a TemplateView class to handle get and post requests for creating a Company model:
# views.py
class create_company(TemplateView):
def get(self, request):
form = CompanyCreateForm()
title = "Some form"
return render(request, "form.html", {"form": form, "title": title})
def post(self, request):
form = CompanyCreateForm(request.POST)
if form.is_valid():
comp = form.save(commit=False)
comp.creator = request.user
comp.save()
return redirect('index')
The form is showing correctly also storing when I submit, the problem I am facing is with base.html where I show {% user.company %}; the form template extends it like:
{% extends "account/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="post" action="">
{% csrf_token %}
{{form|crispy}}
<button class="btn btn-success" type="submit">Save</button>
</form>
<br>
</div>
<br>
{% endblock %}
and in base.html I access
{% if user.is_authenticated %}
{% user.company %}
{% endif %}
But user.company is not showing even it is set; it shows only when I redirect to index but not when I render the form.
Can someone help me understand what causes this?
{% if request.user.is_authenticated %}
{% request.user.company %}
{% endif %}
you are not sending any context to the base.html, thus only user wont work.
This was the error when I simulated your code.
Error during template rendering
In template /home/user/django/drf_tutorial/snippets/templates/base.html, error at line 2
Invalid block tag on line 2: 'user.company', expected 'elif', 'else' or 'endif'. Did you forget to register or load this tag?
1 {% if user.is_authenticated %}
2 {% user.company %}
3 {% endif %}
4 {% block content %}{% endblock %}
It gives hint that the code to show company should be variable {{ }} instead of tag {% %}. So the base.html template should be as below.
{% if user.is_authenticated %}
{{ user.company }}
{% endif %}
{% block content %}{% endblock %}

django - csrf protection - markdown2

In some of my blog posts using django and markdown2, I am trying to include a form as follows:
views.py:
def post_detail(request, slug=None):
instance = get_object_or_404(Post, slug=slug)
if not instance.published:
raise Http404
return render(request, "posts/post_detail.html", {'instance': instance})
My template post_detail.html contains a {{ instance.get_markdown }} variable:
{% extends "base.html" %}
{% block body_content %}
<div>
{{ instance.get_markdown }}
</div>
{% endblock body_content %}
base.html:
<html>
<body>
{% block body_content %}
{% endblock body_content %}
</body>
</html>
models.py:
import markdown2
class Post(models.Model):
...
text = models.TextField(verbose_name=_("Text"))
...
def get_markdown(self):
return mark_safe(markdown2.markdown(self.text))
Example for a saved text in Post:
### Example
<form method = "POST">
{% csrf_token %}
First name:<input type="text" name="firstname">
<input type="submit" value="Submit">
</form>
This way, the page simply shows the string "{% csrf_token %}" within the form. How can I render the post with csrf protection?
My temporary solution is:
post_detail.html:
{% extends "base.html" %}
{% block body_content %}
<div>
{{ instance.get_markdown_text }}
</div>
{% if instance.form %}
<div>
<form method = "POST">{% csrf_token %}
{{ instance.get_markdown_form }}
</form>
</div>
{% endif %}
{% endblock body_content %}
models.py:
import markdown2
class Post(models.Model):
...
text = models.TextField(verbose_name=_("Text"))
form = models.TextField(verbose_name=_("Form"), blank=True, null=True)
...
def get_markdown_text(self):
return mark_safe(markdown2.markdown(self.text))
def get_markdown_form(self):
return mark_safe(markdown2.markdown(self.form))
I'm not very happy with this solution, because of the unnecessary field "form" and method get_markdown_form in Post and post_detail.html.

BootstrapError Parameter "form" should contain a valid Django Form

i have an app called reviews
reviews/forms.py
from django.forms import ModelForm, Textarea
from reviews.models import Review
class ReviewForm(ModelForm):
class Meta:
model = Review
fields = ['rating', 'comment']
widgets = {
'comment': Textarea(attrs={'cols': 40, 'rows': 15}),
}
reviews/views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from .models import Review, Wine
from .forms import ReviewForm
import datetime
from django.contrib.auth.decorators import login_required
#login_required
def add_review(request, wine_id):
wine = get_object_or_404(Wine, pk=wine_id)
form = ReviewForm(request.POST)
if form.is_valid():
rating = form.cleaned_data['rating']
comment = form.cleaned_data['comment']
user_name = form.cleaned_data['user_name']
user_name = request.user.username
review = Review()
review.wine = wine
review.user_name = user_name
review.rating = rating
review.comment = comment
review.pub_date = datetime.datetime.now()
review.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('reviews:wine_detail', args=(wine.id,)))
return render(request, 'reviews/wine_detail.html', {'wine': wine, 'form': form})
reviews/templates/reviews/wine_detail.html
{% extends 'base.html' %}
{% load bootstrap3 %}
{% block title %}
<h2>{{ wine.name }}</h2>
<h5>{{ wine.review_set.count }} reviews ({{ wine.average_rating | floatformat }} average rating)</h5>
{% endblock %}
{% block content %}
<h3>Recent reviews</h3>
{% if wine.review_set.all %}
<div class="row">
{% for review in wine.review_set.all %}
<div class="col-xs-6 col-lg-4">
<em>{{ review.comment }}</em>
<h6>Rated {{ review.rating }} of 5 by {{ review.user_name }}</h6>
<h5><a href="{% url 'reviews:review_detail' review.id %}">
Read more
</a></h5>
</div>
{% endfor %}
</div>
{% else %}
<p>No reviews for this wine yet</p>
{% endif %}
<h3>Add your review</h3>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'reviews:add_review' wine.id %}" method="post" class="form">
{% csrf_token %}
{% bootstrap_form form layout='inline' %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "star" %} Add
</button>
{% endbuttons %}
</form>
{% endblock %}
base.html
{% load bootstrap3 %}
{% bootstrap_css %}
{% bootstrap_javascript %}
{% block bootstrap3_content %}
<div class="container">
<nav class="navbar navbar-default">
<div class="navbar-header">
<a class="navbar-brand" href="{% url 'reviews:review_list' %}">Winerama</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>Wine list</li>
<li>Home</li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %}
<li>Hello {{ user.username }}</li>
<li>Logout</li>
{% else %}
<li>Login</li>
<li>Register</li>
{% endif %}
</ul>
</div>
</nav>
<h1>{% block title %}(no title){% endblock %}</h1>
{% bootstrap_messages %}
{% block content %}(no content){% endblock %}
</div>
{% endblock %}
I am getting the error at the line {% bootstrap_form form layout='inline' %} in the html file
Any idea how to fix this?
There's a few problems with your code as it stands, so I'll try to clean it up with some comments as I would write it to add a review to a wine.
#login_required
def add_review(request, wine_id):
wine = get_object_or_404(Wine, pk=wine_id)
if request.POST:
form = ReviewForm(request.POST)
else:
form = ReviewForm()
if form.is_valid():
### NO NEED FOR - already set as part of valid modelform ::: rating = form.cleaned_data['rating']
### AS WELL AS ::: comment = form.cleaned_data['comment']
### THIS IS NOT A FIELD IN YOUR FORM :::user_name = form.cleaned_data['user_name']
user_name = request.user.username
review = form.save(commit=False) # commit = False means that this instantiate but not save a Review model object
review.wine = wine
review.user_name = user_name # Why use this instead of a ForeignKey to user?
review.pub_date = datetime.datetime.now() # works as long as pub_date is a DateTimeField
review.save() # save to the DB now
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('reviews:wine_detail', args=(wine.id,))) # THIS will redirect only upon form save
return render(request, 'reviews/wine_detail.html', {'wine': wine, 'form': form})
Now, the error your seeing is most likely related to you passing request.POST to a form even if request.POST is blank; the form will attempt to set initial values but with a querydict that has no values that actually relates to the form.
EDIT: In response to your comments, my next step would be to try and render each form field individually and see if I can trigger a failure.
Instead of {% bootstrap_form form layout='inline' %}, try-
{% for field in form %}
{% bootstrap_field field %}
{% endfor %}
If this is an error with the django-bootstrap library trying to render the textarea widget and the inline style together (as I would suspect at this point), you can also eliminate the widget parameter and see if there's a fix. If there is, I'd suggest overriding your modelform's init method for assign a widget post a call super on init.
In Class Base View
This error may occur when you use form_class in the wrong generic view.
⮕ Open your views.py then check to see if you have set the wrong generic view in your class.
Example
class ProfileUpdateView(T̶e̶m̶p̶l̶a̶t̶e̶V̶i̶e̶w̶ UpdateView):
model = User
form_class = forms.ProfileForm
success_url = reverse_lazy("stories:story_list")
template_name = 'profile.html'
def get_object(self, queryset=None):
return get_object_or_404(User, pk=self.request.user.id)

Categories

Resources