Django: How to add Image of User in UserPostList View - python

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

Related

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"

Posts not showing author who wrote it in Django

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.

Django pagination with ListView and django-filter not working properly

I am using django-filter in a ListView which is causing problems with pagination. In my listview, when the page is loaded the pagination appears correct and shows
page links for pages 1-7 at the bottom. However when I go to any of these pages it displays all model instances. For example, I click on page 2, the url is appended with ?page=2 and as I set paginate-by as 20 I expect objects with ids 21-40.
I know the problem is caused by the django-filter, since when I remove this from 'get_context_data' the pagination works correctly. I am only using 'self.queryset' as an argument in the django-filter object so I cannot work out why this is happening. The html for the pagination is contained in a base template which I extend in the templates for the ListView subclasses.
Any suggestions on why the pagination is not working correctly when I include the django-filter in the context data and/or what I should try in order to fix it would be greatly appreciated.
Base ListView and a subclass:
class BaseCompoundListView(ListView):
queryset = Compound.objects.all()
template_name = 'compounds/compound_list.html'
paginate_by = 20
def get_context_data(self, **kwargs):
context = super(BaseCompoundListView, self).get_context_data(**kwargs)
context['odor_types'] = OdorType.objects.values('term')
compound_filter = CompoundFilter(self.request.GET, queryset=self.queryset)
context['compound_filter'] = compound_filter
return context
class CompoundListView(BaseCompoundListView):
def get_context_data(self, **kwargs):
context = super(CompoundListView, self).get_context_data(**kwargs)
context['page_header'] = 'All compounds'
return context
The base template:
{% block content %}{% endblock %}
{% block pagination %}
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
previous
% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>
{% endif %}
{% endblock %}

Django management form missing or tampered with - management form is in the template

I have a template with lots of forms on it, all wrapped in one form element. I have on MultiForm that is comprised of 4 regular forms, and two formsets. The formsets have been overridden to use custom formset classes.
I render the management forms in the templates, and can see the relevant info in the post.
For the formsets, I initialize the page with only one form visible.
When I try to submit the combined form I get the following error:
ManagementForm data is missing or has been tampered with
I have searched everywhere for the answer, and read about 15 posts on stack overflow with the same error, but none of the solutions seem to help.
The error page highlights the following line:
{{ beneficiaries.management_form }}
Template:
<form class='pension_form' id='implementation_form' action="{% url "confirmation_form" %}" method="post">
{% csrf_token %}
<ul>
{{ the_form.user_info.as_ul }}
</ul>
<ul>
{{ the_form.spouse_info.as_ul }}
</ul>
<div class='formset_container'> {{ children.management_form }}
{% for form in children %}
<div class='formset'><ul>{{ form.as_ul }} </ul><a class="glyphicon glyphicon-plus"></a></div>
{% endfor %}
</div>
<ul>
{{ the_form.employer_info.as_ul }}
</ul>
<ul>
<li>{{ the_form.beneficiary_general.WHO_BENEFITS }}</li>
</ul>
<div id='beneficiary_info_container' style='display:none;'>
<div class='formset_container'>
{{ beneficiaries.management_form }}
{% for form in beneficiaries %}
<div class='formset' >
<ul>{{ form.as_ul }}</ul><a class="glyphicon glyphicon-plus"></a></div>
{% endfor %}
</div>
<ul><li id='inheritance_order'>
{{ the_form.beneficiary_general.BENEFICIARIES_DIE.label_tag }}
{{ the_form.beneficiary_general.BENEFICIARIES_DIE }}
</li>
</ul>
</div>
<button class='btn btn-default main-btn'>{% trans "_Continue" %}
</form>
View:
def show_confirmation_form(request):
ChildFormSet = formset_factory(ChildInfo, formset=ChildInfoFormSet,
extra=14, can_delete=True)
BeneficiaryFormSet = formset_factory(BeneficiaryInfo, formset=BeneficiaryInfoFormSet,
extra=10, can_delete=True)
multi_form_prefix = 'main_form'
child_prefix = 'children_info'
beneficiary_prefix = 'beneficiary_info'
if request.method == 'POST':
form = ConfirmationForm(request.POST, prefix=multi_form_prefix)
children_forms = ChildFormSet(request.POST, prefix=child_prefix)
beneficary_forms = BeneficiaryFormSet(request.POST,
prefix=beneficiary_prefix)
if form.is_valid():
#not ready yet
return HttpResponseRedirect('/thanks/')
else:
form = ConfirmationForm(prefix=multi_form_prefix)
children_forms = ChildFormSet(prefix=child_prefix)
beneficary_forms = BeneficiaryFormSet(prefix=beneficiary_prefix)
context = {'the_form' : form, 'children' : children_forms,
'beneficiaries' : beneficary_forms}
return render(request, "confirmation_form.html", context)
Forms.py
class BeneficiaryInfo(forms.Form):
SHEM_PRATI_MUTAV = forms.CharField(label=_("First_Name"))
SHEM_MISHPACHA_MUTAV = forms.CharField(label=_("Last_Name"))
MISPAR_ZEHUT_MUTAV = forms.IntegerField(label=_("Mispar_Zehut"))
ACHUZ_HALUKA = forms.IntegerField(label=_("Percent_Allocate"))
class BeneficiaryInfoFormSet(BaseFormSet):
def clean(self):
"""
Adds validation to check that no two links have the same anchor or URL
and that all links have both an anchor and URL.
"""
if any(self.errors):
return
teudot_zehut = []
distribution_total = 0
for form in self.forms:
if form.cleaned_data:
teudat_zehut = form.cleaned_data['MISPAR_ZEHUT_MUTAV']
#allow empty forms.
if teudat_zehut:
if teudat_zehut in teudot_zehut:
form.add_error(None, 'No mutavim can share teudot_zehut')
distribution_total += int(form.cleaned_data['ACHUZ_HALUKA'])
if distribution_total != 100:
form.add_error(None, 'Distribution Total must be 100')
In case someone runs into a similar problem:
The problem was the I only showed the formsets if a certain checkbox was checked, and the management form was in the hidden area. I moved it out of the div that was hidden and it worked perfectly.

django - What does this line achieve here?

I'm following a tutorial on effectivedjango.com, and this is the code they have:
views.py:
class CreateContactView(CreateView):
model = Contact
template_name = 'edit_contact.html'
fields = '__all__' #this is needed for error msg Using ModelFormMixin (base class of CreateContactView) without the 'fields' attribute is prohibited.
def get_success_url(self):
return reverse('contacts-list')
def get_context_data(self, **kwargs):
context = super(CreateContactView, self).get_context_data(**kwargs)
context['action'] = reverse('contacts-new')
return context
class UpdateContactView(UpdateView):
model = Contact
template_name = 'edit_contact.html'
fields = '__all__'
def get_success_url(self):
return reverse('contacts-list')
def get_context_data(self, **kwargs):
context = super(UpdateContactView, self).get_context_data(**kwargs)
context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id})
return context
urls.py:
url(r'^$', contacts.views.ListContactView.as_view(),
name='contacts-list',),
url(r'^new$', contacts.views.CreateContactView.as_view(),
name='contacts-new',),
url(r'^edit/(?P<pk>\d+)/$', contacts.views.UpdateContactView.as_view(),
name='contacts-edit',),
contact_list.html:
{% block content %}
<h1>Contacts</h1>
<ul>
{% for contact in object_list %}
<li class="contact">
{{ contact }}
(edit)
</li>
{% endfor %}
</ul>
Add contact
{% endblock %}
edit_contact.html:
{% block content %}
{% if contact.id %}
<h1>Edit Contact</h1>
{% else %}
<h1>Add Contact</h1>
{% endif %}
<form action="{{ action }}" method="POST">
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input id="save_contact" type="submit" value="Save" />
</form>
Back to list
{% if contact.id %}
Delete
{% endif %}
{% endblock %}
Why does the line context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id}) in views.py look like its calling itself?
What I mean is, this action is called when the submit button is pressed in the contact-edit template, correct? So it starts there, and it is reverse-calling contact-edit which is itself, right?
What am I not seeing here?
Thank you for all your help.
Yes, the line context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id}) in views.py is calling itself. This line generates the proper url for contacts-edit view.
This is done so that POST requests come to the same view i.e. UpdateContactView which is an UpdateView. There, proper handling will be done i.e. form validation will occur with the sent data. If the form is valid, object will be updated. Otherwise, the form will be displayed again with errors.
Django docs on UpdateView:
A view that displays a form for editing an existing object,
redisplaying the form with validation errors (if there are any) and
saving changes to the object.

Categories