Django, ManyToMany field - django

I have two models in my blog app:
class Tag(models.Model):
tag_name = models.CharField(max_length=20,
null=True)
def __str__(self):
return self.tag_name
class Post(models.Model):
tag = models.ManyToManyField(Tag, related_name="blog_tag",
blank=True)
In views i have:
tags = Tag.objects.all()
And
post = get_object_or_404(Post,
status="published",
publish__year=year,
publish__month=month,
publish__day=day,
slug=post)
So my question is - how can i filter tags by post ? I mean that i want to show only tags what i add to my post. I tried to do than in template , but something dosen't work :
{% for tag in tags %}
{% if tag in post %}
<div>
{{ tag.tag_name }}
</div>
{% endif %}
{% endfor %}
Post model:
class Post(models.Model):
STATUS_CHOICES = (
("draft", "Draft"),
("published", "Published"),
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250,
unique_for_date="publish")
author = models.ForeignKey(User,
related_name="blog_posts",
on_delete=models.CASCADE)
tag = models.ManyToManyField(Tag, related_name="blog_tag",
blank=True)
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10,
choices=STATUS_CHOICES,
default="draft")
image = models.ImageField(upload_to="blog/%Y/%m/%d",
blank=True)
class Meta:
ordering = ("-publish",)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("blog:post_detail",
args=[self.publish.year,
self.publish.strftime("%m"),
self.publish.strftime("%d"),
self.slug])
The answer is:
{% for tag in post.tag.all %}
<div class="fa fa-plus-square">
{{ tag.tag_name }}
</div>
{% endfor %}

Since you're using a ManyToManyField in the Post class, it would be clearer if that field was called tags rather than tag, since it is referring to more than one thing.
class Post(models.Model):
tags = models.ManyToManyField(Tag, related_name="blog_tag",
blank=True)
So once you have looked up a post with:
post = get_object_or_404(Post,
status="published",
publish__year=year,
publish__month=month,
publish__day=day,
slug=post)
You pass it to the template with:
return render(request, 'post_detail.html', {'post': post})
And then in the post_detail.html you can render the tags associated with the post:
{% for tag in post.tags.all %}
<div>
{{ tag.tag_name }}
</div>
{% endfor %}
No need to explicitly filter the tags as the ManyToManyField handles that for you.

You can do this :
{%for tag in tags %}
{% if tag.id == post.tag.id%}
<div>
{{ tag.tag_name }}
</div>
{%endif%}
{%endfor%}

Related

Object_list doesn't show the correct template

I have a Django project where users can ask questions and receiving answers. I have three models: Question, Answer and Comment. I don't know why comment template doesn't show the correct data, I dont't hnow where to find the comment data either object.comment, object.comment_set.all or anything else.
I had the same problem with fetching Answer data, but I successfully solved it by using '{% for answer in object.answer_set.all %}', but the same method doesn't apply in comment. I noticed that I don't understand where is all the information stucks to retrieve.
I'm relatively new to Django, so I'd be grateful to get all answers with description, why am getting this to avoid this in the fitire projects.
models.py
class Question(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=255, unique=True, db_index=True, verbose_name="URL")
detail = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('detail', kwargs={'slug': self.slug})
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
detail = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.detail
class Comment(models.Model):
answer = models.ForeignKey(Answer, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comment_user')
detail = models.TextField(default='')
date_posted = models.DateTimeField(default=timezone.now)
views.py
class AnswerView(ListView):
model = Answer
template_name = 'forum/detail.html'
class CommentView(ListView):
model = Comment
template_name = 'forum/detail.html'
detail.html
<div class="col-11">
{% for answer in object.answer_set.all %}
<p>{{ answer.detail }}</p>
<p>{{ answer.user.username }}
<span>5 комментариев</span></p>
{% endfor %}
</div>
<!-- Comment Section Starts-->
<h3 class="my-4">Комментарии:</h3>
{% for comment in comment.answer.all %}
<div class="card mb-3">
<div class="card-body">
<p>{{ comment.detail }}</p>
<p>{{ comment.user.username }}</p>
</div>
</div>
{% endfor %}
Your comments are connected to answers in a many-to-one relation, so you can get them in the same way as you access answers from questions - you just need to do it within a nested loop, i.e.
{% for answer in object.answer_set.all %
// your answer template stuff
{% for comment in answer.comment_set.all %}
// your comment template stuff
{% endfor %}
{% endfor %}
you need use object_list in your template has the docs of ListView says
<div class="col-11">
{% for answer in object_list %} <!-- change to object_list-->
<p>{{ answer.detail }}</p>
<p>{{ answer.user.username }}
<span>5 комментариев</span></p>
{% endfor %}
</div>
if need/wanna change the name of the variable object_list, you can define context_object_name in the view.

Django Change boolean field with click

I am working on a To-do app. The individual to-dos reference a to-do list via a foreign key and the to-do lists reference a project via a foreign key.
I want the to-do's status to be set to true when clicked. I have seen some tutorials where this is done but I haven't been able to get this to work yet.
Here are the models:
class Project(models.Model):
title = models.CharField(max_length= 200)
description = tinymce_models.HTMLField()
status = models.CharField(max_length=20, choices=PROJECT_CHOICES, default="active")
date = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse ('project_detail', args=[str(self.id)])
class ProjectTodoGroup(models.Model):
title = models.CharField(max_length=250)
description = tinymce_models.HTMLField()
project = models.ForeignKey(Project, blank=True, on_delete=models.CASCADE, related_name='todo_group')
date = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return self.title
class ProjectTodo(models.Model):
title = models.CharField(max_length= 250)
notes = tinymce_models.HTMLField()
status = models.BooleanField(default=False)
projectgroup = models.ForeignKey(ProjectTodoGroup, blank=True, null=True, on_delete=models.CASCADE, related_name='todo_set')
date = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return self.title
The view:
model = ProjectTodo
fields = ['status']
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['project'] = get_object_or_404(Project, id=self.kwargs.get('pk'))
return context
def get_success_url(self):
return reverse('company_project:todo_group_detail', args=[self.kwargs.get('pk'), (self.object.id)])
Everything I have tried to far with the view hasn't worked.
The template:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="section-container container">
<div class="todo-group">
<h2>{{ projecttodogroup.title }}</h2>
<p>{{ projecttodogroup.description | safe }}</p>
</div>
<div><b>Todos</b></div>
{% if projecttodogroup.todo_set.all %}
{% for todo in projecttodogroup.todo_set.all %}
<div class="todos" style="padding: 10px;">
{{ todo.title }}
</div>
{% endfor %}
{% else %}
<p>No Todos have been have been added yet.</p>
{% endif %}
<h1>Add Todo</h1>
<form action="" method="post">
{% csrf_token %}
{{ form.media }}
{{ form|crispy }}
<input type="submit" value="save">
</form>
</div>
{% endblock content %}

Filter a queryset in Django based on a changing value

I build a Blogpost App with Django and want comments under the blogpost. I can already post new comments, and see comments, but I see every comment under every blogpost. .
class blogpost(models.Model):
user = models.ForeignKey(User, default=1, null=True, on_delete=models.SET_NULL)
title = models.TextField()
slug = models.SlugField(unique=True)
content = models.TextField(null=True, blank=True)
class blogcommment(models.Model):
user = models.ForeignKey(User, default=1, null=True, on_delete=models.SET_NULL)
post = models.ForeignKey(blogpost, default=1, null=True, on_delete=models.SET_NULL)
title = models.TextField()
content = models.TextField(null=True, blank=True)
def blogpost_detail_view (request, slug):
# Blogeintrag anzeigen
obj = blogpost.objects.get(slug=slug)
form = blogcommentform(request.POST or None)
qs = blogcommment.objects.filter(***What should stay here and why?***)
if form.is_valid():
comment = blogcommment.objects.create(**form.cleaned_data)
form = blogcommentform
template_name = 'blogpost_detail.html'
context = {"object": obj,'form': form,'object_list': qs}
return render(request, template_name, context)
{% extends "base.html" %}
{% block content %}
<h1>{{ object.title }}</h1>
<p>{{ object.content }}</p>
<form method="POST" action=""> {% csrf_token %}
{{ form.as_p }}
<button type='submit'>Senden</button>
</form>
{% for object in object_list %}
{{ object.content }}
{% endfor %}
{% endblock %}
class blogcommentform(forms.Form):
title = forms.CharField()
content = forms.CharField(widget=forms.Textarea)
Try this
qs = blogcommment.objects.filter(post_id=obj.id)
In blogcommment model you have a reference to blogpost. So, you can easily filter comment for which blog post related to it.
qs = blogcommment.objects.filter(post=obj)
This will tell the queryset to filter all blogcomments that have the current specific blogpost as their post reference.

Trying to show latest record - Django

Models
class Category(models.Model):
class Meta():
verbose_name_plural = "Categories"
cat_name = models.CharField(max_length=50)
description = models.TextField()
def get_forums(self):
get_forum = Forum.objects.filter(category=self)
return get_forum
def __str__(self):
return f"{self.cat_name}"
class Forum(models.Model):
class Meta():
verbose_name_plural = "Forums"
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="forums")
parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE)
forum_name = models.CharField(max_length=50)
description = models.TextField()
def __str__(self):
return f"{self.forum_name}"
class Thread(models.Model):
class Meta():
verbose_name_plural = "Threads"
get_latest_by = "date_posted"
forum = models.ForeignKey(Forum, on_delete=models.CASCADE, related_name="threads")
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.title} by: {self.author}"
View
class Home(ListView):
model = Category
template_name = 'forums/index.html'
context_object_name = 'category'
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 Cat
context['category'] = Category.objects.all()
return context
HTML
{% block content %}
{% for cat in category %}
<div style="padding-top: 20px;">
<div class="row">
<div class="bg-success rounded-top border border-dark" style="width:100%; padding-left:8px;">
{{ cat.cat_name }}
</div>
</div>
{% for forum in cat.forums.all %}
<div class="row">
<div class="bg-secondary border border-dark" style="width:100%; padding-left:16px;">
{{ forum.forum_name }}
{% for threads in forum.threads.all %}
<div class="float-right" id="latest-post">
<p>{{ threads.title }}</p>
<p> {{ threads.author.username }} </p>
</div>
{% endfor %}
</div>
</div>
{% endfor%}
</div>
{% endfor %}
{% endblock content %}
Problem
I am building a forums and am trying to get my homepage to show the last post in a forum.
I have got it to work to show all threads, but i just want the latest one to show on the latest post div.
I setup the get_latest_by on the Thread model so that it gets the latest one by the time created.
How would i be able to get this to display the latest thread?
You can set a property on the Form model, and then call it in the template.
views.py
class Form(models.Model):
...
#property
def get_newest_thread(self):
return self.threads.all().order_by('date_posted').first()
html
{% with threads=forum.get_newest_thread %}
<div class="float-right" id="latest-post">
<p>{{ threads.title }}</p>
<p> {{ threads.author.username }} </p>
</div>
{% endwith %}

Get Featured Image from different Model

I have 2 model objects, Business & BusinessImage as so, listed with views and index.html. I am trying to list the business's featured image, but it's not happening. I am getting the following error:
'QuerySet' object has no attribute 'businessimage_set'
How can I get the business featured image for a list?
Business
class Business(models.Model):
name = models.CharField("Name", max_length=70, default="Business Name")
slug = models.SlugField()
description = models.TextField("About", max_length=400)
category = models.ManyToManyField(Category, verbose_name="Categories", blank=True)
order = models.IntegerField("Order", default=0)
claimed = models.BooleanField("Claimed", default=False)
featured = models.BooleanField("Featured", default=False)
class Meta:
ordering = ['order']
verbose_name = "Business"
verbose_name_plural = "Businesses"
def __str__(self):
return self.name
BusinessImage
class BusinessImage(models.Model):
business = models.ForeignKey(Business)
image = models.ImageField(upload_to="images/business")
title = models.CharField(max_length=120)
featured = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return self.title
view.py
from .models import Business, BusinessImage
def index_view(request):
latest_business_list = Business.objects.all()
images = latest_business_list.businessimage_set.all()
template = loader.get_template('index.html')
context = RequestContext(request, {
'latest_business_list': latest_business_list,
'images': images,
})
return HttpResponse(template.render(context))
index.html
{% block content %}
<div class="text-center business_title">
<h2>Featured</h2>
</div>
{% if latest_business_list %}
{% for business in latest_business_list|slice:":4" %}
{% if business.active %}
<div class="col-sm-6 col-md-3">
<li>{{ business.name }}</li>
{% for image in latest_business_list.businessimage_set.all %}
{% if image.featured %}
<a href="{% url 'single_product' product.slug %}">
<img src="{{MEDIA_URL}}{{image.image}}" alt="{{image}}">
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}
businessimage_set is an attribute of a Business instance, but you're trying to access it as an attribute of a queryset (i.e. list of businesses). If your goal is just to be able to access the images for each business in a template, you can leave out images entirely. Instead your template would have:
{% for image in business.businessimage_set.all %}
(Though look into prefetch_related for efficiency.)