I have this model
class Post(models.Model):
title = models.CharField(max_length=100)
title2 = models.CharField( max_length=100)
content = models.TextField(default=timezone.now)
content2 = models.TextField(default=timezone.now)
post_image = models.ImageField(upload_to='post_pics')
post_image2 = models.ImageField(upload_to='post2_pics')
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
and this function based view that uses the model:
class PostListView(ListView):
model = Post
template_name = 'front/front.html'
context_object_name = 'listings'
ordering = ['-date_posted']
def get_context_data(self, **kwargs):
check_for_zipcode = #where I want to access the author for the current instance
context = super().get_context_data(**kwargs)
context['zipcodes'] = check_for_zipcode
return context
All I want to know is how can I access the author field in the class-based view. I can access the author in my HTML like so"
{% for listings in listings %}
<h3>listings.author</h3>
{% endfor %}
With this, I'll get back the author field for every instance of that model.
How would I get the author for the instance in the variable check_for_zipcode? I tried self.author, self.listings.author, etc; but nothing works
In get_context_data method you have the object_list which is the result of get_queryset method. So you can override the get_queryset method. You can even do the same in get_context_data in object_list. Sample example as below:
def get_queryset(self):
return Post.objects.all().annotate(zipcode=F('author'))
Then in your template:
{% for listing in listings %}
<h3>listing.zipcode</h3>
{% endfor %}
This returns the id of author object since it is a foreignkey. If you want some other attribute like username then do author__username in the annotate function.
Related
Working on my first Django project and could use some help. I have 2 models (Decisions, Votes) linked by the foreign key called 'decision'. The template, vote_list.html, shows the user a list of decisions (generated by other users) that are contained in Decisions. The user taps a particular decision and is re-directed to a second template to vote on options pertaining to that decision. How do I autopopulate the foreign key 'decision' in Votes with the corresponding instance of Decision so that the second template, vote_form.html, displays the options for the decision they tapped on? I assume it's coded in views.py (I commented an attempt below that doesn't work), but how might it be done? Thank you!
urls.py
path('vote-list/', views.VoterView.as_view(), name='vote_list'),
path('vote-list/<pk>/vote-form/', views.VoteForm.as_view(), name='vote_form'),
models.py
class Decisions(models.Model):
custom_user = models.ForeignKey(CustomUser,
default=None, null=True,
on_delete=models.SET_NULL)
description = models.CharField(default="",
max_length=100, verbose_name="Decision
Summary")
class Votes(models.Model):
decision = models.ForeignKey(Decisions,
default=None, null=True,
on_delete=models.SET_NULL)
vote = models.CharField(default="", max_length=100,
verbose_name="Your vote")
views.py
class VoteForm(LoginRequiredMixin, CreateView):
model = Votes
form_class = VotingForm
template_name = 'users/vote_form.html'
def post(self, request, *args, **kwargs):
super()
form = self.form_class(data=request.POST)
if form.is_valid():
instance = form.save(commit=False)
# instance.decision = Decisions.description
instance.save()
return redirect('users:vote_list')
forms.py
class VotingForm(forms.ModelForm):
class Meta:
model = Votes
fields = ['vote']
vote_list.html
{% for item in Decisions %}
<tr>
<td>{{ item.description }}</td>
<td><a href="{% url 'users:vote_form' item.id
%}">Vote</a></td>
</tr>
{% endfor %}
vote_form.html
{# trying to display the corresponding
decision description here from vote_list.html # }}
{{ form.vote|as_crispy_field }}
I think this might solve your problem:
Add decision field in the voting form. This will display an option to select for which decision you need to save this Vote for.
If you don't want to allow users to change the Decision, you can mark the field as disabled. See this issue for more details on how to do that. Another alternative is to completely hide the field.
class VotingForm(forms.ModelForm):
class Meta:
model = Votes
fields = ['vote', 'decision']
Add initial value of the decision when instantiating the VotingForm. This will automatically set which decision is selected when displaying the form.
class VoteForm(LoginRequiredMixin, CreateView):
model = Votes
form_class = VotingForm
template_name = 'users/vote_form.html'
# Use this to pass 'pk' to your context in the template
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({'pk': self.kwargs['pk'})
return context
def get_initial(self):
initial = super().get_initial()
initial.update({'decision': self.kwargs['pk']})
return initial
def get_success_url():
# Import reverse from django.urls
return reverse('users:vote_list')
Also, your form should probably be displayed like this in the HTML template: {% crispy form %}. This way all defined fields from the VotingForm class are rendered automatically.
<form method="post" action="{% url 'users:vote_form' pk %}">
{% crispy form %}
</form>
I am trying to create a page on my site (social media type) where the posts would be grouped and displayed by the user that created them.
I tried creating a following context item and then accessing the posts through each of the users in that group but it is not showing any results.
I also tried filtering posts by the users in following. However, it does not show any results. I don't know if I am using the filter function correctly.
This is my View:
class CommunityListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'community/community.html'
context_object_name = 'posts'
ordering = ['-date_added']
def get_context_data(self, **kwargs):
context = super(CommunityListView, self).get_context_data(**kwargs)
active_user = self.request.user
active_user_following = active_user.following.values_list('user_id', flat=True)
following_user_objects = []
context['following'] = following_user_objects
context['followed_user_pots'] = Post.objects.filter(user__in=following_user_objects)
for id in active_user_following:
followed = User.objects.get(id=id)
following_user_objects.append(followed)
return context
This is my HTML code:
{% for user in following %}
{{user}}
{% for post in user.post %}
{{post}}
{% endfor %}
{% endfor%}
All the above HTML displays is the username of the users in following. Is there some other way I need to access the posts?
This is the Post model:
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, null=True)
post = models.CharField(max_length=200)
date_added = models.DateTimeField(default=timezone.now)
First I think in values_list you have to put id not user_id, because you want to get ids of the following users. and also that's much better practice when you get query logic in get_queryset method. so you don't need to add followed_user_pots in context it's better to remove it and instead use this code for getting what you want.
def get_queryset(self):
qs = super().get_queryset()
active_user = self.request.user
active_user_following = active_user.following.values_list('id', flat=True)
return Post.objects.filter(user__id__in=following_user_objects)
in template you can access posts by iterating through object_list
{% for post in object_list %}
{{post}}
{% endfor %}
I am trying to filter a queryset by a variable in another queryset that hasn't been set yet. I know it sounds confusing so let me show you.
Views.py
def ViewThreadView(request, thread):
posts = Post.objects.filter(post_thread=thread)
thread = Thread.objects.get(pk=thread)
form_class = QuickReplyForm
thread_name = thread.name
return render(request, 'thread/viewthread.html',
{'thread': thread, 'posts': posts, 'thread_name': thread_name})
Post Model
class Post(models.Model):
post_body = models.TextField(blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post_date = models.DateTimeField(auto_now_add=True)
post_thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
def __str__(self):
return str(self.id) + ' | ' + str(self.author)
The User model is the standard Django model
As of now, if I want to access the post author in the template, I'd do this
{% for post in posts %}
post.author
{% endfor %}
My question is, how do I access the tables of post.author. So if I want to filter how many posts that author has, I want to do something like user_posts = Post.objects.get(author=post.author). But that can't work in the views because "posts" is a queryset and not a value. How can I do this?
In your template you can access your related objects with post_set:
{% for post in posts %}
{{ post.author.post_set.count }}
{% endfor %}
If you need more then the total number of posts, do you want filter your related objects or something else. You can always write a custom method for your model. See Model methods
For example:
from django.utils.functional import cached_property
class Post(models.Model):
post_body = models.TextField(blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post_date = models.DateTimeField(auto_now_add=True)
post_thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
def __str__(self):
return str(self.id) + ' | ' + str(self.author)
#cached_property
def count_by_author_and_thread(self):
return self.author.post_set.filter(post_thread=self.post_thread).count()
And then in your template simple use:
{% for post in posts %}
{{ post.count_by_author_and_thread }}
{% endfor %}
I have two interconnected models in my blog app; Category and Post. The blog front page displays a list of posts and their corresponding metadata, like it should; fairly standard stuff.
Aside from displaying the posts on the front page, they're also displayed on the individual user's profile page in short form (just the category and the headline).
What I'm interested in doing is sorting all the posts that belong in a category, however the only way I've managed to make it work is something like this:
NEWS
some title
NEWS
another title
PYTHON
another arbitrary title
NEWS
yet another title
I'd like to sort it thusly instead:
NEWS
some title
another title
yet another title
PYTHON
another arbitrary title
Alas, my mind keeps turning into a bowl of spaghetti when I try to come up with a method, so without further ado; how should I go about this bit?
I reckon that there's something off with calling the category from the post's metadata only to try and categorize the posts via the retrieved data, but aside from that, I'm somewhat lost.
Here's the template snippet from user_profile.html:
{% if user.post_set.exists %}
<p>
{% for post in user.post_set.all|dictsortreversed:"date_posted" %}
<span style="margin-right: 5px; padding: 3px 6px; border-radius:12px; color:#FFF; background-color:#FFA826;">{{ post.category }}</span><br/>
<a style="margin-left:3px;" href="{% url 'blog:post-detail' post.slug %}">{{ post.title|truncatechars_html:30 }}</a><br/>
{% endfor %}
</p>
{% endif %}
The models:
class Category(models.Model):
class Meta:
verbose_name = 'category'
verbose_name_plural = 'categories'
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=60)
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.CASCADE)
content = RichTextUploadingField(
external_plugin_resources=[(
'youtube',
'/static/ckeditor/ckeditor/plugins/youtube/',
'plugin.js'
)],
blank=True,
null=True,
)
date_posted = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=70, blank=True, null=True, help_text='<font color="red">don\'t. touch. the. slug. field. unless. you. mean. it.</font> (it will auto-generate, don\'t worry.)')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post-detail', kwargs={'slug': self.slug})
And finally the view which relate to the post_list.html:
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = '-date_posted'
paginate_by = 6
Should I be doing it in a different manner altogether, I wonder? And if so, what would be considered 'best practice'?
Thank you :)
You can add the ordering in your model:
class Post(models.Model):
...
class Meta:
ordering = ['category', '-date_posted']
See the documentation for more details:
update
Maybe its better to use custom manager for this:
from django.db import models
class CustomManager(models.Manager):
# subclass model manager
def custom_category_dict(self, **kwargs):
# add a new method for building a dictionary
nDict = dict()
for i in self.get_queryset().filter(**kwargs): # filter queryset based on keyword argument passed to this method
current_list = nDict.get(i.category.name, [])
current_list.append(i)
nDict.update({i.category.name: current_list})
return nDict
class Posts(models.Model):
# override posts model with manager
objects = CustomManager()
Usage:
# view
class PostListView(ListView):
...
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
context['category_wise_sorted_posts'] = Posts.objects.custom_category_dict() # you can pass filter logic as well, like Posts.objects.custom_category_dict(author_id=1)
return context
# template
{% for category, posts in category_wise_sorted_posts.items %}
<!-- Or {% for category, posts in user.posts_set.custom_category_dict.items %} -->
{{ category }}
{% for p in posts %}
{{ p.title }}
{% endfor %}
{% endfor %}
I'm trying to print all the followers the person that owns a profile page has. Here is my following table that shows the following relationship:
class Following(models.Model):
target = models.ForeignKey('User', related_name='followers', on_delete=models.CASCADE, null=True)
follower = models.ForeignKey('User', related_name='targets', on_delete=models.CASCADE, null=True)
def __str__(self):
return '{} is followed by {}'.format(self.target, self.follower)
I am also using Django's auth User model.
views.py
class FollowersView(DetailView):
model = User
slug_field = 'username'
template_name = 'profile/followers.html'
def get_profile_followers(user):
return user.followers.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["follower_list"] = get_profile_followers(self.object) # self.object is user profile
return context
In the template, I am doing this:
{% for follower in follower_list %}
<h1>{{ follower }}</h1>
{% endfor %}
But, I get this instead: Chris is followed by John. This is correct, Chris is followed by John, however, I want to display only John's user and John's attributes like avatar, follower_count, etc. that are fields in the User table. How can I do this?
Based on text, I suppose you get Following instance as {{ follower }} value. So you can just use . syntax to get follower attributes like this:
{% for follower in follower_list %}
{{ follower.follower.username }}
{{ follower.follower.avatar }}
{% endfor %}
Note that accessing related objects through . required additional DB query. So you may use select_related in you queryset for optimization:
def get_profile_followers(user):
return user.followers.all().select_related('follower')