How to get value in template from model through another one? - django

I have two models connected by one-to-many relation. How can I get value from one field of one model through another? I need to get value "low" from Image model from the Album model. In my Image model "low" is a path to a low-res copy of image and "path_to_image" is the path to full-size one.
I use Django 2.1, build-in sqlite db.
models.py:
from django.db import models
from django.utils.timezone import now
class Album(models.Model):
title = models.CharField(max_length=120)
def __str__(self):
return self.title
class Image(models.Model):
path_to_image = models.CharField(max_length=120)
low = models.CharField(max_length=120)
pub_date = models.DateField(default=now)
album = models.ForeignKey(Album, on_delete=models.CASCADE)
def __str__(self):
return self.path_to_image
views.py:
def albums(request):
albums_list = Album.objects.all()
paginator = Paginator(albums_list, 20)
page = request.GET.get('page')
albums = paginator.get_page(page)
random_idx = random.randint(0, Image.objects.count() - 1)
image = Image.objects.all()[random_idx]
context = {
'albums': albums,
'image': image,
}
return render(request, 'gallery/albums.html', context)
albums.html:
<div class="item2 my-gallery responsive" itemscope itemtype="http://schema.org/ImageGallery">
{% for album in albums %}
<div class="gallery">
<a href="/albums/{{ album.id }}">
<img src="{{album.image_set.model.low}}" width="300" height="225">
</a>
<div class="desc">{{album}}</div>
</div>
{% endfor %}
</div>
I want to get value from Image model im that <img> tag. So, is it possible without refactoring all code entirely?
(Also, in the views.py I tried to get random image from album to set it as a preview, but it doesn't work too.)

In order to get the low value for the first Image in every Album you can do the following:
albums.html:
<div class="item2 my-gallery responsive" itemscope itemtype="http://schema.org/ImageGallery">
{% for album in albums %}
<div class="gallery">
<a href="/albums/{{ album.id }}">
<img src="{{album.image_set.first.low}}" width="300" height="225">
</a>
<div class="desc">{{album}}</div>
</div>
{% endfor %}
</div>
The only difference is {{album.image_set.first.low}} where we select the first item in the set.

From what I understand your current view returns a random image, which might not be related to the album at all. I would iterate through the albums and find a random image for each album if this is relevant. Like so:
def albums(request):
albums = Album.objects.all()
albums_list = []
for album in albums:
album_list.append({
album_images = Image.objects.filter(album=album)
'id'=album.pk,
'image_low'= album_images[random.randint(0, album_images.count() - 1)].low
'title'=album.title
})
paginator = Paginator(albums_list, 20)
page = request.GET.get('page')
albums = paginator.get_page(page)
context = {
'albums': albums,
'image': image,
}
return render(request, 'gallery/albums.html', context)
Then you can access the albums in the template like this
<div class="item2 my-gallery responsive" itemscope itemtype="http://schema.org/ImageGallery">
{% for album in albums %}
<div class="gallery">
<a href="/albums/{{ album.id }}">
<img src="{{album.image_low}}" width="300" height="225">
</a>
<div class="desc">{{album.title}}</div>
</div>
{% endfor %}
</div>

Related

Django: How to Join One-to-many relationship? / Show joined query in template

I am new in Django. I am trying to make a clone of craiglist. Currently I have to models: Posts and Media (images). One post can have many Media. I want to show first image that was posted by user when they create a post as well as info about post on the index page.
Unfortunately I can't properly join Media when I refer to Posts and vice versa. How to properly show post and media instances in index template?
Models:
class Post(models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=1500)
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
subcategory = models.ForeignKey(Subcategory, on_delete=models.SET_NULL, null=True)
price = models.DecimalField(max_digits=12, decimal_places=2)
city = models.CharField(max_length=200)
class Media(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
name = models.CharField(max_length=500, null=True, blank=True)
photo = models.ImageField(upload_to='photo/%Y/%m/%d/', null=True, blank=True)
Views:
def index(request):
posts = models.Media.objects.all()
paginator = Paginator(posts, 2)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'aggregator/index.html', {'page': page_obj})
index.html template:
{% for media in page %}
<div class="col">
<div>
<p>Picture</p>
</div>
<div>
<h4>{{ media.post.title|upper }}</h4>
</div>
<div>
<h5>{{media.post.price}}$</h5>
</div>
<div>
<h6>{{media.post.city}}</h6>
</div>
</div>
{% endfor %}
So again. How to normally join first media related to the posts? Like in the attached screenshot.
I found a solution to get posts from Media:
posts = models.Media.objects.all()
instead of getting posts themselves (i know this is horribly wrong) because I do not understand how to join Media: select_related and prefetch_related don't work - there is an error like this:
posts = models.Post.objects.select_related('Media').all()
Invalid field name(s) given in select_related: 'Media'. Choices are: user, category, subcategory
You can prefetch with:
def index(request):
posts = models.Post.objects.prefetch_related('media_set')
paginator = Paginator(posts, 2)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'aggregator/index.html', {'page': page_obj})
Then in the template render with:
{% for post in page %}
<div class="col">
<div>
{% if post.media_set.all.0.photo %}
<img src="{{ post.media_set.all.0.photo.url }}">
{% endif %}
</div>
<div>
<h4>{{ post.title|upper }}</h4>
</div>
<div>
<h5>{{ post.price }}$</h5>
</div>
<div>
<h6>{{ post.city }}</h6>
</div>
</div>
{% endfor %}
The .prefetch_related is not even necessary, but it will slow the view down, because then it will make a query per Post, not one extra query to fetch all Media objects.

How do I display a picture in Django's DetailView based on a foreign key relationship?

I am currently building a website with Django that will host music and album/song details. My models are set up so that each song has a foreign key to it's associated album, or to a database object titled "Singles" if they are not part of an album.
This relationship between song and album has worked great while building the site, until I got to the 'Play Song' page I am currently working on. Each Single has an associated artwork in the 'Song' model, while each song in an album has no 'picture' in the database, as the 'picture' is a part of the Album model and not the Song model in these instances. I am attempting to pass data from both the Song model and the Album model into my DetailView so that if the song being played is from an album rather than a single, it takes the 'picture' from the Album model it has a foreign key to rather than from it's own internal 'picture' object. My code is below, it renders the 'picture' for Singles perfectly, but cannot render the 'picture' object from the album and instead shows my {% else %} condition of a object not found picture. The HTML file has the logic I am using to find the associated picture:
{% elif song in album.song_set.all %}
<img src="{{ album.picture.url }}">
Any help would be appreciated.
models.py
class Album(models.Model):
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Must be at least two characters.")]
)
release_date = models.DateField()
picture = models.ImageField(upload_to='albums/', blank=True)
content_type = models.CharField(max_length=256, blank=True, help_text='The MIMEType of the file')
description = models.TextField(blank=True)
def __str__(self):
return self.title
def __unicode__(self):
return self.title
class Song(models.Model):
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Must be at least two characters.")]
)
release_date = models.DateField(blank=True)
length = models.CharField(
max_length=200)
featured_artists = models.CharField(
max_length=200,
blank=True)
picture = models.ImageField(upload_to='singles/', blank=True)
content_type = models.CharField(max_length=256, blank=True, help_text='The MIMEType of the file')
description = models.TextField(blank=True)
alb = models.ForeignKey(Album, on_delete=models.CASCADE)
audio_file = models.FileField(upload_to='music_audio/', blank=True)
def __str__(self):
return self.title
views.py
class PlaySongView(DetailView):
model = Song
template_name = 'music/play_song.html'
def get_context_data(self, **kwargs):
context = super(PlaySongView, self).get_context_data(**kwargs)
context['album'] = Album.objects.exclude(title="Single")
return context
urls.py
urlpatterns = [
path('', views.MusicView.as_view(), name='music'),
path('<int:pk>/', views.AlbumDetailView.as_view(), name='album_detail'),
path('<int:pk>/play', views.PlaySongView.as_view(), name='play')
]
play_song.html
{% load static tailwind_tags %}
<!DOCTYPE html>
<head>
<title>LewnyToons - {{ song.title }}</title>
{% tailwind_css %}
</head>
<body class="antialiased text-slate-400 bg-slate-900">
<div class="w-full sm:w-9/12 lg:w-8/12 px-4 sm:pr-2 lg:pr-4 mx-auto">
<div class="flex flex-col font-bold text-2xl text-center items-center text-white mx-auto pt-2">
<h1 class="mb-4">Listen to {{ song.title }} now!</h1>
{% if song.picture %}
<img src="{{ song.picture.url }}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% elif song in album.song_set.all %}
<img src="{{ album.picture.url }}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% else %}
<img src="{% static 'something_wrong.jpg' %}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% endif %}
</div>
{% if song.audio_file %}
<span class="flex justify-center py-10">
<div>
<audio controls><source src="{{ song.audio_file.url }}" type="audio/mpeg"></audio>
</div>
</span>
{% else %}
<p>The file could not be found.</p>
<a href="{% url 'the_music:music' %}" class="text-white bold_underline">Return to music.</p>
{% endif %}
</div>
</body>
</html>
The issue lies within the album context passed to the template in PlaySongView.get_context_data().
You are passing a QuerySet of albums instead of the album object the song is related to. So when you try to do album.song_set.all, you're running that on a QuerySet instead of an Album object. The condition is not met, hence the fallback to the else block.
Instead of:
context['album'] = Album.objects.exclude(title="Single")
A better way to approach this in get_context_data() would be:
song = self.get_object()
context['album'] = song.alb
self.get_object() will look for a pk in your URL kwargs (in this case, '<int:pk>/play') and lookup the song based on its primary key.
Then in your template, it's as simple as doing:
{{ album }}
There is no need to do a reverse relation in the template here because you have direct access to the album from your song DetailView with self.get_object(). Plus, this way you're not making unnecessary queries by fetching all your albums and passing them as context to the template. All you need is that 1 album.
As a general rule of thumb: if you can accomplish what you need query-wise in the view, it's typically a more optimal solution.

Django _Categorize products

I want to show specific categories of items on a single page. Each category has a subheading and specific categories in the group. Whenever i run the enve, everything is good but each subcategories output all the products, even the ones in other categories. How can i rectify this.
def waterProducts(request):
category = request.POST.get('category')
if category == None:
products = Product.objects.order_by('-price').filter(is_published=True)
else:
products = Product.objects.filter(categoryname=category)
categories = Category.objects.all()
context = {
'products': products,
'categories': categories
}
return render(request, "products/WaterProductPage.html", context)
Above is my view.py files where i confirm if there is any category.
<main style="background-image: url(images/waterrocks.jfif)">
<section class="body-section">
<div class="all-products">
<!--Water coolers-->
{%for category in categories%}
<section class="main-products cooler">
<div class="upper-bar">
<div>
<h2>{{category.name}}</h2>
</div>
<div class="sortby">
<span>Sort By: <select class="sort">
<option value="highest">Highest Price</option>
<option value="lowest">Lowest Price</option>
</select></span>
</div>
</div>
<hr />
<!--Single Products-wrap-->
<div class="specific-product-wrap specific-product-wrap-cooler">
{%if products%}
{%for product in products%}
<a href="{%url 'product' product.pk%}">
<div class="specific-single-product">
<div class="product-image center">
<img class="product-image-shape" src="{{product.image.url}}" alt="adamol Distilled" />
</div>
<div class="produc-descriptions">
<h4 class="specific-product-title">{{product.title}}</h4>
<p class="one-line-description"></p>
<p class="price">Ksh.{{product.price}}</p>
<button type="button" class="AddToCart">Add To Cart</button>
</div>
</div>`
</a>
{%endfor%}
{%else%}
<p>No Product Lol</p>
{%endif%}
</div>
</section>
{%endfor%}
The above is my html template where i output each category with related product.
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Product(models.Model):
image = models.ImageField(null=False, blank=False)
title = models.CharField(max_length=2000, null=False, blank=False)
category = models.ForeignKey(
Category, on_delete=models.CASCADE, default=True, null=False)
price = models.DecimalField(max_digits=5, decimal_places=2)
description = models.TextField()
is_published = models.BooleanField(default=True)
created_at = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.title
The above code is for model.py where i declaire category as a foregn key.
from django.urls import path
from .import views
urlpatterns = [
path('waters', views.waterSamples, name='waters'),
path('services', views.waterServices, name="services"),
path('products', views.waterProducts, name='products'),
]
The above code is urls.py
I want the products to be shown like this...
on the same page
enter image description here
You fetch the products related to a Category with category.product_set, so:
{% for category in categories %}
<!-- … -->
<h2>{{category.name}}</h2>
<!-- … -->
{% for product in category.product_set.all %}
<a href="{%url 'product' product.pk %}">
<!-- … -->
<h4 class="specific-product-title">{{ product.title }}</h4>
<!-- … -->
{% empty %}
<p>No Product Lol</p>
{% endfor %}
{% endfor %}
The category.product_set.all will retrieve all related Products for a given Category, and thus not render all the ones.
If you only want to use Products with is_published=True and ordered by price, you can use a Prefetch object:
from django.db.models import Prefetch
def waterProducts(request):
categories = Category.objects.prefetch_related(Prefetch(
'product_set',
queryset=Product.objects.filter(is_published=True).order_by('-price')
))
context = {
'categories': categories
}
return render(request, "products/WaterProductPage.html", context)
In your views file in the else part, shouldn't it be
products = Product.objects.filter(category=category)

How can I display the image to the desired product category? (if there are two separate models (Product and Image))

I can't figure out how to connect two models (product and images) in views and output images in html.
At the moment, several images are loaded for a specific project (for example, photos of a 3D model) all this through the admin panel. There are several projects, that is, 3D models, engineering and so on. And now I can't display 1 of those uploaded images on my site for each product (project). That is, either all the images that were uploaded are displayed (both 3d models and books and engineering).
Or if you use model.objects.first () displays the very first uploaded image( that is, the same for all projects).
My models:
class Portfolio (models.Model):
modeling='Modeling'
books ='Books'
engineering='Engineering'
category_ch = [
(modeling, 'Modeling'),
(books, 'Books'),
(engineering, 'Engineering'),
]
category = models.CharField('Category',max_length=100,choices=category_ch, default=modeling)
name = models.ForeignKey (Teams, related_name='maked', on_delete=models.PROTECT,blank=True)
short_discription = models.CharField("Name of the work", max_length=200, blank=True)
discription = models.TextField('Discription', blank=True)
сustomer = models.CharField('Customer', max_length=100, default='Заказчик')
created = models.DateTimeField('Date',auto_now_add=True)
class Meta:
verbose_name= 'Portfolio'
verbose_name_plural = 'Portfolios'
def __str__(self):
return self.short_discription
#def get_absolute_url(self):
#return reversed ('shop:product_detail', args=[self.category.slug, self.slug]')
class Image(models.Model):
image = models.ImageField('Picture of work', upload_to='products/%Y/%m/%d', blank=True)
product = models.ForeignKey(Portfolio, default=None, related_name='image', on_delete=models.PROTECT)
Views:
def portfolio_page(request):
portfolio = Portfolio.objects.all()
image_work= Image.objects.all()
ctx = {
'portfolio': portfolio,
'image':image_work,
}
return render(request, 'mainApp/portfolio_page.html',ctx)
HTML:
{% for el in portfolio %}
<div class="portfolio_db">
<h3> {{ el.short_discription }} </h3>
{% for i in image %}
<img class="photo_work" src="{{ i.image_work }}" alt="Oh, there is something wrong here" width="155px" height="215px"></img>
{% endfor %}
<h4> Maked by {{ el.name }} </h4>
<h4> Ordered by {{ el.сustomer }} </h4>
</div>
{% endfor %}
You can enumerate over the el.image_set.all in the template, so:
{% for el in portfolio %}
<!-- … -->
{% for i in el.image_set.all %}
<img class="photo_work" src="{{ i.image.url }}" alt="Oh, there is something wrong here" width="155px" height="215px"></img>
{% endfor %}
<!-- … -->
{% endfor %}
in the view, we can boost performance by fetching all related Images and do the JOINing at the Django/Python layer:
def portfolio_page(request):
portfolio = Portfolio.objects.prefetch_related('image_set')
ctx = {
'portfolio': portfolio
}
return render(request, 'mainApp/portfolio_page.html',ctx)

Pass multiple objects to templates with render

I'm beginner on Django.
I have a project with the following models:
My Articles models:
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=160)
content = models.TextField()
image = models.ImageField(upload_to=upload_location)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
categorie = models.CharField(max_length=200)
categorie = models.ForeignKey('categorie.Categorie')
publier = models.BooleanField()
My Portfolio categories models which is linked with my Article Model:
class Categorieport(models.Model):
title = models.CharField(max_length=200)
article = models.OneToOneField('posts.Post')
And finally, my portfolio models with all the photos:
class Portfolio(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to=upload_location)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
categorieportfolio = models.ForeignKey('Categorieport')
In one view and one template, i'd like to display information concerning the article and the portfolio related to the article.
I wrote the following view:
def portfolio(request, article=None):
portfolio = get_object_or_404(Categorieport, article=article)
image_portfolio = portfolio.portfolio_set.all()
return render(request, 'portfolio1.html', {'portfolio': portfolio, 'image_portfolio': image_portfolio})
And the following templates:
<div class="titre-display">
<h2>{{ portfolio.article.timestamp|date:"d E Y" }} / {{ portfolio.article.categorie}} </h2>
<h1>{{ portfolio.article.title}}</h1>
</div>
<div class="content-display">
<div class="content-display-main">
<div class="content-display-main-portfolio">
<div class="image-portfolio">
<a class="example-image-link" href="{{ image_portfolio.image.url }}" data-lightbox="example-set" data-title="{{image_portfolio.title}}">
</a>
I can access to information from my article but i can't access information from my portfolio. I tried it with the shell, and it works. I can't figure out why it doesn't work in my view and template.
Do you have any idea?
Thank you in advance
Singertwist
Your image_portfolio is a querySet, that's means is some kind of list, you have to use a loop to access the items and them access the properties:
<div class="content-display">
<div class="content-display-main">
<div class="content-display-main-portfolio">
<div class="image-portfolio">
{% for item_img in image_portfolio %}
<a class="example-image-link" href="{{ item_img.image.url }}" data-lightbox="example-set" data-title="{{item_img.title}}"></a>
{% endfor %}
Try this:
# views.py
def portfolio(request, article=None):
# first get the Post instance with slug = article (I'm assuming article passed as url argument, is a slug)
post = get_object_or_404(Post, slug=article)
# get the Categoriepost object based on a specifi article
categorie_port = get_object_or_404(Categorieport, article=post)
# image_portfolio is a QuerySet (that is a list of Portfolio objects)
image_portfolio = categorie_port.portfolio_set.all()
return render(request, 'portfolio1.html', {'portfolio': categorie_port, 'image_portfolio': image_portfolio})
Leave your HTML as is.
Hi thank you all for your answer.
So, I used a for loop for solving my case as mentioned previously.
Below, my code:
<div class="titre-display">
<h2>{{ portfolio.article.timestamp|date:"d E Y" }} / {{ portfolio.article.categorie}} </h2>
<h1>{{ portfolio.article.title}}</h1>
</div>
<div class="content-display">
<div class="content-display-main">
<div class="content-display-main-portfolio">
{% for photo in image_portfolio %}
<div class="image-portfolio">
<a class="example-image-link" href="{{ photo.image.url }}" data-lightbox="example-set" data-title="{{photo.title}}">
{% thumbnail photo.image "300x300" crop="center" as im %}
<img class="example-image" src="{{ im.url }}" alt=""/>
{% endthumbnail %}
</a>
<p>{{photo.title}}</p>
</div>
{% empty %}
<p>Aucun portfolio.</p>
{% endfor %}
</div>
</div>
And my views:
def portfolio(request, slug=None, article=None):
slug = get_object_or_404(Post, slug=slug)
portfolio = get_object_or_404(Categorieport, article=article)
image_portfolio = portfolio.portfolio_set.all()
return render(request, 'portfolio.html', {'portfolio': portfolio, 'image_portfolio': image_portfolio})
Thanks again for your help
Singertwist