Accessing profile info through user model Django - python

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 }}

Related

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

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:

Filtering displayed objects in django view

I am learning Django. Currently I build my blog project. I want to add function to filter posts by date (you can choose specific date from combo box and click "filter" button and then on the main page will display only this posts which were created in this date). Since I'm still new to django, I'm struggle how to handle it.
My question is how to build a functionality that will extract the sent date from the combo box and pass it to the view where I will do the appropriate filtering in the get_queryset method. Below I publish my code:
Part of my .html file where I build combo box:
<p class='text-muted'>Choose date from the list below.</p>
<form method="GET">
<select name="date_filter">
<option>-----------------</option>
{% for post in posts %}
<option>{{ post.date_posted }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-info btn-sm mt-1 mb-1">Filter</button>
</form>
I would also like each date to be unique and displayed only once. Currently, each date is displayed as many times as many posts were created that day because DateTimeField in my model also stores the post creation hour.
Main page view where post are displayed - in my views.py file:
class PostListView(ListView):
model = Post
template_name = "blog_app/home.html"
context_object_name = 'posts'
ordering = ['-date_posted']
# I believe here should be something which fetch choice from combo box and asign it to the
# date_from_combo_box variable. Please, correct me if I'm wrong.
def get_queryset(self):
# Here I will decide what posts are to be displayed based on the selection made in the combo box
if self.date_posted == date_from_combo_box:
return Post.objects.filter(date_posted=date_from_combo_box)
My models.py file:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
comm_content = models.TextField()
add_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
def __str__(self):
return f"Comment of post {self.post} posted at {self.add_date}."
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.post.pk})
Thanks for any hints and advice.
For getting unique date from your post table for dropdown change your query to
Post.objects.dates('date_posted', 'day').distinct()
Change your html
<p class='text-muted'>Choose date from the list below.</p>
<form action="url_to_list_view" method="GET">
<select name="date_filter">
<option>-----------------</option>
{% for post in posts %}
<option>{{ post.date_posted }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-info btn-sm mt-1 mb-1">Filter</button>
</form>
Your listview will look like this.
class PostListView(ListView):
model = Post
template_name = "blog_app/home.html"
context_object_name = 'posts'
ordering = ['-date_posted']
def get_queryset(self):
search = self.request.GET.get('date_filter', None)
if search is not None:
return Post.objects.filter(date_posted__date=search)
else:
return Post.objects.all()
You could use something like this,
Model.objects.filter(date_attribute__month=month, date_attribute__day=day)
or for a range you can use
Sample.objects.filter(date__range=["2011-01-01", "2011-01-31"])
Credits

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>

List all the compositions by a specific composer in their profile details page

I want a public page showing the profile of a specific user (so I cannot grab the user id from the logged-in users). I'm not able to select their specific compositions.
I am using a custom user model, so that I have a User class and then a Profile class which is linked to the User via OneToOneField (see code below). I also have a Composition class, which is linked to a specific composer via a ForeignKey. I am able to get the details of a specific profile and I'm also able to print out all the compositions (using Composition.objects.all()).
My models.py:
class User(AbstractBaseUser):
email = models.EmailField(unique=True, max_length=255)
full_name = models.CharField(max_length=255, blank=True, null=True)
objects = UserManager()
[...]
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
[...]
def get_absolute_url(self):
return reverse('profile', args=[str(self.id)])
class Composition(models.Model):
title = models.CharField(max_length=120) # max_length = required
description = models.TextField(blank=True, null=True)
composer = models.ForeignKey(
USER, #my custom user model
on_delete=models.CASCADE
)
def get_absolute_url(self):
return reverse("composition-detail", kwargs={"id": self.id})
def __str__(self):
return "%s" % (self.title)
My views.py:
def profile_details(request, id):
compositions = Composition.objects.filter(composer__id=id)
context = {
"object_list": compositions
}
return render(request, "profile.html", context)
My urls.py:
path('profile/<int:id>/', views.profile_details, name='profile')
My template.html:
{% extends "base.html" %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ user.full_name }}</h2>
<p>{{ obj.profile_id}}</p>
<p class="text-secondary">{{ user }}</p>
{% for composition in object_list %}
<li>{{ composition.title }}</li>
{% endfor %}
</div>
</div>
</div>
{% endblock content %}
I'm expecting to see the compositions by that specific composer, i.e. user associated to the profile I'm viewing.
Edit
I've made some progress by adding some custom data to my view:
class ProfileDetails(generic.DetailView):
model = Profile
template_name = 'profile.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['compositions'] = Composition.objects.filter(id=1)
return context
Now the context contains indeed all the composition. How do I filter only the composition by the user connected to the profile I'm viewing?
After many tests I've apparently finally come to a solution.
My views.py:
def profile_details(request, pk):
compositions = Composition.objects.filter(composer__id=pk)
context = {
"compositions": compositions,
}
return render(request, "profile.html", context)
And my urls.py:
path('profile/<int:pk>/', views.profile_details, name='profile')
The pk, which is the same for User and Profile objects, is taken from the URL, and used for filtering the Composition objects and find only the compositions by that composer.

Django: Get all objects from a specific user

I have a problem when trying to display all the Announce objects from a user.
My problem is : Consider that I am logged in as a user with an id=1. When I go to /users/1/ it displays all my posts. But the problem is when I want to display all the posts from another user with id=2 by going to /users/2/, it still display all my Announce objects, and not the user with the id=2.
models.py
class Announce(models.Model):
owner = models.ForeignKey('auth.User', related_name='announces')
created_date = models.DateTimeField(auto_now_add=True)
body = models.TextField(max_length=1000)
views.py
class UserAnnouncesList(ListView):
model = Announce
template_name = 'myApp/user_announces_list.html'
context_object_name = 'all_announces_by_user'
def get_queryset(self):
return Announce.objects.filter(owner=self.request.user)
urls.py
urlpatterns = [
url(r'users/(?P<pk>[0-9]+)/$', views.UserAnnouncesList.as_view(), name='user_announces_list'),]
user_announces_list.html
{% extends "account/base.html" %}
{% block content %}
{% for announce in all_announces_by_user %}
<h1>{{announce.user.username}}</h1>
<p>{{announce.body}}</p>
{% endfor %}
{% endblock content %}
Do I have to use some kind of like : Announce.objects.get(pk=???) ?
I appreciate your help!
The request.user is the user that is logged in. You need to use the pk that is passed as url. This is stored in the kwargs dictionary of the listview:
class UserAnnouncesList(ListView):
model = Announce
template_name = 'myApp/user_announces_list.html'
context_object_name = 'all_announces_by_user'
def get_queryset(self):
return Announce.objects.filter(owner=self.kwargs['pk'])

Categories