Django - Can't access data from object in many to many relationship - python

In my Django project, I am trying to create a website that streams TV shows. Each show belongs in many categories, hence the use of many to many relations in my model. What I want to do with a certain page on my website is dynamically load a page of shows belonging to a specific category. However, all of my attempts have ended in failure as I am unable to figure out a way on how to access the actual category data from each show.
In views.py
def shows_in_category(request, category_slug):
category = get_object_or_404(Category, slug=category_slug)
showsall = theShow.objects.all()
shows = []
for show in showsall:
print(show.category.name, category.name)
if show.category.name == category.name:
shows.append(show)
print(shows)
return render(request, 'show/show_list_view.html', {'category':category, 'shows': shows})
In models.py
class Category(models.Model):
name = models.CharField(max_length=255, db_index=True)
slug = models.SlugField(max_length=255, unique=True)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("theshowapp:shows_in_category", args=[self.slug])
class theShow(models.Model):
english_name = models.CharField(max_length=400)
show_type = models.CharField(max_length=200, blank=True)
is_active = models.BooleanField(default=False)
category = models.ManyToManyField(Category)
slug = models.SlugField(max_length=400,unique=True)
class Meta:
verbose_name_plural = 'Shows Series'
def __str__(self):
return self.english_name
In the template (show_list_view.html)
{% for show in shows %}
<script> console.log("I'm trying to get in")</script>
<script> console.log("{{ show.name }} {{show.category.name}}")</script>
<script> console.log("I'm in")</script>
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-6">
<div class="product__item">
<div class="product__item__text">
<ul>
{% for genre in show.category %}
<li>{{ show.category }}</li>
{% endfor %}
</ul>
<h5>{{ show.english_name }}</h5>
</div>
</div>
</div>
</div>
{% endfor %}
Any insight on this matter would be much appreciated.

What you're doing here violates some of the best practices of Django and also isn't using the Django ORM to its full potential. Please replace the lines
showsall = animeShow.objects.all()
shows = []
for show in showsall:
print(show.category.name, category.name)
if show.category.name == category.name:
shows.append(show)
print(shows)
with
shows = animeShow.objects.filter(category__name=category.name)
Also in the template change <li>{{ show.category }}</li> to <li>{{ genre }}</li> since that's the iterating variable.

I read up a bit more on the many to many fields examples in Django's documentation and figured out that I should use this:
shows = animeShow.objects.all().filter(category__name=category)

Related

Getting the number of comments from a specific post in django

I have a little problem with a query. I work on a blog website with django. For posts I have the first page where i display all the posts as a list, with their details (title, date posted etc.) and I want to display the number of comments for each post along with title, date posted and tags. I'm not sure how to make that, I need to implement something on the model classes or in view function that renders the page ?
Here are the model classes.
class Post(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=500)
content = models.TextField()
tags = models.CharField(max_length=100)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment_text = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return f'{self.user.username} Comment ' + str(self.id)
and the view function
def blog(request):
context = {
'posts': Post.objects.all(),
'title': 'Blog',
'banner_page_title': 'Blog',
'page_location': 'Home / Blog'
}
return render(request, 'blog/blog.html', context)
Method 1:
You can use this in your template.
{% for post in posts %}
{{ post.comment_set.count }}
{% endfor %}
Method 2:
You can implement a model method like this:
class Post(models.Model):
....
def __str__(self):
return self.title
#property
def comment_count(self):
return self.comment_set.count()
And you can call the model method in your template like this:
{% for post in posts %}
{{ post.comment_count }}
{% endfor %}
Use a reverse lookup in the template and count to get the number of related objects:
{{ post.comment_set.count }}
Something like this?
template.html
...
{% for post in posts %}
<div> Title: {{post.title}} </div>
<div> Date Posted: {{post.date_posted}} </div>
<div> Number of Comments: {{post.comment_set.count}} </div>
{% endfor %}

Django Getting Data From Foreign Key

Im a newbie working on a news site (or at least trying to, a lot of "problems" in the last few days lol ) trying to learn Django the best I can.
This is what I want to do :
I have an Article Model, it used to have 6 image fields that I used to send to the template and render the images, each image field had its own name and all was well in the world.
Then I got tasked with puting the Article images in a separate Image model.
So I did this :
class Article(models.Model):
title = models.CharField('title', max_length=200, blank=True)
slug = AutoSlugField(populate_from='title', default="",
always_update=True, unique=True)
author = models.CharField('Author', max_length=200, default="")
description = models.TextField('Description', default="")
is_published = models.BooleanField(default=False)
article_text = models.TextField('Article text', default="")
pub_date = models.DateTimeField(default=datetime.now, blank=True)
article_category = models.ForeignKey(Category, on_delete="models.CASCADE", default="")
def __str__(self):
return self.title
class ArticleImages(models.Model):
article = models.ForeignKey(Article, on_delete="models.CASCADE", related_name="image")
image = models.ImageField("image")
name = models.CharField(max_length=50, blank=True)
But so far I wasnt able to access my images in my template using
{{ article.image.url }} or {{ article.image.image.url }}
or any other combination. Why is that ?
Did I set up my models correctly ? One person suggested that I should change the model field from ForeignKey to OneToOneField, but I didn't get much feedback on why and how ?
So, how would I make a for loop that loops through the Articles model and then gets the related images for each Article ? I essentially want it to behave like I still have the 6 different fields like I did before. ( I have to do it this way, it's a part of the task ).
here are my views and my "index" template that I used to loop through the Articles and display 6 latest news on my home page. (please ignore the tags,I am aware they aren't working like this..the template is just so you can understand what I am talking about )
my views.py:
class IndexView(generic.ListView):
template_name = 'news/index.html'
context_object_name = 'latest_article_list'
def get_queryset(self):
return Article.objects.all()
class CategoryView(generic.ListView):
template_name = 'news/categories.html'
context_object_name = 'category'
def get_queryset(self):
return Article.objects.filter(article_category__category_title="Politics")
class ArticlesView(generic.ListView):
context_object_name = 'latest_article_list'
template_name = 'news/articles.html'
paginate_by = 5
def get_context_data(self, **kwargs):
context = super(ArticlesView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
def get_queryset(self):
category_pk = self.request.GET.get('pk', None)
if category_pk:
return Article.objects.filter(article_category__pk=category_pk).order_by("-pub_date")
return Article.objects.order_by("-pub_date")
def article(request, article_id):
article = get_object_or_404(Article, pk=article_id)
context = {'article': article,
'article_category': article.article_category.category_title}
return render(request, 'news/article.html', context)
template that I used with my old model :
{% for article in latest_article_list %}
<img class="single-article-img" src="{{ article.image.name.url }}" alt="">
<div class="container row">
<!-- Start Left Blog -->
<div class="article mt-10 col-md-4 col-sm-4 col-xs-12">
<div class="single-blog" style="margin:10px auto;">
<div class="single-blog-img">
<a href="{% url 'news:article' article.id %}#article-title">
<img class="for-imgs" src="{{ article.image.url }}" alt="">
</a>
</div>
<div class="blog-meta">
<span class="date-type">
<i class="fa fa-calendar"></i>{{ article.pub_date }}
</span>
</div>
<div class="xx blog-text">
<h4>
{{ article.title }}
</h4>
<p>
{{ article.description|truncatewords:30 }}
</p>
</div>
<span>
Read more
</span>
</div>
</div>
{% endfor %}
Thank you !
You need to loop over the images as you have many images against a single article object. You can have something like below to show images in your template:
{% if latest_article_list.articleimages %}
{% for articleimage in latest_article_list.articleimages.all %}
<img src="{{ articleimage.image.url }}" class="d-block w-100" alt="...">
{% endfor %}
{% endif %}

Django blog - calling latest 3 posts per category via many to many

So far my blog project has :
Models.py
class Category(models.Model):
title = models.CharField(max_length=200)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "categories"
class Post(models.Model):
title = models.CharField(max_length=200)
summary = models.CharField(max_length=500, default = True)
body = models.TextField()
pub_date = models.DateTimeField(default=timezone.now)
categories = models.ForeignKey('Category', default=True)
def __str__(self):
return self.title
Views
from django.shortcuts import render
from .models import Post, Category
def getAllCategories(request):
categories = Category.objects.all()
context = {
'categories':categories,
}
return render(request, 'categories/getAllCategories.html', context)
Template
{% extends "../posts/base.html" %}
{% block content %}
<div>
{% for category in categories %}
<div>
<h3>{{ category.title }}</h3>
<ul>
{% for post in category.post_set.all %}
<li><a href=#>{{post.title}}</a></li>
- {{post.summary}}
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
{% endblock %}
This pulls through all of my posts per category fine - however I'm stuck as I would like to only pull through the latest three posts per category. I understand that in a simple view you can use order_by()[:3], but then you can't use post_set with that. So I need a way of filtering by top three posts within a category, so using the many-to-many relationship. Hope this is clear.
Many thanks

Django QuerySet in template for relational models

I am trying to setup a relation with another model using FK in django but I can't call FK related model fields in django templates. In template when I call "provider.name" it shows me "None" as result. I do have 5 different suppliers listed and can see from admin. I have providers and I got products like below:
class Product(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=20, decimal_places=2)
sku = models.CharField(null=True, max_length=100)
url = models.URLField(blank=True)
slug = models.SlugField(unique=True)
providers = models.ManyToManyField('Provider', blank=True)
def __unicode__(self):
return self.title
class Provider(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
logo = models.ImageField(upload_to='products/')
shipping_method = models.CharField(max_length=250)
shipping_time = models.CharField(max_length=250)
return_policy = models.CharField(max_length=200)
free_delivery = models.CharField(max_length=200)
customer_service_number = models.CharField(max_length=200)
shipping_charges = models.CharField(max_length=200)
def __unicode__(self):
return self.name
In template the way I am calling it is:
{% with provider=object.providers %}
{{ provider.name }}
{% endwith %}
As result it shows "None". I am trying to call provider in a different template. In it's own List/Detail generic views it works just fine. I am trying to call it in different app templates. Please advise.
Update................
My search.html for whoosh search engine includes
{% for result in page.object_list %}
<div class="col-lg-1-5 col-md-3 col-sm-4 col-xs-12">
<div class="pbox">
<div class="photo">
<img src="{{ result.object.get_image_url }}" alt="product">
</div>
<div class="info">
<div class="url">
name
{% for item in result.object.providers.all %}
{{ result.provider.name }}
{% endfor %}
</div>
<div class="description">
{{ result.object.description|truncatewords:7 }}
</div>
<div class="price">
{{ result.object.price|intcomma }}
</div>
</div>
</div>
</div>
As I am not using any ModelManager or just using default Managers pre-defined for models. I've added a line of code under Product App model like this:
def get_provider(self):
item = self.providers.all()
return item
I case of calling provider you call related manager so it return None because manager have no method like this.
If you want to retrieve objects from managers, you should call self.providers.all()
In your case, template tags should looks like this:
{% for item in object.providers.all %}
{{ provider.name }}
{% endfor %}

Search Field in Django Python

First, I have to say that is this my first application in Django. So my knowledge is still limited.
I have this home page where it shows all the data in my model. The model name is "Asset".
I am trying to have a search field inside the home page.
models.py
class Asset(models.Model):
asset_desc = models.CharField(max_length=120, null=False)
BEIRUT = 'Beirut'
SAIDA = 'Saida'
HALBA = "Halba"
base_choice = ((SAIDA, "Saida"), (BEIRUT, "Beirut"), (HALBA, "Halba"))
asset_base = models.CharField(max_length=120, null=False, choices=base_choice)
created_date = models.DateField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True)
asset_user = models.CharField(max_length=120, blank=True)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.asset_desc)
super(Asset, self).save(*args, **kwargs)
def __str__(self):
return self.asset_desc
views.py
def search_asset(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
assets = Asset.objects.filter(asset_desc__icontains=q)
context = {'desc': assets}
return render(request, 'home.html', context)
html for the search field:
<form method="GET" class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search..."id="search_box" name="q">
urls.py
url(r'^search/$', "asset.views.search_asset", name="home")
Please any help on why it is not showing the result. I am using Django 1.9.
some corrections:
you dont need null=False for TextField() and CharField(), since they never save Null to database but empty string. so you can remove null=False
the search url name is home which logically not really approriate. it should be changed to search or search_view and then you can refer to it via url tag:
action="{% url 'search' %}"
this is useful if someone should look over your code. "Readability counts" ;)
and finally, put this to your home.html (actually you must already have it)
{% for asset in desc %}
<div>
{{ asset.asset_desc }} <br>
{{ asset.base_choice }} <br>
{{ asset.asset_user }}
</div>
{% endfor %}
I hope, this helps
You have not provided the template or the HTML portion where you list the results. You should consider the name of you context variable, but by following your name, you should list the results like this:
{% for asset in desc %}
<div>
{{ asset }}
</div>
{% endfor %}
Anything else looks correct.
Hope it helps

Categories