I now have a model and I want to display two fields of it in the same view.Then display them in the details template.But the result returns an empty in template.What is the best way to do that?
my model:
class Book(models.Model):
class Meta :
verbose_name_plural = 'Books'
category = models.ForeignKey(Categorie,on_delete=models.CASCADE,null=True,
related_name="apps")
book_slug = models.SlugField(blank=True,allow_unicode=True,editable=True)
book_title = models.CharField(max_length=50 , null=True)
book_picture = models.ImageField(upload_to='imgs' , null=True)
book_description = RichTextUploadingField(null=True,blank=True)
book_size = models.CharField(max_length=20 , null=True)
book_created_at = models.DateField(auto_now_add=True)
book_updated_at = models.DateField(auto_now=True)
book_auther = models.CharField(max_length=100 , null=True)
my views:
def book_details(requset ,book_slug):
book = get_object_or_404 (Book, book_slug = book_slug)
try :
book = Book.objects.filter(book_slug = book_slug)
except :
raise Http404
similar_books = Book.objects.filter(book_auther = book_slug) ------ >>> Here is my query
context ={
'book' :book,
'similar_books':similar_books,
}
return render( requset,'book.html',context)
my template:
<div class="row">
{% for b in book %}
<div>
<img src="{{ b.book_picture.url }}" class="Rounded circle Image" height="150"
width="150" alt="">
{{b.book_title}}
</div>
{% endfor %}
</div>
<div class="row">
{% for sb in similar_books%}
<div>
<img src="{{ sb.book_picture.url }}" class="Rounded circle Image" height="150"
width="150" alt="">
{{sb.book_title}}
</div>
{% endfor %}
</div>
First of all
book = get_object_or_404 (Book, book_slug = book_slug)
this is a get method, which returns a single instance of book if found (not a Queryset)
Then you are repeating the same thing, and in my opinion this piece of code can be discarded since it's a duplicate of the row above
try :
book = Book.objects.filter(book_slug = book_slug)
except :
raise Http404
Then in the template you don't have to cicle on book: that's a single object not an enumerable - queryset, so you should write
<div class="row">
<div>
<img src="{{ book.book_picture.url }}" class="Rounded circle Image" height="150"
width="150" alt="">
{{book.book_title}}
</div>
</div>
About your second query, based on your example data you cannot extract data with a filter, but have to process in this way:
similar_books = []
for sb in Book.objects.all():
if sb.book_author in book_slug:
similar_books.append(sb)
Insisted of using filter(book_slug = book_slug) use __icontains with Q object (use the each word in book_slug inorder to get maximum similar entries).
for example : -
slugs = book_slug.split() # which will give list of slugs
condition = Q(book_slug__icontains=slugs[0])
for slug in slugs[1:]:
condition &= Q(book_slug__icontains=slug)
Book.objects.filter(condition).all()
if you wish to eliminate explicit loop like above you can use operator to do that.
import operator
slug_conditions = reduce(operator.and_, [Q(book_slug__icontains=slug) for slug in book_slug])
queryset = Profile.objects.filter(slug_conditions).all()
Try obtain list ahuthor book by first query and in second query filter all books with same authors
book = Book.objects.filter(book_slug=book_slug)
authors = book.values_list('book_auther').distinct()
similar_books = Book.objects.filter(book_auther__in=authors)
If you want exclude book from simiar books
book = Book.objects.filter(book_slug=book_slug)
authors = book.values_list('book_auther').distinct()
book_ids = book.values_list('id', flat=True)
similar_books = Book.objects.filter(book_auther__in=authors).exclude('id__in'=book_ids)
Related
I'm having trouble passing data from a Django model to a a Django template. The code below works, but I'm it seems clunky and I'm a bit confused by why it works. I'm trying to read a random entry from my database for each one of my models. I then want to print the data from each model on my home template. I thought I could add each model to a single dictionary, then iterate through the dictionary of dictionaries in my template, but that didn't work ( I know the models have different fields, but I was planning on working around that later). I played around with it for a while, and realized that my for loops weren't actually iterating through the dictionary of dictionaries, but was actually just reading each individual entry in the dictionary through its key. Additionally, it doesn't seem like the for loops are doing any iterating, since I can access the fields of my model directly with the .field notation. This seems to be the only method that works but I'm still not entirely sure why. Could anyone clarify why this works, or let me know if there is a more straightforward way of reading the dictionary? Here is the function that renders the template:
def home(request):
# Get a random song and album from the database
randomSong = randint(1,501)
randomAlbum = randint(1,501)
songChoice = Songs.objects.filter(rank = randomSong).values()
albumChoice = Albums.objects.filter(rank = randomAlbum).values()
entry = {'ent': songChoice, 'entry': albumChoice}
return render(request,'rollingStone/home.html', entry)
And this is the template that home renders:
{% extends "rollingStone/layout.html" %}
{% block title %}
A Record A Day
{% endblock %}
{% block body %}
{% for song in ent %}
<div class = "container">
<div class = "row">
<h1>#{{song.rank}}: {{song.title}} </h1>
</div>
<div class = "row ">
<div class = "col-sm">
<img src = {{song.cover}} alt = "No cover">
</div>
<div class = "col-sm">
<p>
<strong>{{song.releaseInfo}}</strong>
<br>
<br>
<strong>Artist:</strong> {{song.artist}}
<br>
<strong>Writer(s):</strong> {{song.writers}}
<br>
<strong>Producer(s):</strong> {{song.producer}}
<br>
<br>
{{song.description}}
</p>
</div>
</div>
</div>
{% endfor %}
{% for album in entry %}
<div class = "container">
<div class = "row">
<h1>#{{album.rank}}: {{album.title}} </h1>
</div>
<div class = "row ">
<div class = "col-sm">
<img src = {{album.cover}} alt = "No cover">
</div>
<div class = "col-sm">
<p>
<strong>Artist:</strong> {{album.artist}}
<br>
<strong>Label:</strong> {{album.label}}
<br>
<strong>Release Year:</strong> {{album.year}}
<br>
<br>
{{album.description}}
</p>
</div>
</div>
</div>
{% endfor %}
{% endblock %}
And the definitions of the models themselves, if that helps:
# Model to store a Rolling Stone top 500 song
class Songs(models.Model):
rank = models.IntegerField()
artist = models.CharField(max_length=50)
title = models.CharField(max_length=50)
cover = models.ImageField()
writers = models.CharField(max_length=100)
producers = models.CharField(max_length=100)
releaseInfo = models.CharField(max_length=100)
description = models.TextField()
used = models.BooleanField(default=False)
def __str__(self):
return self.title
# Model to store a Rolling Stone top 500 album
class Albums(models.Model):
rank = models.IntegerField()
artist = models.CharField(max_length=50)
title = models.CharField(max_length=50)
cover = models.ImageField()
label = models.CharField(max_length=100)
year = models.CharField(max_length=100)
description = models.TextField()
used = models.BooleanField(default=False)
def __str__(self):
return self.title
It works because you are filtering and retrieving .values(), which sort of means it's returning a queryset of probably 1.
In other words {% for song in ent %} is equivalent to for song in ent_queryset.
If you changed the filter to retrieve multiple songs/albums, you would see more than one song/album show up.
If you just want to retrieve one song/album, then you would need to do something like:
songChoice = Songs.objects.filter(rank = randomSong).values().first()
albumChoice = Albums.objects.filter(rank = randomAlbum).values().first()
entry = {'ent': songChoice, 'entry': albumChoice}
return render(request,'rollingStone/home.html', entry)
And then in your template you can access them directly:
{{ ent }} # this is your song obj
{{ ent.writers }}
{{ ent.artist }}
{{ entry }} # this is your album entry
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>
I have a list of reviews and each review has a rating average. My problem is trying to added each review id to the filter for the query result. For this, I assume a for loop in the filter is best.
I've found a previous post with a similar situation, but the same result doesn't seem to be working for me.
When I load my reviews page, I receive a TypeError: 'function' object is not iterable.
Here is my view.py file with the queries.
def reviews(request):
context = {
'reviews': Employee.objects.all(),
'rating': Employee.objects.filter(id__in=[review.id for review in reviews]).aggregate(rate=Avg(F('value1')+F('value2')+F('value3').....+F('valueN'))/N)
}
return render(request, 'reviews/reviews.html', context)
Reviews.html template.
{% extends "reviews/layout.html" %}
{% block content %}
{% for review in reviews %}
{% for rating in ratings %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ review.author.profile.image.url }}">
<div class="media-body">
<div class="article-metadata">
<h4 class="mr-2">{{ review.company }} {{rating}}</h4>
<small class="text-muted">{{ review.date_posted|date:"F d, Y" }}</small>
</div>
<h5><a class="article-title" href="{% url 'review-detail' review.id %}">{{ review.title }}</a></h5>
<p class="article-content">{{ review.content }}</p>
</div>
</article>
{% endfor %}
{% endfor %}
{% endblock content %}
Any suggestions are much appreciated.
EDIT: Here is my model for the Employee table.
class Employee(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
company = models.CharField(max_length=100)
recommend = models.BooleanField(default=False)
salary = models.CharField(max_length=100)
salary_satis = models.CharField(max_length=100)
culture = models.CharField(max_length=100)
location = models.CharField(max_length=100)
work_env = models.CharField(max_length=100)
communication = models.CharField(max_length=100)
opportunity = models.CharField(max_length=100) # Opportunity happiness
leadership_satis = models.CharField(max_length=100)
fair_treatment = models.CharField(max_length=100)
advice = models.TextField() # Advice for management
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['-date_posted']
def __str__(self):
return f'{self.title, self.content, self.company, self.recommend, self.salary, self.salary_satis, self.culture, self.location, self.work_env, self.communication, self.opportunity, self.leadership_satis, self.fair_treatment, self.advice, self.date_posted, self.author}'
def get_absolute_url(self):
# Returns user to reviews page after their review has been submitted.
return reverse('reviews')
Your reviews in the dictionary, refers to the reviews view function (here both in boldface):
def reviews(request):
context = {
'reviews': Employee.objects.all(),
'rating': Employee.objects.filter(
id__in=[review.id for review in reviews]
).aggregate(
rate=Avg(F('value1')+F('value2')+F('value3').....+F('valueN'))/N
)
}
return render(request, 'reviews/reviews.html', context)
Indeed, you can not iterate over this function, since it has no __iter__ method attached to it, but even if it had, this is not what you want to do.
I think the most elegant way to solve this, is simply defining a reviews variable:
def reviews(request):
reviews = Employee.objects.all()
context = {
'reviews': reviews,
'rating': Employee.objects.filter(
id__in=[review.id for review in reviews]
).aggregate(
rate=Avg(F('value1')+F('value2')+F('value3').....+F('valueN'))/N
)
}
return render(request, 'reviews/reviews.html', context)
So now reviews is a local variable that you can acces, and since a queryseet is iterable, we can iterate over this.
That being said, it is a bit odd to use an id__in here, since reviews has all the Employees.
I am trying to display fields from child model 'usertraining' entry linked by foreign key to a 'training' entry. but second queryset usertraining_set does not seem to contain anything. What am I missing?
I have the following template loop:
{% for training in training_list %}
{% for elem in training.usertraining_set.all %}
<div class="training">
<div class="training-info">
<div class="training-main-title">
<div class="training-state">{{ elem.state }}</div>
{{ training.title }}
</div>
<div class="training-date">
<p>Last trained: {{ elem.last_training_date|date:"D, d M Y, H:i:s" }}</p>
</div>
<div class="training-score">
Score: <span class="badge btn-circle">{{elem.score}}</span>
</div>
</div>
<img src="{{ training.title_pic.url }}">
</div>
{% endfor %}
{% endfor %}
model
class Training(models.Model):
title = models.CharField(max_length = 2000, blank = False)
title_pic = models.ImageField(upload_to = 'training/title_pics', blank = False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("training_detail",kwargs={'pk':self.pk})
#property
def title_pic_url(self):
if self.title_pic and hasattr(self.title_pic, 'url'):
return self.title_pic.url
class UserTraining(models.Model):
user = models.ForeignKey(User,related_name='usertraining',on_delete=models.CASCADE)
training = models.ForeignKey(Training,related_name='usedtraining',on_delete=models.CASCADE)
state = (
('train', 'train'),
('training', 'training...'),
('retrain', 'retrain'),
('trained','trained')
)
state = models.CharField(max_length = 200, choices = state, default = 'train')
last_training_date = models.DateTimeField(auto_now=True)
score = models.CharField(max_length = 4, default = '0%')
def __str__(self):
""" String representation of Training entry """
return "{}_{}".format(self.user,self.training)
You're using "related_name" in your UserTraining model fields:
user = models.ForeignKey(User,related_name='usertraining',on_delete=models.CASCADE)
training = models.ForeignKey(Training,related_name='usedtraining',on_delete=models.CASCADE)
_set postfix is used only when field has no related name, so in your templates it would be:
{% for elem in training.usertraining.all %}
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