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 %}
Related
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)
Well, I am creating a user profile where the user can see his all posts which he has been uploaded. But I don't understand one thing that how could I possibly grab the fields of Post model from Posts/models.py and show them on the template which I have created in another app (Profiles) templates.
The reason I am trying to access them on other app is that I want to show them in the userprofile.html template. Just like Facebook posts. And please tell me if you know that it is not possible with django?
posts/models.py :
class Post(models.Model):
username = models.ForeignKey(User, verbose_name=("user name"), on_delete=models.CASCADE)
description = models.CharField(('Description'),max_length=250)
title = models.CharField(('Content Title'), max_length=250)
create_date = models.DateTimeField(default = timezone.now)
image_data = models.ImageField(upload_to='User_Posts', height_field=None, width_field=None, max_length=None)
def __str__(self):
return self.title
profiles/views.py
from posts.model import Post
from django.views.generic import ListView
class UserPostListView(ListView):
model = Post
context_object_name = 'userpost_list'
template_name = 'profiles/userprofile.html'
def get_queryset(self):
user = get_object_or_404(User, username = self.kwargs.get('username'))
return Post.object.filter(username = user).order_by('-create_date')
profiles/templates/profiles/userprofile.html
<div class="jumbotron">
{% for post in userpost_list %}
<div class="post">
<h1>{{ posts.post.title }} <img src="" alt="not found" height="60" width="60" style="float:right ;border-radius: 20px;" ></h1>
<div class="date">
<p>
<!-- Published on: {{ object.author.post.create_date|date:"D M Y" }} -->
</p>
</div>
</div>
{% endfor %}
</div>
</div>
You can access any app from any other app. You just need to do the necessary model imports which you are doing. Looks like you just need to tweak your line of code in the template from:
<h1><a href="">{{ posts.post.title }}...
to:
<h1><a href="">{{ post.title }}...
and when you decide to use it.
<!-- Published on: {{ object.author.post.create_date|date:"D M Y" }} -->
to:
<!-- Published on: {{ post.create_date|date:"D M Y" }} -->
The reason is that your queryset is returning a dataset of the Post model. So you are already in it.
It just done by importing model from the app you want to use model to other app. And that it. This is python OOP(object oriented programming) concept.
I am trying to ad a condition into an html model in Django (python) and it is not working:
This is my HTML:
<p class="card-text text-muted h6">{{ post.author }} </p>
{% if post.author == 'John_Woo' %}
<p class="card-text text-muted h6"> NOK </p>
{% else %}
<p class="card-text text-muted h6"> ok </p>
{% endif %}
Not sure what is wrong here...
Even though I have a John_Woo author, I only get ok message
This is my models.py:
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
This is my views.py:
from django.views import generic
from .models import Post
class PostList(generic.ListView):
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'index.html'
class PostDetail(generic.DetailView):
model = Post
template_name = 'post_detail.html'
Post.author returns class's representation like(str, unicode, repr) function. You have to override this functions which is depends on your django version. But you have one way to compare this. Like if Post.author.first_name == "John_Woo"
You want post.author.name (where .name is whatever field of an User object that will sometimes contain the string 'John_Woo' )
I am unable to design a code to render one particular post and it's related comments. The issue is maybe in views.py or the url.
I have looked at multiple sources without any results. I am a novice to coding and feel like I am missing some essential point. Posts and comments are getting created correctly and all comments get the correct post_id assigned.
My models.py is set up like this:
class Post(models.Model):
title = models.CharField(max_length=1000)
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('blog-home')
class Comment(models.Model):
cid = models.AutoField(primary_key=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment = models.TextField()
comment_date = models.DateTimeField(default=timezone.now)
def save(self, *args, **kwargs):
super(Comment, self).save(*args, **kwargs)
def __str__(self):
return self.comment
def get_absolute_url(self):
return reverse('blog-home')
My views.py is set up like this:
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comment_list'] = Comment.objects.filter(post=WHAT SHOULD GO HERE?)
return context
I need to pass the Post.id or primary key of the post in the filter above. Can someone explain what it should be?
The url used to access the post detail is as follows:
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail')
I get the post detail view rendered out as the author, title and content of the post when I have the following in views.py:
class PostDetailView(DetailView):
model = Post
The template for that is as below:
{% extends "blog/base.html" %}
{% block content%}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{object.author.profile.image.url}}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-posts' object.author.username %}">{{ object.author }}</a>
<small class="text-muted">{{ object.date_posted|date:"F d, Y P e" }}</small>
{% if object.author == user %}
<div><a class="btn btn-secondary btn-sm m-1 mb-1" href="{% url 'post-update' object.id%}">Update</a>
<a class="btn btn-danger btn-sm m-1 mb-1" href="{% url 'post-delete' object.id%}">Delete</a></div>
{% endif %}
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
</div>
</article>
{% for comment in comment_list %}
<div class='article-content'>
<p>{{comment}}</p>
</div>
{% endfor %}
{% endblock %}
How should I take the post.id or pk of the Post and use it to filter the comments related only to that particular post?
Also, what is a good way to set up a template for rendering the queryset?
You should be able to iterate over the reverse link from Post object to the Comment objects linked to it (by default as comment_set) in your template:
{% for comment in post.comment_set %}
If you want greater control you inject a queryset into the context, something like this, to get the most recent six comments only.
"comments": post.comment_set.order_by("-comment_date")[:6]
The post object selected should be available as self.object in a DetailView and will be default be injected into the render context as object. An invaluable resource for navigating the structure of Django Class-based views is the Classy CBV
site.
Warning: this is "off the top of my head" so don't assume it's all perfect.
A single object will have access to its related objects.
Try this:
class PostDetailView(DetailView):
model = Post
# There's no need to define get_context_data() here
Now your template will have the post available as post (and also object).
All of the comments that have this post are available on the template like this:
{% for comment in post.comment_set.all %}
<div class='article-content'>
<p>{{ comment }}</p>
</div>
{% endfor %}
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 %}