Getting profile picture of blogposter in Django - python

When I display a blog post in HTML using a for loop
{% for post in posts %}
<div class="blogpost">
<h3><strong>{{post.title}}</strong></h3>
<img class="thumbnail" src="{{author.imageURL}}">
<h7>{{post.date}} - {{post.author}}</h7>
<br>
<hr>
<br>
{{post.context|linebreaks}}<br><br>
</div>
<br>
{% endfor %}
it works perfectly fine, except the authors profile picture does NOT get displayed.
I get the posts by getting all posts in my views.py from my models.py. The thing is that the profile picture of the user posting the blog isn't stored in the "post" model in the database. It is stored in the "Customers". Everybody should be able to read and post blogs. The admin (me) can later delete unwanted posts.
I have tried making a for loop using an array key:value in JavaScript. That is not very secure, because everybody just gets all users and all profilepictures through the whole database. That might not be a good idea.
This is my models.py
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank = True)
name = models.CharField(max_length=200, null=True)
email = models.EmailField(max_length=200, null=True)
about = models.CharField(max_length=100, null=True)
image = models.ImageField(null=True, blank=True)
#property
def imageURL(self):
try:
url = self.image.url
except:
url = 'placeholder.png'
return url
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=200, null=True)
context = models.TextField(max_length=250, blank=True,
validators=[MaxLengthValidator(250)])
author = models.CharField(max_length=200, null=True)
from datetime import date
date = models.DateField(("Date"), default=date.today)
views.py
def homepage(request):
if request.user.is_authenticated: # Everybody has to be logged in
posts = Post.objects.all()
authors = Customer.objects.filter()
context = {'posts':posts, 'authors':authors}
return render(request, 'store/homepage.html', context)
That means that I want the user to see the persons profile picture in the corner of the blog post.

First I think you need to link the author in the Post model to a Customer with a foreignkey:
Then you can acces author fields like
{{ post.author.xyz }}
Currently your context contains authors as a queryset:
authors = Customer.objects.filter()
context = {'posts':posts, 'authors':authors}
But in your template you use it as if it were a single Customer:
<img class="thumbnail" src="{{author.imageURL}}">
And be aware of the typo "author" and "authors"

It is unclear what you meant by {{author.imageURL}} as author is QuerySet( that results in collection of authors).
You probably meant the related author to post which would be post.author
Other than that you should pass image URL to template and not ImageField string representation as documented
So it should be something in a line of
{{ post.author.imageURL.url }}

Related

How to retrieve data using id or pk in django

I am working on a project but after trying multiple ways I still couldn't find the solution. I need to get the current room on the page so that user can leave room, I am aware that it has to do with making queries or using pk,id. (My 1st question ever, sorry if it is not correct way to ask).
This is the code from Views.py:
def leave_room(request, room):
room = request.GET['room.pk']
room_details = Room.objects.get(name=room)
messages = Messages.objects.filter(room=room_details.pk)
membership = RoomMember.objects.filter(user=request.user, room=messages)
membership.delete()
return redirect("/")
urls.py:
path("leave/<int:pk>/join/", views.leave_room, name="leave_room"),
html:
<a class="btn btn-danger" href="{% url 'leave_room' pk=room.pk %}">Leave Room</a>
models.py:
class Room(models.Model):
name = models.CharField(max_length=100)
about = models.TextField(max_length=500, null=True, blank=True)
creator = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='room_creator')
members = models.ManyToManyField(User, through="RoomMember")
class RoomMember(models.Model):
approved = models.BooleanField(default=False, blank=False)
room = models.ForeignKey(Room, related_name='memberships', on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name='user_groups', on_delete=models.CASCADE)
class Messages(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=False, blank=False)
text = models.CharField(max_length=10000, blank=False, null=False)
date = models.DateTimeField(default=datetime.now)
room = models.ForeignKey(Room, null=True, blank=False, on_delete=models.CASCADE)
Since you have a parameter <int:pk> the primary key of the room is passed through the pk parameter. This thus means that leave_room should be implemented as:
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
#require_POST
#login_required
def leave_room(request, pk):
nitems, __ = RoomMember.objects.filter(
user=request.user, room_id=pk
).delete()
if not nitems:
raise Http404
else:
return redirect('name-of-some-view')
The removal link is thus a mini form to make a POST request to the view:
<form method="post" action="{% url 'leave_room' pk=room.pk %}">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Leave Room</button>
</form>
Note: Section 9 of the HTTP protocol
specifies that requests like GET and HEAD should not have side-effects, so you
should not change entities with such requests. Normally POST, PUT, PATCH, and
DELETE requests are used for this. In that case you make a small <form> that
will trigger a POST request, or you use some AJAX calls.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

Django Forum App, comments don't update on user-side, but can be seen through admin

For reference, here are my models in my Forum app:
class Forum(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('forum-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
forum = models.ForeignKey(Forum, on_delete=models.CASCADE)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
To display the forum posts, I have a CBV 'ForumListView':
class ForumListView(ListView):
model = Forum
template_name = 'forum/forum.html'
context_object_name = 'forum_posts'
ordering = ['-created_at']
From this list, the user can click on any forum and it will lead them to 'forum-detail' with the CBV 'ForumDetailView':
class ForumDetailView(DetailView):
model = Forum
extra_context = {
'comments': Comment.objects.all().order_by('-created_at')}
Here is where I passed in the comments from my Comment model to be shown alongside the post. I think this is the reason why the comments don't update, but I'm not too sure how to fix this.
In the template for forum_detail.html, this is how I display all the comments made:
{% for comment in comments %}
{% if comment.forum == forum %}
<div class="content-section">
<p>{{ comment.description }}</p>
<small>{{ comment.user.username }}, on {{ comment.created_at|date:"F d, Y" }}</small>
</div>
{% endif %}
{% endfor %}
Note that the new comment made will be shown if I re-run
python manage.py runserver
and sometimes the new comment appears after a few minutes of waiting/refreshing the page.
Also, I think function-based views may fix this, however I have coded my entire app with CBVs and would love a fix that doesn't involve re-coding everything!
Any help is greatly appreciated, and can provide more info/code if needed!
Putting it in extra_context like this will cause the queryset to be evaluated when you define the view, and any values that it has at that time will be the only values that the view will get. That's why it is working when you restart the server. So it should be dynamic and fetched every time a new request comes. In that case you need to put it in get_context_data
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.all().order_by('-created_at')}
return context

Django Templates Display The Latest 3 Comments

I have this code in django that, for every django comment, does whatever is inside the for loop:
{% for comment in post.comments.all %}
{% endfor %}
This code, for every comment, does whatever is inside the for. To count the comments I use this:
{{ post.comments.count }}
This is my comment model in case you need it:
class Comment(models.Model):
post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField(default='')
created_date = models.DateTimeField(default=timezone.now)
anonymous = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
Now, I want to sort of combine these together, so it shows only the latest 3 comments and only runs the for loop 3 times or less. (Please note that I want to do all of this work in the templates, and not in the views.py file)
You can order the comments by default by the creation date with:
from django.conf import settings
class Comment(models.Model):
post = models.ForeignKey(
'blog.Post',
on_delete=models.CASCADE,
related_name='comments'
)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
text = models.TextField(default='')
created_date = models.DateTimeField(auto_now_add=True)
anonymous = models.BooleanField(default=False)
class Meta:
ordering = ['-created_date']
# …
Then you can work with the |slice template filter [Django-doc] to slice the queryset:
{% for comment in post.comments.all|slice:':3' %}
{% endfor %}
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
It is however better to prefetch the elements in the view, with:
Post.objects.prefetch_related('comments')
this will fetch the comments in bulk, which is more efficient than running a query per Post object.

Unable to add a comment on my Django blog

I've been trying to add comments to the forum on my website and I've managed to get to the stage of being able to see the comment form. But, when you actually submit the comment form, I get the below error.
FYI, 'What Good Marketing Events are There in 2020?' is the name of the post I'm trying to add a comment too.
Here are the models used:
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments')
user_name = models.CharField(max_length=250)
email = models.EmailField()
content = models.TextField()
created_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user_name
class Post(models.Model):
creator_id = models.ForeignKey(User, null=False, default=1)
title = models.CharField(max_length=200)
content = models.TextField()
created_date = models.DateTimeField(auto_now_add=True)
published_date = models.DateTimeField(blank=True, null=True, default=timezone.now)
views = models.IntegerField(default=0)
category = models.CharField(choices=CATEGORY_CHOICES, max_length=30, blank=True, null=True)
image = models.ImageField(upload_to="img", blank=True, null=True)
def __str__(self):
return self.title
Here is the view used:
def comment_post_form(request, pk):
""" Create a view that allows us to add a comment """
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentPostForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('forum_post_details.html', {'post': post})
else:
form = CommentPostForm()
return render(request, 'comment_post_form.html', {'form': form})
Here is the form used:
class CommentPostForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
Here is the 'for' statement used in the template:
{% for comment in comments %}
<p>{{ comment.created_date }}</p>
<p>{{ comment.user_name }}</p>
<p>{{ comment.content }}</p>
{% empty %}
<p>No comments added. Why not be the first!</p>
{% endfor %}
Any help is greatly appreciated and if you need anything else, please let me know :)
The way you are redirecting is not how you do it. You can use the redirect() function in a number of ways.
url.py
path('teacherprofile/', views.teacher_profile_view, name='teacher-profile'),
views.py
By passing the name of a view
return redirect('teacher-profile')
By passing a hardcoded URL to redirect to:
return redirect(teacherprofile/)
This also works with full URLs:
return redirect('https://example.com/')
This way the user is redirect the html page itself.

Problems with passing data from view to template?

I am new to Django Python and I am learning how to use Django and passing data from view to template. Now, here is my situation and I really need some help in understanding where i can be doing wrong.
I am trying to pass data from view to template and then parse the object in the view but for some reason nothing is happening in the template. I have printed the registration object in my views.py and it works fine and displays the information that is correct. But when I send the registration object from views to template nothing happens.
models.py
from django.db import models
from datetime import datetime
from django.shortcuts import redirect
# Create your models here.
# Create your models here.
class Registration(models.Model):
first_name = models.CharField(max_length=255, null=True, blank=True)
last_name = models.CharField(max_length=255, null=True, blank=True)
email = models.CharField(max_length=255, null=True, blank=True)
password = models.CharField(max_length=255, null=True, blank=True)
mobilenumber = models.CharField(max_length=255, null=True, blank=True)
created_on = models.DateTimeField(auto_now_add=True, blank=True)
class Meta:
ordering = ('first_name',)
views.py
class Loginview(CreateView):
model = Registration
form_class = LoginForm
template_name = "loginvalentis/valentis_login.html"
def get(self, request):
form = LoginForm()
# returning form
return render(request, 'loginvalentis/valentis_login.html', {'form': form});
def form_valid(self,form):
user_email = form.cleaned_data.get('email')
user_password = form.cleaned_data.get('password')
try:
registration = Registration.objects.get(email=user_email)
print ("registration",registration.mobilenumber)
return redirect('/loginvalentis/home/',{'registration':registration})
except Registration.DoesNotExist:
user_info = None
return redirect('/loginvalentis/login/')
Template result.html --- ('/loginvalentis/home/')
<html>
<body>
<form id="form1">
{% csrf_token %}
<div>
hello world
<form id ="form1">
<ul>
{% for user in registration %}
<li>{{ user.mobilenumber }}</li>
{% endfor %}
</ul>
</form>
</div>
</form>
</body>
</html>
Your problem is with the redirect() function. You are trying to pass the registration object through it, but it doesn't support this, it's *args and **kwargs are simply a parameters for reversing the url, see here:
https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#django.shortcuts.redirect
You should use some other way to pass it to another view, e.g. pass only it's id as a parameter of that view's url (you will have to change the url conf appropriatelly), the other way is to use sessions etc.
see:
https://docs.djangoproject.com/en/2.0/topics/http/sessions/
https://docs.djangoproject.com/en/2.0/topics/http/urls/
But really, it would be much easier for you just to walk through this tutorial very carefully
https://docs.djangoproject.com/en/2.0/intro/tutorial01/ trust me, it will be really worth your time, because from your question I can easily tell that you just don't understand what you are doing.

Categories