Django: page not found 404 error when doing a post request - python

I have a model where I am using two foreign keys. One of them is the logged-in user and the other is for inheriting the post. But post foreign key is not working or not saving into the database but it should save the particular post chosen by the user or with pk.
This is my models.py:
class Booking(models.Model):
b_price = models..ManyToManyField(price, related_name='b_price',on_delete=models.CASCADE,null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, default='')
approved_price = models.BooleanField(default=False)
pay = models.CharField(max_length=30, default='')
mode = models.CharField(max_length=30 ,default='')
def __str__(self):
return str(self.user)
This is my views.py:
class booking_approve(CreateView, LoginRequiredMixin):
form_class = forms.booking_form
model = Booking
template_name = "confirm_booking.html"
success_url = reverse_lazy("Loader:post")
def form_valid(self, form):
booking = get_object_or_404(Booking, pk=self.kwargs.get('pk'))
print(form.cleaned_data)
bk = form.save(commit=False)
bk.user = self.request.user
bk.save()
bk.b_price.add(booking)
return super().form_valid(form)
This is my urls.py:
path('confirm_booking/<int:pk>/booking',views.booking_approve.as_view(), name="booking_approve"),
This is the HTML page where I pass the pk:
{% for loader_post in request.user.Loader.all %}
{% for price in loader_post.prices.all %}
{{loader_post.image_of_load.url }}
Post id : {{loader_post.id }}
Driver offer : {{price.driver_price }}
Offer by : {{price.driver_name }}
<a style="margin-right:20px;" href="{% url 'Loader:booking_remove' price.id %}"></a>
<
</div>
</div>
{% endfor %} {% endfor %} {% endblock content %}
This is my post pic. When I click on the tick button, the next form will appear but it should also take the id of the post but it won't.
This the pk's error:

Related

Accessing profile info through user model Django

Hello I am a beginner with the django python framework. I need to display a user's image and bio on a file named user_posts.html in my blog app. I have it where I can access the user's image and bio by looping over the posts for the user. However, I need it so it only displays the bio and the image once. I have a separate profile.html in a users app. In that file, I can do just src="{{ user.profile.image.url }}" and {{ user.profile.bio }} to access the users information but that does not seem to work in my user_posts.html because of the structure of my project. I can't figure out how to tell the for loop to just go over once to access the users information.
users_post.html
{% extends "blog/base.html" %}
{% block content %}
<hr size="30">
<div class="row">
<div class="column left">
{% for post in posts %}
<img style= "float:left" src="{{ post.author.profile.image.url}}" width="125" height="125">
<h5 style="text-align: left;">{{ post.author.profile.bio }}</h5>
{% endfor %}
</div>
views.py
class UserPostListView(ListView):
model = Post
template_name = 'blog/user_posts.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
models.py
from django.db import models
from django.contrib.auth.models import User
from PIL import Image
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
bio = models.TextField(default='enter bio text here')
def __str__(self):
return f'{self.user.username} Profile'
This is what the problem looks like
Any help is appreciated
Here's how you might do it using get_context_data().
class UserPostListView(ListView):
model = Post
template_name = 'blog/user_posts.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
def get_context_data(self, **kwargs):
"""
Add User Profile to the template context.
"""
context = super().get_context_data(**kwargs)
profile_user = get_object_or_404(User, username=self.kwargs.get('username'))
context['profile_user'] = profile_user
return context
You'd then use {{ profile_user.profile.bio }} in your template instead of posts for the user profile info.
This is probably better than getting the first object in posts and getting the user profile information from that object in the case the user has no posts yet (but has a bio).
Note, we're fetching the User object both in get_queryset and in get_context_data so this is not super efficient. There are ways around this but I'll leave it to future editors or you to optimize :)
Probably Not Recommended, But Answering Question
Since you initially were just trying to get the first element, here's how I'd do it.
# Method 1
{% with first_post=posts|first %}
{{ posts.user.profile.bio }}
{{ posts.user.profile.image.url }}
{% endwith %}
# Method 2
{{ posts.0.profile.bio }}
{{ posts.0.profile.image.url }}

Comments under Post

I created model Post and Comment, now I want to display comments from Post but I have a problem. In tutorial there is a line of code in html {% for comment in post.comments.all %} but isn't working in my app. If I set {% for comment in comments %} it works but displays all comments from models (I want only comments from the post). How to fix that? Below I pasted my code.
models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=64, blank=False, null=False)
short_text = models.TextField(blank=False, null=False)
text = models.TextField(blank=False, null=False)
image = models.TextField(blank=False, null=False)
date = models.DateTimeField(auto_now_add=True, blank=True)
views = models.IntegerField(default=0)
class Comment(models.Model):
post = models.ForeignKey('main.Post', on_delete=models.CASCADE)
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(auto_now_add=True, blank=True)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
views.py
from django.shortcuts import get_object_or_404, render, redirect
from .models import Post, Comment
def index_pl(request):
posts = Post.objects.all()
return render(request, 'index_pl.html', {'posts': posts})
def single_article_pl(request, id):
posts = get_object_or_404(Post, pk=id)
posts.views = posts.views + 1
posts.save()
comments = Comment.objects.all()
return render(request, 'single_article_pl.html', {'posts': posts, 'comments': comments})
html
{% for comment in post.comments.all %}
<div class="comment">
<div class="date">{{ comment.created_date }}</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|linebreaks }}</p>
</div>
{% empty %}
<p>No comments here yet :(</p>
{% endfor %}
admin.py
from django.contrib import admin
from .models import Post, Comment
admin.site.register(Post)
admin.site.register(Comment)
In tutorial there is a line of code in html {% for comment in post.comments.all %} but isn't working in my app.
This is likely because they specified the related_name=… parameter [Django-doc] in the ForeignKey from Comment to Post, like:
# Option 1: set the related_name to 'comments'
class Comment(models.Model):
post = models.ForeignKey(
'main.Post',
related_name='comments',
on_delete=models.CASCADE
)
# …
The related_name=… specifies the name of the relation in reverse, so in this case accessing the Comments of a given Post object. By default this is model_name_set, so in this case comment_set.
You thus can either specify a related name; or you can acces the comment_set manager:
Option 2: use the default related_name
{% for comment in post.comment_set.all %}
<div class="comment">
<div class="date">{{ comment.created_date }}</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|linebreaks }}</p>
</div>
{% empty %}
<p>No comments here yet :(</p>
{% endfor %}

How to return multiple items in a get_queryset function in Django?

So I have a web app where the user can enter their information, and eventually I want to display all of it, but at the moment this code right here
class UserPostListView(ListView):
model = Post
template_name = 'mainapp/user_posts.html'
context_object_name = 'posts'
def get_queryset(self):
user = get_object_or_404(User,username=self.kwargs.get('username'))
first_name = get_object_or_404(User,first_name=self.kwargs.get('first_name'))
return Post.objects.filter(author=user).order_by('-published_date')
It gives me an error, and it says User not found.
I have tried add this to the end of the return statement
.order_by('-published_date'),first_name
However this did not work.
This is the relevant urls.py file responsible for the user posts
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
This is the UserProfileInfo model
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=50,blank=True,null=True)
last_name = models.CharField(max_length=50,blank=True,null=True)
description = models.CharField(max_length=150)
image = ProcessedImageField(upload_to='profile_pics',
processors=[ResizeToFill(150, 150)],
default='default.jpg',
format='JPEG',
options={'quality': 60})
joined_date = models.DateTimeField(blank=True,null=True,default=timezone.now)
verified = models.BooleanField( default=False)
def __str__(self):
return f'{self.user.username} Profile'
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
And here is the relevant bit of the user-posts.html
<div class="sidebar">
<p class="active" href="#">{{ view.kwargs.username }}</p>
<button class="commentbtn"><a class="aclass" href="#">Connect with {{ view.kwargs.username }}</a></button>
<p>{{ view.kwargs.first_name }}</p>
<p>Lorem</p>
</div>
I want to be able to display the first name of the person in the ```{{ view.kwargs.first_name }}, however everything I have tried has failed to work
I expected no errors to occur and for this to work, however this is not the case. Is it possible to have 2 get_queryset methods, or is there something I can do in the current one to achieve my goal of displaying the information of the user
Thanks for any help you can give :)
How about this?
def get_queryset(self):
qs = super().get_queryset() #this is the same as Post.objects.all()
user = self.request.user
return qs.objects.filter(author=user).order_by('-published_date')
Now you can access this query using object_list on your template, but since you changed it's name with context_object_name='posts', use posts instead:
{% for post in posts %}
<h1>{{ post.first_name }}</h1>
...
{% endfor %}
But it looks like the Post model isn't going to have a first_name column?
You might be better off using the request.user object here as well:
<h1>{{ request.user.first_name }}</h1>
<h2>{{ request.user.username }}</h2>

how to connect two models in html template?

I would like to create a simple forum with Python Django. The main thing I can not figure out is getting informations from two models.
I want to display: Post title, content, author, published date and signature. This is my post_detail.html
{% extends 'Homepage/base.html' %}
{% block content %}
<h1>{{ post.title }}</h1>
<h6>Kategoria: {{ post.category }} | Autor: {{ post.author }} | {{ post.published_date }}</h6>
<p>{{ post.description|linebreaksbr }}</p>
<p><hr>{{ HERE I DON'T KNOW HOW TO SHOW SIGNATURE }}</p>
{% endblock %}
</body>
</html>
Homepage app models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Category(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
def __str__(self):
return self.title
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
published_date = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(blank=True, null=True)
views = models.IntegerField(default=0)
def __str__(self):
return self.title
accounts app models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='user', on_delete=models.CASCADE)
website = models.URLField(default='', blank=True)
city = models.CharField(max_length=100, default='', blank=True)
signature = models.TextField(default='', blank=True)
def create_profile(sender, **kwargs):
user = kwargs["instance"]
if kwargs["created"]:
user_profile = UserProfile(user=user)
user_profile.save()
post_save.connect(create_profile, sender=User)
def __str__(self):
return self.user.username
My full code is on https://github.com/Incybro/Forum
You can just follow the relationships:
<p>{{ post.author.user.signature }}</p>
(Note, you've set the related_name from User to UserProfile to user, which makes no sense. You should leave it as the default, which would be userprofile.)
I guess your {{post.author}} won't be returning anything, change in to {{post.author.get_full_name}}
For signature, in your model you don't need to add any related name,
user = models.OneToOneField(User, on_delete=models.CASCADE)
in template,
{{post.author.userprofile.signature}}
Why not just return the relevant UserProfile model when you send the request?
def my_view(request):
context = {}
my_post = Post.objects.all.get(0) # get the post here
my_user = UserProfile.objects.get(user=my_post.author)
context['post'] = my_post
context['user'] = my_user
return TemplateResponse(request, 'my_template.html', context)
Then, in your html, you can use those template tags.
{% block content %}
<h1>{{ post.title }}</h1>
<h6>Kategoria: {{ post.category }} | Autor: {{ post.author }} | {{ post.published_date }}</h6>
{% autoescape on %}
<p>{{ post.description|linebreaksbr }}</p>
<p><hr>{{ user.signature }}</p>
{% endautoescape %}
{% endblock %}
You want the autoescape so people can't change the look or function of your site by injecting malicious HTML/JS into your site.

Django: Querying to ImageField all images are being rendered

So my problem is that when I try to query to the image url so it can be posted to its corresponding Post all the images that have been uploaded to the media folder is being rendered, even though in the admin panel it shows that each post has it's own image and they are assigned to different posts, instead all of them are being rendered together for each and every post.
The models that I have are SellPost which is for creating a post and SellPostImage is for assigning the image to the post.
models.py
class SellPost(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=128)
category = models.ForeignKey(Category)
type = models.ForeignKey(SellPostType, default=None)
body = models.CharField(max_length=400)
price = models.DecimalField(decimal_places=1, max_digits=5, default=0.0)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
slug = models.SlugField(unique=True, default='automatic')
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(SellPost, self).save(*args, **kwargs)
def __unicode__(self):
return self.title
class SellPostImage(models.Model):
user = models.ForeignKey(User, null=True)
post = models.ForeignKey(SellPost)
pictures = models.ImageField(upload_to='post_images', blank=True)
def __str__(self):
return "{}".format(self.post)
class Meta:
verbose_name_plural = "Post Images"
In the view I tried to create a context dict (because I'm a newbie in Django and have learned that from Tango with Django so I went with it) for the post and then the images:
views.py
def post(request, post_name_slug):
context_dict = {}
try:
post = SellPost.objects.get(slug=post_name_slug)
context_dict['post'] = post
post_image = SellPostImage.objects.all()
context_dict['post_image'] = post_image
except SellPost.DoesNotExist:
pass
return render(request, 'p.html', context_dict)
and here is how I tried to render them in the HTML file.
p.html
<ul>
{% for post in posts %}
<li>{{ post.title }} </li>
{% for post_images in post_image %}
<img style="width:200px; height:200px;" src="{{ post_images.pictures.url }}" />
{% endfor %}
{% endfor %}
</ul>
You'll want to filter the SellPostImage for the retrieved post:
post = SellPost.objects.get(slug=post_name_slug)
context_dict['post'] = post
post_image = SellPostImage.objects.filter(post=post)
context_dict['post_image'] = post_image
But you can just as easily put that logic part directly into your template:
{% for post in posts %}
<li>{{ post.title }} </li>
{% for post_images in post.sellpostimage_set.all %}
<img style="width:200px; height:200px;" src="{{ post_images.pictures.url }}" />
{% endfor %}
{% endfor %}
and then you can remove the SellPostImage in your views:
try:
post = SellPost.objects.get(slug=post_name_slug)
context_dict['post'] = post
except SellPost.DoesNotExist:
pass
In your post method you query for all SellPostImages:
post_image = SellPostImage.objects.all()
That's why you get all images for each post.
You can filter only the images associated with a post by doing the following instead:
post_image = SellPostImage.objects.filter(post=post)
It will provide all images for that specific post.

Categories