I am currently faced with a challenge, when a user gets authenticated they can see objects posted by other users they are following. The challenge here is I want the authenticated user to click on the name of a user following them and it automatically takes then to the a page containing all the posts by that user they clicked on. So far my code keeps showing me just the posts by the authenticated user only.
#Views.py
def article(request):
return render(request, 'article.html',
{'posts': post.objects.filter(user = request.user),'form' : CommentForm()})
#article.html
{% for post in posts %}
<div class = "username">
<font size="4.5">{{post.user}}</font>
</div>
<div class = "postbody">
<p>{{ post.body|lower|truncatewords:50 }}</p>
<p>{{post.likes}} people liked this article</a></p>
</div>
<p>likes {{post.likes}} </p>
{% endfor %}
{% endif %}
I appreciate any help thanks.
Change this line
post.objects.filter(user = request.user)
to filter on some other user object most likely like this
post.objects.filter(user__pk=user_id)
For a more complete solution your urls.py should look like this
url(r'^article/(?P<user_id>)/$', views.article),
in your views.py, accept the user_id as a new parameter
def article(request, user_id):
return render(request, 'article.html',
{'posts': post.objects.filter(user__pk=user_id),'form' : CommentForm()})
Related
I am building a simple social network in django.
In the "home" of my social, I have the list of all posts published by all users, with author and publishing date. Under each post of the logged user, a "delete" button appears. If the user clicks on it, it returns a specific view of that post, with a message "do you really wish to delete this post?" and two buttons to confirm or cancel the post deletion.
However, as I click on button "confirm deletion", the page reloads to the same point, and nothing changes except for the fact that
?csrfmiddlewaretoken=--random-sequence--
appears at the end of the current url in the urlbar.
What am I missing?
Here is my template:
<h3>Do you want to delete this post?</h3>
<div class="posts">
{% include "posts/_post.html" with post=object hide_delete=True %}
</div>
<form class="POST">
{% csrf_token %}
<input type="submit" value="Confirm Delete" class="btn btn-danger btn-large">
Cancel
</form>
and my DeletePost view based on generic.DeleteView:
class DeletePost(LoginRequiredMixin, SelectRelatedMixin, generic.DeleteView):
model = models.Post
select_related = ('user', 'group')
success_url = reverse_lazy('posts:all')
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user_id = self.request.user.id)
def delete(self,*args,**kwargs):
messages.success(self.request,'Post Deleted')
return super().delete(*args,**kwargs)
You currently have
<form class="POST">
It should be
<form method="POST">
When method is missing, the browser does a GET request by default, so you see the form values in the URL querystring.
I'm trying to get the form to create the fields based on what exam page the user is on. In the error page, all local variables have the correct value for form and view, but I keep getting ExamQuestion object not iterable and an error at line 0 of the template. It also highlights the render() at line 44 in the view as the source of the problem. If I change line 28 from exam__name=exam_name to exam__name="exam_name", basically turning the variable into a str, the page runs but no data is passed.
In the error console choice_list shows querysets as individual list items as it should for forms.py
How do I make the object ExamQuestion iterable? I've been stumped for a week now. I've written a hundred ways at this point.
I know it's listing questions instead of answers for the questions, I'm just trying to get it to load ANY queryset and freaking run at this point.
view
def exampage(request, exam_name):
exams = Exam.objects.all()
questionlist = ExamQuestion.objects.filter(exam__name=exam_name)
choicelist = ExamChoice.objects.filter(question__exam__name=exam_name)
form = ExamTest(request.POST, exam_name=exam_name)
if request.method == "POST":
if form.is_valid():
#form.save()
#choice = form.cleaned_data.get('choice')
return redirect('exampage.html')
return render(request, 'exams/exampage.html', {'exams': exams,'questionlist': questionlist, 'exam_name': exam_name, 'choicelist': choicelist, 'form': form, 'choice': choice})
else:
form = ExamTest(exam_name=exam_name)
return render(request, 'exams/exampage.html', {'exams': exams,'questionlist': questionlist, 'exam_name': exam_name, 'choicelist': choicelist, 'form': form})
form
class ExamTest(forms.Form):
def __init__(self, *args, **kwargs):
exam_name = kwargs.pop('exam_name')
super(ExamTest, self).__init__(*args, **kwargs)
#choice_list = [x for x in ExamQuestion.objects.filter(exam__name="dcjs01")]
#choice_list = []
x = ExamQuestion.objects.filter(exam__name=exam_name)
#for q in x:
# choice_list.append(q)
self.fields["choices"] = forms.ChoiceField(choices=x, label="testlabel")
template
{% extends 'portal/base.html' %}
{% block content %}
<h1>{{ exam_name }} Page</h1>
{{ exam_id }}
<hr>
{% for exam in exams %}
<li>{{ exam }}</li>
{% endfor %}
<h1>! {{ questionlist }} !</h1>
<form method="post" action="#">
{% csrf_token %}
formtest{{ form }}
<button type="submit"> finish test </button>
</form>
{% endblock %}
The first part of the question is - you getting the ExamQuestion not iterable error:
here I think is the problem, that you, in the Form init function pass the Queryset (objects.filter(xxx)), but not the .all() which will select it.
the second thought is - would'n it be better to pass the questions as a parameter to the Form, as you previously selected all the question for this particular exam?
figured it out. choices=x needs to be a tuple
self.fields['name'] = forms.ChoiceField(choices=tuple([(name, name) for name in x]))
I'm building my first application (guess app) with Django, I've been doing well so far. But I've encountered an error when trying to redirect to a Detail View when a user submits a file through a submit function (similar to a 'post blog' scenario).
I've looked up several posts with the same problem and I can not figure out why my code is not working.
views.py
#login_required
def submit(request):
if request.method == 'POST':
submited_form = SubmitFileForm(request.POST, request.FILES)
if submited_form.is_valid():
...
form.save()
return HttpResponseRedirect(reverse('result-detail'), kwargs={'pk': form.pk})
else:
submited_form = SubmitFileForm()
return render(request, 'guess/submit.html', context)
class ResultDetailView(LoginRequiredMixin, DetailView):
model = Result
template_name = 'guess/result_detail.html'
context_object_name = 'result'
I know I'm mixing class based views with function based views, but for some reason I can not get my submit function to work when I try to implement it as a class based view. Anyway, I think that shouldn't be a problem
urls.py
url_patterns = [
...
path('result/<int:pk>', guess_views.ResultDetailView.as_view(), name='result-detail'),
...
]
result_detail.html
{% extends "guess/base.html" %}
{% block content %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ result.author }}</a>
<small class="text-muted">{{ result.date_posted }}</small>
</div>
<h2 class="article-title">{{ result.title }}</h2>
<p class="article-content">{{ result.statistic }}</p>
</div>
</article>
{% endblock content %}
I'd expect a redirect to the detail view of the object that has been created with the submit function and submitfileform (model form). I can access the details if I just type /result/ and the primary key of any created object. But apparently I can't do the same through a redirect.
The error I am getting:
NoReverseMatch at /submit/
Reverse for 'result-detail' with no arguments not found. 1 pattern(s) tried: ['result/(?P<pk>[0-9]+)$']
In your code, you have two problems. First, form is not a Model instance. When you call form.save(), it will return the model instance. So you need to store it in a variable. Second problem is that, you need to pass the kwargs as a known argument in the reverse, not in HttpResponseRedirect. So the following code should work:
instance = form.save()
return HttpResponseRedirect(reverse('result-detail',kwargs={'pk': instance.pk}))
I have created a view which allow user to delete a specific Booking provided with id but I dont want other user except the user associated with the Booking object to delete it. How I can restrict user to access urls which delete booking they not own.
The urls:
path('manage/', views.ManageView, name='manage'),
path('manage/delete_booking/(?P<pk>\d+)', views.delete_booking, name='delete_booking'),
The views:
#login_required
def delete_booking(request, pk):
booking = Booking.objects.get(pk=pk)
booking.delete()
return redirect('manage')
#login_required
def ManageView(request):
bookings = Booking.objects.filter(user=request.user)
context = {
'user': request.user,
'bookings': bookings
}
return render(request, 'timetable/manage.html', context)
Template:
<div class="w3-content">
<div class="w3-third w3-padding w3-center">
<h4 class="bg-color">Infomation</h4>
<p>Student ID: {{ user.profile.student_id }}</p>
<p>Full name: {{ user.last_name }} {{ user.first_name }}</p>
</div>
<div class="w3-two w3-padding w3-center">
<h4 class="bg-color">Booking</h4>
{% if not bookings.exists %}
<p>You don't have any booking</p>
{% else %}
{% for booking in bookings %}
<p>{{ booking.room }} {{ booking.date }} {{ booking.lesson.number }} Delete</p>
{% endfor %}
{% endif %}
</div>
You can add a check in the view, for example:
#login_required
def delete_booking(request, pk):
booking = get_object_or_404(Booking, pk=pk, user=request.user)
booking.delete()
return redirect('manage')
Here it will raise a 404 error (not found) in case it can not find an object with that pk, or the user is not request.user.
You can also check:
#login_required
def delete_booking(request, pk):
booking = get_object_or_404(Booking, pk=pk)
if booking.user_id != request.user.id:
return HttpResponse('Unauthorized', status=401)
booking.delete()
return redirect('manage')
This will return a 401 error (not authorized).
Both methods have its merits. For example by raising a 401, you give a "hint" that there exists an object with this pk. A malicious user could then try to access (view, alter, or delete) the object through another way. On the other hand a 401 describes better what the problem is: the user can not view/update/change the object. The frontend can give a more informative message. For example if a certain administrator has not enough rights to handle a certain object.
Note: usually only POST, PATCH, PUT, and DELETE methods should have side-effects. So you might want to omit the request.method == 'GET' case as well. Robots like search engines typically "assume" that GET requests are safe (well actually they should be safe). So by not checking the method, a search engine could "trigger" removal, updates, etc.
I previously asked this question, but it was hijacked by a troll. Here's a new one:
I have the following class in views.py. My goal is to click on a list item and see its detailview template, on which should be another list of items, all associated with this particular list item.
class Posts(DetailView):
model = Book
template_name='books/post_create.html'
slug_field = 'id'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['post'] = Post.objects.all()
return context
Right now, with this in the template:
<p>{{post}}</p>
it's showing (on the website):
<QuerySet [<Post: This is post 1>, <Post: This is post 2>, <Post: This is post 4>, <Post: This is post 5>]>
How can I make only the post names show? ("This is post 1" and others are the names I inputted).
Also on the template is:
<ul>
{% for post in object_list %}
<div class='ui card'>
<a class="content">
<div class="header">{{ post }}</div>
</a>
</div>
{% empty %}
<h5>You don't have any posts!</h5>
{% endfor %}
</ul>
This output on the website says only "You don't have any posts!"
What's wrong, and how can I make it work? I think object_list does not belong here; what should I put here instead?
Thank you so much for your help.
Fixing the rendering of post
Well first of all, you make the view misleading. Post.objects.all() is a queryset with multiple Posts (although it can, strictly speaking, contain zero or one Post as well). So I propose to use 'posts' insead:
class Posts(DetailView):
model = Book
template_name='books/post_create.html'
slug_field = 'id'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['posts'] = Post.objects.all()
return context
Then it works the same way as the object_list: you iterate over it:
<ul>
{% for post in posts %}
<div class='ui card'>
<a class="content">
<div class="header">{{ post }}</div>
</a>
</div>
{% empty %}
<h5>You don't have any posts!</h5>
{% endfor %}
</ul>
That being said, it looks like you actually want a ListView with as model the Posts, so change DetailView to ListView, perhaps change the template_name, change model = Book to model = Post, and drop the get_context_data(..) function. You can then iterate over the object_list instead.