Problem:
I cannot get images to appear on my template plant_detail.html. I think I'm calling on variables incorrectly, but not sure what to change.
Context:
I created a model PlantImage, that allows me to associate multiple images within my model Plant.
I then created a class-based view PlantDetailView to link the two models, PlantImage and Plant together.
However, now when I try to display those images in the template plant_detail.html, nothing will appear. How can I get images to appear on my template?
I tried reading through the Django documentation, reading articles, and watching youtube videos, but I'm not sure what I'm missing or need to edit.
My files:
plants/models.py
class Plant(models.Model):
name = models.CharField(max_length=120)
slug = models.SlugField(null=False, unique=True)
description = models.TextField()
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("plant_detail", kwargs={"slug": self.slug})
class PlantImage(models.Model):
plant = models.ForeignKey(Plant, default=None, on_delete=models.CASCADE)
images = models.ImageField(upload_to = 'images/')
def __str__(self):
return self.plant.name
plants/views.py
def plant_index(request):
plant_objects = Plant.objects.all()
context = {'plant_objects': plant_objects}
return render(request, 'plants/plant_index.html', context)
class PlantDetailView(DetailView):
model = Plant
template_name = 'plants/plant_detail.html'
slug = 'slug'
def get_context_data(self, **kwargs):
context = super(PlantDetailView, self).get_context_data(**kwargs)
context['plant_images'] = PlantImage.objects.all()
return context
plant_detail = PlantDetailView.as_view()
plants/urls.py
from django.urls import path
from .views import plant_index, plant_detail
app_name = 'plants'
urlpatterns = [
# ex: /plants/
path('', plant_index, name='plant_index'),
# ex: /plants/pothos/
path("<slug:slug>", plant_detail, name="plant_detail"),
]
plants/plant_detail.html
{% block content %}
<body>
<h1> {{ plant.name }} </h1>
<br>
<p> <b>Latin Name: </b>{{ plant.latin_name }} </p>
<br>
<p><b>Description: </b></p>Dj
<p> {{ plant.description }} </p>
<br>
<br>
<p>Images:</p>
<br>
{% for image in plant_images %}
<p><img src="{{ plant.images }}" width="300px"></p>
{% endfor %}
</body>
{% endblock content %}
settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = str(BASE_DIR.joinpath('mediafiles'))
Screenshots of output:
The code "PlantImage.objects.all()" gets not only specific image related plant but another images.
so if you want to access PlantImage from Plant, you have to write related_name.
https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.ForeignKey.related_name
class PlantImage(models.Model):
plant = models.ForeignKey(Plant, default=None, on_delete=models.CASCADE, related_name="plant_images")
...
ImageField inherit FileField. so, you have to know something of Filefield functions.
https://docs.djangoproject.com/en/4.0/ref/models/fields/#filefield-and-fieldfile
if you want to show images, add '.url' like this.
{% for image in plant.plant_images.all %}
<p><img src="{{ image.images.url }}" width="300px"></p>
{% endfor %}
Related
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.
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)
I am a beginner learning Django through a building an app, called PhoneReview. It will store reviews related to the latest mobile phone. It will also display phone brands, along with the associated phone models and their reviews.
Right now, I am facing an error just after I have added codes to use slug in the URLs. When I go to http://127.0.0.1:8000/index, I see this page:
When I click on "Samsung," I get this error:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/index/samsung/
Raised by: PhoneReview.views.ModelView
No phone model found matching the query
You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
I have successfully performed migration. But still, I am facing the issue.
Here are my codes of models.py located inside PhoneReview folder:
from django.db import models
from django.template.defaultfilters import slugify
# Create your models here.
class Brand(models.Model):
brand_name = models.CharField(max_length=100)
origin = models.CharField(max_length=100)
manufacturing_since = models.CharField(max_length=100, null=True, blank=True)
slug = models.SlugField(unique=True, max_length=150)
def __str__(self):
return self.brand_name
def save(self, *args, **kwargs):
self.slug = slugify(self.brand_name)
super().save(*args, **kwargs)
class PhoneModel(models.Model):
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
model_name = models.CharField(max_length=100)
launch_date = models.CharField(max_length=100)
platform = models.CharField(max_length=100)
slug = models.SlugField(unique=True, max_length=150)
def __str__(self):
return self.model_name
def save(self, *args, **kwargs):
self.slug = slugify(self.model_name)
super().save(*args, **kwargs)
class Review(models.Model):
phone_model = models.ManyToManyField(PhoneModel, related_name='reviews')
review_article = models.TextField()
date_published = models.DateField(auto_now=True)
slug = models.SlugField(max_length=150, null=True, blank=True)
link = models.TextField(max_length=150, null=True, blank=True)
def __str__(self):
return self.review_article
Here are my codes of urls.py located inside PhoneReview folder:
from . import views
from django.urls import path
app_name = 'PhoneReview'
urlpatterns = [
path('index', views.BrandListView.as_view(), name='brandlist'),
path('index/<slug:slug>/', views.ModelView.as_view(), name='modellist'),
path('details/<slug:slug>/', views.ReviewView.as_view(), name='details'),
]
Here are my codes of views.py located inside PhoneReview folder:
from django.shortcuts import render
from django.views import generic
from .models import Brand, PhoneModel, Review
class BrandListView(generic.ListView):
template_name = 'PhoneReview/index.html'
context_object_name = 'all_brands'
def get_queryset(self):
return Brand.objects.all()
class ModelView(generic.DetailView):
model = PhoneModel
template_name = 'PhoneReview/phonemodel.html'
context_object_name = 'phonemodel'
class ReviewView(generic.DetailView):
model = Review
template_name = 'PhoneReview/details.html'
Here are my codes of apps.py located inside PhoneReview folder:
from django.apps import AppConfig
class PhonereviewConfig(AppConfig):
name = 'PhoneReview'
Here are my codes of index.html located inside templates folder:
{% extends 'PhoneReview/base.html' %}
{% load static %}
{% block title%}
Brand List
{% endblock %}
{% block content %}
<!--Page content-->
<h1>This is Brand List Page</h1>
<h2>Here is the list of the brands</h2>
<ul>
{% for brand in all_brands %}
<!-- <li>{{ brand.brand_name }}</li>-->
<li>{{ brand.brand_name }}</li>
{% endfor %}
</ul>
<img src="{% static "images/brandlist.jpg" %}" alt="Super Mario Odyssey" /> <!-- New line -->
{% endblock %}
Here are my codes of phonemodel.html located inside templates folder:
{% extends 'PhoneReview/base.html' %}
{% load static %}
{% block title%}
Phone Model Page
{% endblock %}
{% block content %}
<!--Page content-->
<h1>This is Phone Model Page</h1>
<h2>Here is the phone model</h2>
<ul>
<li>{{ phonemodel.model_name }}</li>
</ul>
<img src="{% static "images/brandlist.jpg" %}" alt="Super Mario Odyssey" /> <!-- New line -->
{% endblock %}
Here are my codes of details.html located inside templates folder:
{% extends 'PhoneReview/base.html' %}
{% load static %}
<html>
<link rel="stylesheet" type="text/css" href="{% static "css/style.css" %}">
<html lang="en">
{% block title%}Details{% endblock %}
{% block content %}
<h1>This is the Details Page</h1>
<h2>Review:</h2>
<p>{{ review.review_article }}</p>
<h2>News Link:</h2>
<p>{{ review.link }}</p>
{% endblock %}
</html>
I feel that I have made a mistake on either index.html or phonemodel.html. But being a beginner, I can't catch it.
How can I fix the issue?
Update: I added the following codes in phonemodel.html to loop over phone models, as suggested by #c.grey :
<ul>
{% for model_name in all_model_name %}
<li>{{ phonemodel.model_name }}</li>
{% endfor %}
</ul>
Also, I added this line in index.html:
<li>{{ brand.brand_name }}</li>
Moreover, I added these codes in views.py:
class ModelView(generic.ListView):
template_name = 'PhoneReview/phonemodel.html'
context_object_name = 'all_model_name'
def get_queryset(self):
return PhoneModel.objects.all()
But now, I am getting this error:
django.urls.exceptions.NoReverseMatch: Reverse for 'modellist' with arguments '('',)' not found. 1 pattern(s) tried: ['index/(?P<slug>[-a-zA-Z0-9_]+)/$']
Here is the link to my project files:
https://github.com/shawnmichaels583583/phoneradar
Basically you are passing Brand model slug to get PhoneModel model data.
To get details from PhoneModel you need to pass PhoneModel slug to get data
Your error is that your link points to brand.slug (here samsung) but it seems to me that you don't have any phone model whose name is just samsung, do you?
You mix up brands and phone models
When a user registers for my app.I receive this error when he reaches the profile page
'ValueError at /profile/:The 'image' attribute has no file associated with
it.'
This is my profile model:
class Profile(models.Model):
Full_Name = models.CharField(max_length=32,blank=True)
Name = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
E_mail = models.EmailField(max_length=70,blank=True)
Qualification = models.CharField(max_length=32,blank=True)
Permanant_Address = models.TextField(blank=True)
image = models.ImageField(upload_to='user_images', null=True, blank=True)
def __str__(self):
return str(self.Name)
def get_absolute_url(self):
return reverse("userprofile:profiledetail")
#property
def image_url(self):
if self.image and hasattr(self.image, 'url'):
return self.image_url
This is my form:
class profileform(forms.ModelForm)"
class Meta:
model = Profile
fields = ('Full_Name','Name', 'E_mail','Qualification','Permanant_Address','image')
This is my view:
from django.shortcuts import render
from django.views.generic import DetailView,UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from userprofile.models import Profile
from userprofile.forms import profileform
# Create your views here.
class profiledetailview(LoginRequiredMixin,DetailView):
context_object_name = 'profile_details'
model = Profile
template_name = 'userprofile/profile.html'
def get_object(self):
return self.request.user.profile
class profileupdateview(LoginRequiredMixin,UpdateView):
model = Profile
form_class = profileform
template_name = 'userprofile/profile_form.html'
def get_object(self):
return self.request.user.profile
And in my template I have done something like this:
<img class="profile-user-img img-responsive img-circle" src="{{
profile_details.image.url|default_if_none:'#' }}/" alt="User profile
picture">
I'm been reading the documentation for Built-in template tags and filters I think a solution here is to use and I think I can't seem to use template tag properly.
How can I configure this template to make picture an option. If their are no picture leave it but display the persons name.
Thank you
You are trying to get the url for an image that doesn't exists.
Basically if you're trying to check if an image exists, then you have to do it this way:
if profile_details.image:
url = profile_details.image.url
or in your case:
src={% if profile_details.image %}{{ profile_details.image.url }}{% else %}#{% endif %}
My dear friend, Navid's solving is good but not enough because If user hasn't profile picture you should show default image easily (not need migration). So you can follow below steps:
Add this method to your person model:
#property
def get_photo_url(self):
if self.photo and hasattr(self.photo, 'url'):
return self.photo.url
else:
return "/static/images/user.jpg"
You can use any path (/media, /static etc.) but don't forget putting default user photo as user.jpg to your path.
And change your code in template like below:
<img src="{{ profile.get_photo_url }}" class="img-responsive thumbnail " alt="img">
If your error comes from the HTML and not the admin site, then do like this.
{% if profile_details.image %} # make sure that it's not .url in the end
<img src="{{ profile_details.image.url }}" alt="{{ profile_details.title }}">
{% else %}
<p>No Image</p>
{% endif %}
Change the profile_details, if your context is named differently,
Example
context = {
'products': products,
}
also change image, if your ImageField is named differently
Example:
thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True, )
<!-- For me it looked like this -->
{% for product in products %}
{% if product.thumbnail %}
<img src="{{ product.thumbnail.url }}" alt="{{ product.title }}">
{% else %}
<p>No Image</p>
{% endif %}
{% endfor %}
very easy! this was what i did: i deleted all my old post that doesn't have an image attached to it, made a new one and it worked fine.
EDIT: I fixed the views.py with Av4t4r's code but it still shows no content. When I type something in voodoo.html it actually shows content, but all of the content inside the {% block content %} are not showing. Why is that?
Hello I am trying to make a simply gallery app where the first view (listview) is a list of all the persons (which are the objects), and when a user clicks on one it proceeds to the next page with a given pk/id key. But when it comes to that page... the content is blank. Here is what I have:
urls.py:
urlpatterns = [
url(r'^$', ListView.as_view(queryset=Images.objects.all(), template_name='imgboard/home.html')),
url(r'^imgboard/(?P<id>\d+)/$', views.voodoofunction, name='voodoofunction'),
]
views.py (I feel like this is where the problem is):
def voodoofunction(request, id=None):
instance = get_object_or_404(Moreimages, id=id)
context = { "object_list": instance, }
return render(request, "imgboard/voodoo.html", context)
models.py
class Images(models.Model):
name_person = models.CharField(max_length=70)
instagram = models.CharField(max_length=200)
img_url = models.CharField(max_length=500)
def __unicode__(self):
return self.name_person
class Meta:
verbose_name_plural = 'Images'
class Moreimages(models.Model):
key = models.ForeignKey(Images, on_delete=models.CASCADE)
img_url = models.CharField(max_length=500)
def __unicode__(self):
return str(self.key)
class Meta:
verbose_name_plural = "More Images"
listview_code.html
{% block content %}
{% for object in object_list %}
<p>{{object.name_person}}</p>
{% endfor %}
{% endblock %}
voodoo.html:
{% block content %}
<h2>{{ object.name_person}}<br></h2>
<h4>{{object.instagram}}</p></h4>
<br>
{% for object in object_list %}
<p><img src="{{object.img_url}}", width=350, height="360></img>"</p>
{% endfor %}
{% endblock %}
Your context has no "object" in it. And your "object_list" is an instance, not a list (so your for loop is doing nothing).