Posts not showing author who wrote it in Django - python

Basically, I'm writing an app in which people can make blog and image posts. So far, I've completed users to be able to write text posts. However, when I try to create a post, it returns "By: None" when it should be returning "By: shrey". In this case, Bob is the author. Here's an image:
Here's an image for the post creation view:
Theoretically, when I enter a post it should say who it was written by.
Here's the template for the create post:
{% extends "social/base.html" %}
{% load crispy_forms_tags %}
{% block content4 %}
<h1>Make Your Post</h1>
<p>Write a post / Share an image</p>
<br>
<div class="container">
<form method="post">
{% csrf_token %}
{{form|crispy}}
<button type="submit" name="button">Make Post</button>
</form>
</div>
{% endblock content4 %}
Here's the function for the create post view:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.author = self.request.user
print(self.request.user)
return super().form_valid(form)
Thank you in advance.
EDIT: Home Page Template (template which displays the posts):
{% extends "social/base.html" %}
{% block content %}
<h1>Your Feed</h1>
<p>This is your feed. Here, you'll see posts from people you follow.</p>
{% if user.is_authenticated %}
<p>You are logged in as {{user.username}}. This is your feed.</p>
{% else %}
<p>You are not logged in. This is a random feed.</p>
{% endif %}
{% for post in posts %}
<h1>{{ post.post_title }}</h1>
<p>By {{ post.post_author }} on <i>{{ post.post_date }}</i></p>
<p>{{ post.post_text_content }}</p>
{% endfor %}
Click here to make a post.
<br>
Click here to logout.
<br>
Click here to login.
<br>
Click here to sign up and make an account.
<!--<p>Want to post something? Enter your info here: </p> -->
{% endblock content %}
Posts Model:
class Posts(models.Model):
post_title = models.CharField(max_length = 40, help_text = 'Enter post title')
post_text_content = models.TextField(max_length = 1000)
post_author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
post_date = models.DateField(auto_now = True, auto_now_add = False)
#Make optional Image Field
class Meta:
ordering = ['post_title', 'post_author', 'post_date', 'post_text_content']
def __str__(self):
return self.post_title
def get_absolute_url(self):
return reverse('social-home')

The name of the field is post_author, not author, hence you should set post_author:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.post_author = self.request.user
return super().form_valid(form)
That being said, normally in Django one does not prefixes the model fields with the name of the model. One reason not to do that is that you can define abstract models where you define the field once, and then use inheritance to add the field to other models.

Related

How to display images of a particular Blog post?

models.py
class PostModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date_time = models.DateTimeField(auto_now_add=True)
title = models.TextField(null=True)
body = models.TextField(null=True)
def __str__(self):
return str(self.user)
class ImagesPostModel(models.Model):
post = models.ForeignKey(PostModel, on_delete=models.CASCADE)
images = models.I
views.py
def show_posts(request):
posts = PostModel.objects.filter(user=request.user)
images = []
for post in posts:
images.append(ImagesPostModel.objects.filter(post=post))
context = {
'posts': posts,
'images': images,
}
return render(request, 'show_posts.html', context)
show_posts.html
{% extends 'base.html' %}
{% block content %}
{% for post in posts %}
{{post.title}}
<br>
{{post.body}}
<br>
{{post.data_time}}
<br>
{% for imgs in images %}
{% for image in imgs %}
{{image.post_id}}
<img src="{{image.images.url}}" alt="postimage" style="width: 300px;">
<br>
{% endfor %}
{% endfor %}
<hr>
<hr>
{% endfor %}
{% endblock %}
I want the images of a post to be displayed which are related to that post only. But here all the images combined for all posts of a particular user are being displayed each time in every post. How to resolve it?
You put yourself in that dilemma, you can fix the view code to fix that easily
def show_posts(request):
posts = PostModel.objects.filter(user=request.user)
for post in posts:
post.images = ImagesPostModel.objects.filter(post=post))
context = {
'posts': posts,
}
return render(request, 'show_posts.html', context)
and then you iterate over post.images in the template
OR you use post.images_set.all() to access the post images.
I would suggest you to use related_name for the post in your ImagePostModel. In your models.py you can do this.
class ImagesPostModel(models.Model):
post = models.ForeignKey(PostModel, on_delete=models.CASCADE, related_name="imagePost")
images = models.ImageField()
And then in your template use {{ post.imagePost.all }}
You can prefetch the images in bulk with:
def show_posts(request):
posts = PostModel.objects.filter(
user=request.user
).prefetch_related('imagespostmodel_set')
context = {
'posts': posts
}
return render(request, 'show_posts.html', context)
then in the template, you can access the .imagespostmodel_set of the post:
{% for post in posts %}
…
{% for image in post.imagespostmodel_set.all %}
{{image.post_id}}
<img src="{{image.images.url}}" alt="postimage" style="width: 300px;">
<br>
{% endfor %}
…
{% endfor %}
Note: Models normally have no Model suffix. Therefore it might be better to rename PostModel to Post.

Rendering Issue with Include template tag and forms

Disclaimer: I am aware of several other posts that raise my issue, but they do not apply to my code.
Hi everyone, I am creating a knockoff of stackoverflow using Django, and I came into an issue.
I have a View that shows a question that has been asked. Obviously, anyone coming to that question should be able to answer it. Here are the views and templates:
This view displays the question and some other info:
class QuestionsDetailView(DetailView):
model = Question
template_name = "function/questions/questions_detail.html"
def get_context_data(self, *args, **kwargs):
context = {}
question_detail = Question.objects.filter(id=self.kwargs.get("pk")).first()
answers = Answer.objects.filter(question=question_detail)
context["question"] = question_detail
context["questioncomments"] = QuestionComment.objects.filter(question=question_detail)
context["answers"] = [[answer, AnswerComment.objects.filter(answer=answer)] for answer in answers]
return context
This view allows you to answer:
class AnswersCreateView(LoginRequiredMixin, CreateView):
model = Answer
fields = ["content"]
template_name = "function/answers/answers_create.html"
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def get_success_url(self):
return reverse("function-questions-detail", kwargs={"pk": self.kwargs.get("pk")})
On stackoverflow, when you view a question, you can answer it by scrolling down and providing your answer. I wanted to do something similar, so instead of having the answer view on a separate page, I put in in the question detail page using the 'include' template tag. Here are the templates:
This is the template for the question (which contains the include statement (bottom)):
{% extends 'core/base.html' %}
{% block content %}
<!--Question-->
<h1>{{ question.title }} by {{ question.author }}</h1>
{% if user == question.author %}
Update
Delete
{% endif %}
<h3>{{ question.content }}</h3>
<!--QuestionComments-->
{% for comment in questioncomments %}
<h6>{{ comment }} -- {{ comment.author }}</h6>
{% endfor %}
<!--Answers-->
{% if answers %}
{% for pair in answers %}
<!--Answers-->
<h3>{{ pair.0.content }}</h3>
<h5>{{ pair.0.author }}</h5>
<!--AnswerComments-->
{% for comment in pair.1 %}
<h6>{{ comment }}</h6>
{% endfor %}
{% endfor %}
{% endif %}
{% include 'function/answers/answers_create.html' %}
{% endblock content %}
Here is the template for the form which allows you to create an answer (this is the template that is referenced by the include template tag):
{% load crispy_forms_tags %}
<form method="POST">
{% csrf_token %}
<h2>Your Answer</h2>
{{ form|crispy }}
<button type="submit">Submit</button>
</form>
When I tried it out on the website in a browser, the question was rendering but the answer form was rendering everything except the form fields. It is rendering 'Your Answer' and the submit button but it is not rendering the actual field.
When I press submit I get the following error in my command prompt:
Method Not Allowed (POST): /questions/3/
Method Not Allowed: /questions/3/
So, in actual fact, the problem is how do I render the form properly. And will that solve the issue?
Thanks in advance.
Use form and pass it through context in your DetailView.
#form
class AnswerCreateForm(forms.ModelForm):
class Meta:
fields = ['content']
#views
class QuestionsDetailView(DetailView):
.......
def get_context_data(self, *args, **kwargs):
......
context['form'] = AnswerCreateForm()
return context
# create view
# provide same template name as the detail view.
class AnswersCreateView(LoginRequiredMixin, CreateView):
model = Answer
form_class = AnswerCreateForm
template_name = "function/questions/question_detail.html"

How to add comments on Python Django posts with logged in username?

Please let me know that where i am making mistake?
views.py
class AddComment(LoginRequiredMixin, CreateView):
model = Comment
form_class = CommentForm
template_name = 'comment.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
form.instance.name = self.request.user
form.instance.post_id = self.kwargs\['pk'\]
return super().form_valid(form)
Are these forms written correctly?
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body', )
widgets = {
# 'name': forms.TextInput(attrs={'class': 'form-control'}),
'body': forms.Textarea(attrs={'class': 'form-control'}),
}
Should I make any changes in models?
models.py
class Comment(models.Model):
post = models.ForeignKey(Post,
related_name='comments',
on_delete=models.CASCADE)
name = models.ForeignKey(
User,
on_delete=models.CASCADE,
)
body = models.TextField(max_length=240)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.post.title, self.name)
This is comment section for letting user to comment on post with showing its own name?
comments.html
{% if not object.comments.all %}
<p>No comments yet...</p>
Add one
{% else %}
Add Comment
<br><br>
{% for comment in object.comments.all %}
<strong>{{ comment.name }} </strong> - <small>{{ comment.date_added }}</small>
<br>
{{ comment.body }}
<br><br>
<hr>
{% endfor %}
{% endif %}
Here is the urls of AddComment class view.
urls.py
path('post/<int:pk>/comment/', AddComment.as_view(), name='comment'),][1]
You did not state clearly what has gone wrong with your code. I would like to give some suggestions. First of all,
{% for comment in object.comments.all %}
...
{% endfor %}
You are putting this block of code inside {% if not object.comments.all %}...{% endif %} so it will not show on template if the comment section is not empty.
Also, this link:
Add Comment
should open a Django template form, where user can actually fill in the comment. After that, on POST request of the form, it will send comment data to the URL you put in action param of the form, as below:
<form action="{% url 'comment' post.pk %}" method="post">
[Comment code here]
</form>
which will link to this URL you provided:
path('post/<int:pk>/comment/', AddComment.as_view(), name='comment'),]
It will be better if you can provide your code in views.py as well to make it easier to track down where it goes wrong.

Django: How to add Image of User in UserPostList View

I am trying to add the profile image of each user(designer) in the below list view
For each designer, there is a profile image that has already been uploaded before I am just trying to get it and show it in the UserPost List View.
Currently, with the below code, the designer image is not showing.
Here is the views.py
class UserPostListView(ListView):
model = Post
template_name = "user_posts.html"
context_object_name = 'posts'
queryset = Post.objects.filter(admin_approved=True)
paginate_by = 6
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(designer=user, admin_approved=True).order_by('-date_posted')
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
has_items = Item.objects.filter(designer__username=self.kwargs['username']).exists()
context['has_items'] = has_items
return context
Here is the template
{% if has_items %}
<h1> Hello, this is {{ view.kwargs.username }} </h1>
--------------------------------------
<img class="profile_image" src={{ designer.profile.image.url }}> <----------- I want it to appear
{% else %}
<h1>Hello, this is {{ view.kwargs.username }} </h1>
--------------------------------------
<img class="profile_image" src={{ designer.profile.image.url }}> <----------- I want it to appear
{% endif %}
Problem
The issue seems to be that your template is missing a for loop to loop through posts so that you can access the respective designer. Also, I'm assuming that view is accessible from a post object as it's not explicitly defined anywhere else in your code example. Lastly, your else loop is doing the same as the if loop.
Solution
Include a for loop in template so that you can access designers' profile image URLs.
{% if has_items %}
{% for post in posts %}
<h1> Hello, this is {{ post.view.kwargs.username }} </h1>
--------------------------------------
<img class="profile_image" src={{ post.designer.profile.image.url }}>
{% endfor %}
{% else %}
...
{% endif %}
References
ListView Documentation https://docs.djangoproject.com/en/3.1/ref/class-based-views/generic-display/#listview
ListView Code https://github.com/django/django/blob/master/django/views/generic/list.py#L194

Django - Find out which model instance I'm editing in an inline formset

Note: I'm new to Django and Python. I'm not sure if I've misunderstood the use of inline formsets in some way.
I needed to be able to edit certain fields for multiple instances of a Guest model (linked to a parent Invite model) at the same time in one form. After a lot of back and forth, I managed to set up an inline formset that submits successfully.
However, the interface is confusing, because you don't know which Guest you're filling in details for. Each Guest has a guest_name field, and I just want to display that name for each form, so that users know who they're editing for.
Here's my (condensed) view:
def extra_view(request, code):
# Get the specific invite
invite = get_invite(code)
# Get the context from the request.
context = RequestContext(request)
# Store guests attending object
guests_attending = invite.guest_set.filter(attending=True, invite=invite)
# Create the formset for each Guest
GuestFormset = inlineformset_factory(Invite, Guest,
form=ExtraForm,
fields=('diet', 'transport'),
extra=0,
can_delete=False)
if request.method == "POST":
formset = GuestFormset(request.POST, request.FILES,
instance=invite,
queryset=Guest.objects.filter(attending=1))
if formset.is_valid():
# Save the data to the database.
formset.save()
# Redirect stuff here
if guests_attending.count() > 0:
formset = GuestFormset(instance=invite, queryset=Guest.objects.filter(attending=1))
# Return the view
return render_to_response('app/extra.html', {
'GuestForm': formset,
'invite': invite,
'guests_attending': guests_attending,
'errors': formset.errors
}, context)
else:
# Backup for if no guests are attending
Here's how the models look:
class Invite(models.Model):
# Code to identify invites by
code = models.CharField(max_length=6, default=code_generator(4, do_check=True), unique=True)
group_name = models.CharField(max_length=200)
def __str__(self):
return self.group_name
class Guest(models.Model):
invite = models.ForeignKey(Invite, on_delete=models.CASCADE)
guest_name = models.CharField(max_length=200)
diet = models.CharField(max_length=250, null=True, blank=True)
transport = models.NullBooleanField(default=False)
attending = models.NullBooleanField(default=False)
def __str__(self):
return self.guest_name
And here's my template
{% if invite %}
<form method="post" action="">
{% csrf_token %}
{{ GuestForm.management_form }}
<table>
{% for form in GuestForm %}
<!-- Which guest am I editing for?! -->
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
<div>
{{ field.help_text }}
{{ field }}
</div>
{% endfor %}
{% endfor %}
</table>
<button type="submit" class="btn">Continue</button>
</form>
{% endif %}
You can access the form's instance with form.instance. In the template, you could do something like:
{{ form.instance.guest_name }}

Categories