Suppose you are making a site to simply list your products.
You want to upload an unspecified number of pictures for your each of your products. So you, following Django's many-to-one documentation, make two models:
# files stored under my_app/static/my_app/product_images/product_<id>/<img_name>
def product_img_dir_path(instance, filename):
return 'my_app/static/my_app/product_images/product_{0}/{1}'.format(instance.product.id, filename)
class Product(models.Model):
name = models.CharField ...
... # other attributes of the product, e.g. price, etc
class ProductImage(models.Model):
product = models.ForeignKey("Product", on_delete=models.CASCADE)
image = models.ImageField(upload_to=product_img_dir_path)
Now if I want all of the images for say product 1, I can retrieve them using:
ProductImages.objects.filter(product__pk=1)
My question starts here.
Suppose you want an index page which just shows the listings for all of your products and for simplicity, the first image associated with each product.
You make a template page with
{% for product in product list %}
<div class="product-listing" style="display:inline">
<!-- image for product goes here -->
<!-- brief description goes here -->
</div>
{% endfor %}
where product_list was passed in your context:
# inside /my_app/views.py
def index(request):
...
context = {"product_list": Product.objects.all()}
...
Question: what is the best way to also have access to the images for displaying the images in the template page?
Currently I thought constructing a parallel image list would suffice:
# inside /my_app/views.py
def index(request):
...
product_list = Product.objects.all()
image_list = [product.productimage_set.all()[0] for product in product_list]
context = {"product_list": product_list, "image_list": image_list}
...
and then somehow using the forloop counter to get the corresponding image for the product.
e.g.
{% for product in product list %}
<div class="product-listing" style="display:inline">
<img src="{{ image_list[<forloop counter>].image.url }}" />
<!-- brief description goes here -->
</div>
{% endfor %}
Is there a better way to do this?
As long as you can access product.productimage_set, you can try to iterate it in your template and do not pass it as a view context.
In your Django template:
{% for product in product_list %}
<div class="product-listing" style="display:inline">
{% for product_image in product.productimage_set.all %}
<img src="{{ product_image.image.url }}" />
<!-- brief description goes here -->
{% endfor %}
</div>
{% endfor %}
I think that it will be easier for you to solve this if you simplify your design by moving the images to your Product model.
If you want to save the path to an image it will be easier to use CharField, but if you want to save many paths why not using JSONField?
My suggestion look like this:
class Product(models.Model):
name = models.CharField(null=True, blank=True)
main_image = models.CharField(null=True, blank=True) # Optional
images = JSONField(null=True, blank=True)
Related
I am pretty new to django and now I'm currently learning how to access images in database and display it using html. I want to put the images as elements in a list for further styling and making the page responsive using css but I don't want to write each and every image as a list item as there are more than 100 images in database. I've tried the below code but its not displaying images as a list. Please do suggest if any better way of doing it. I have image as a model in my models.py.
<div class='gallery'>
{% for x in photo %}
<ul>
<li><img class="images" src="{{x.image.url}}"/></li>
</ul>
{% endfor %}
</div>
This is the model for reference:
from django.db import models
class Photo(models.Model):
photo_uploader = models.CharField(max_length=50)
desc = models.CharField(max_length=300)
image = models.ImageField(upload_to="home/images")
This is the view:
def profile(request):
username = None
if request.user.is_authenticated:
photos_to_show = []
username = request.user.username
photos = Photo.objects.filter(photo_uploader=username)
return render(request, 'profile.html', {'name': username, 'photo': photos})
The <ul> tags should be placed outside the loop, otherwise you each time start a new <ul> and end that </ul> per image:
<div class='gallery'>
<ul>
{% for x in photo %}
<li><img class="images" src="{{ x.image.url }}"/></li>
{% endfor %}
</ul>
</div>
I am working on my first eshop website using django framework and I got stuck on a problem.
I have created a general model for different kinds of products (like laptops, books etc.). Each product that is added to the website can be found by on its foreign key that links that product to a specific category.
The question is how can I display on laptops.html only the products that have the foreign key which points to the right category? Like, to display only the products from laptops category.
Thank you very much for your time!
EDIT:
in urls:
urlpatterns=[
path('', views.HomePage.as_view(), name='home'),
path('ComputerScience/', views.ComputerScience.as_view(), name='computer_science'),
path('category/<int:category_pk>/list-products/', views.CompSProd.as_view(), name='category_products_list')]
In computerscience.html I render all the cateogries.
Here in views.py I have the two controllers, first for categories and second for laptops for instance.
views.py
class ComputerScience(ListView):
model = ComputerScienceCategory
template_name = "computer_science.html"
context_object_name = "category"
class CompSProd(ListView):
model = ComputerScienceProducts
template_name = "laptops.html"
context_object_name = "products"
def get_queryset(self):
queryset = super().get_queryset()
# If you wish to still keep the view only for specific category use below line
category = get_object_or_404(ComputerScienceCategory, pk=self.kwargs.get('category_pk'))
queryset = queryset.filter(category=category)
return queryset
Here I have the template where I want to display all categories.
computer_science.html
<div class="computerScienceContent" id="slide">
{% for cat in category %}
<a href="{% url 'category_products_list' category.pk %} " id="aBar">
<div>
<h4 class="cSh">{{ cat.name }}</h4>
<img src="{{ cat.img.url }}" alt="image" class="img">
</div>
</a>
{% endfor %}
Here is laptops html, where I'd like to have the whole products displayed.
laptops.html
{% extends 'index.html' %}
{% block title %}
<title>Laptops</title>
{% endblock %}
{% block cont2 %}
{% endblock %}
My main goal is that to have a page (computerscience.html) where I have displayed a list with all available categories and when you click on one category, to redirect you to another page where you have listed all the products that belongs to that category.
This is the error that has been thrown to me:
Reverse for 'category_products_list' with arguments '('',)' not found. 1 pattern(s) tried: ['category/(?P<category_pk>[0-9]+)/list\\-products/$']
You should override get_queryset to filter your objects. Also as you are writing a view for a specific instance of category you would end up writing a lot of views, also when a new category would be added this would be very tedious you should use one view for all categories instead. Try this:
from django.shortcuts import get_object_or_404
class CompSProd(ListView):
model = ComputerScienceProducts
template_name = "laptops.html"
context_object_name = "products"
def get_queryset(self):
queryset = super().get_queryset()
category = get_object_or_404(ComputerScienceCategory, pk=self.kwargs.get('category_pk')) # Assuming category_pk will be passed in url
# If you wish to still keep the view only for specific category use below line
# category = get_object_or_404(ComputerScienceCategory, pk=<pk-of-category-here>)
queryset = queryset.filter(category=category)
return queryset
To pass the categories primary key in the url you need to do something as follows:
In your urls.py:
urlpatterns = [
...
path('category/<int:category_pk>/list-products/', views.CompSProd.as_view(), name='category_products_list'),
...
]
Now in the page where you display all categories:
{% for cat in category %}
<a href="{% url 'category_products_list' cat.pk %}" id="aBar">
<div>
<h4 class="cSh">{{ cat.name }}</h4>
<img src="{{ cat.img.url }}" alt="image" class="img">
</div>
</a>
{% endfor %}
Also you write id="aBar" but this line is in a loop meaning you would end up with multiple same ids you should use a class instead.
I am trying to access dict values in my django template. The dict keys are objects. Here is a snippet of my codebase:
models.py
class ProductCategory(models.Mode):
name = models.CharField(max_length=64, blank=False, null=False, unique=True)
slug = models.SlugField(blank=False, null=False, unique=True)
class Product(models.Model):
category = models.ForeignKey(ProductCategory, on_delete = models.CASCADE)
# other fields
pass
def __str__(self):
return self.title
views.py
def index(request):
products = Product.objects.all()
categorized_products = {}
for p in products:
prod_category = p.category
temp = categorized_products.get(prod_category,[])
temp.append(p)
categorized_products[prod_category] = temp
context = {'products_dict': categorized_products, 'categories': categorized_products.keys() }
return render(request,'product/index.html', context=context)
mytemplate.html (relevant snippet)
<div class="tab-content">
{% for category in categories %}
{% if not forloop.counter0 %}
<div class="tab-pane fade in active" id="{{ category.slug }}">
{% else %}
<div class="tab-pane fade in" id="{{ category.slug }}">
{% endif %}
<div >
<h4>{{ category.description }}</h4>
<div class="container-fluid">
<div class="row">
<div class="col-md-3" style="border:1px solid red;">
{% for product in products_dict.category %}
{{ product }}
{% endfor %}
When I step through the code with a debugger, I can see that the variable products_dict is a dict and correctly populated by the view. However, when I run the code, the for loop code is not executed.
Similarly, when I run the same code I have in views.py in the shell, the dict is correctly populated, so I know that there is data in the database and that the retrieved data is being correctly passed to the django template.
So why am I unable to access the dict value in the template and why is the product not displaying in my template?
The simple answer is that Django templates don't do that. Dot reference in template variables does dictionary lookup (among other things) but not based on the value of another variable name:
Note that “bar” in a template expression like {{ foo.bar }} will be interpreted as a literal string and not using the value of the variable “bar”, if one exists in the template context.
https://docs.djangoproject.com/en/3.0/ref/templates/language/#variables
Generally I would solve this by arranging for something iterable to exist on the category values directly, rather than trying to use the category object as a dict key. That could be a reverse FK manager:
{% for product in category.products_set.all %}
Or something I set on the in-memory category objects in the view, if I need to transform or filter the products per-category:
categories = ProductCategory.objects.all()
for category in categories:
category.products = [transform_product_somehow(product) for product in category.products_set.all()]
(Optionally with use of fetch_related to prefetch the category products rather than doing an additional query per category.)
You can use a custom filter for that. In custom_tags.py:
from django import template
register = template.Library()
#register.filter
def get_obj_field(obj, key):
return obj[key]
Then load the tags in django:
{% load custom_tags %}
And use it like this:
{% for product in products_dict|get_obj_field:category %}
{{ product }}
{% endfor %}
I am still fairly new to django and I ran into a problem which I am having a hard time figuring out even with using google or maybe I am not seeing the solution. So I have a model named movieTitles like so:
class movieTitle(models.Model):
title = models.Charfield(max_length=50)
image = models.URLField(max_length=100)
description = models.TextField()
year = models.PositiveSmallIntegerField()
director = models.Charfield(max_length=50)
Then I have my view as so:
from Movie.models import movieTitles
def movieListView(request):
movie = movieTitle.objects.all()
return render(request, 'index.html', {'movies':movie})
and my html as so:
<body>
{% for info in movies %}
{{info.title}}
{% endfor %}
</body>
What I want it to do is once user goes to the movie list and they decide to click on a certain movie, how can I take the user to another html page which will show the info.image, info.description, info.year, and info.director belonging to the movie they decided to click on. I know how to write the template and all that but what im confused on is how can I write the template using {{info.???}} and not having to create a template for every movie to link to but instead use the previous template in which the user chose a movie to create the details in the next template.
You should create a new view, which get a pk of choosen movie and retrives all informations about it.
# Add to urls.py
urlpatterns += [url(r'^detail/(?P<pk>\d+)/$', views.movie_detail, name='movie_detail')]
# Add to views.py
from django.shortcuts import get_object_or_404
def movie_detail(request, pk):
movie = get_object_or_404(movieTitle, pk=pk)
return render(request, 'movie_detail.html', {'movie': movie})
# movie_detail.html
<h1>Title: {{ movie.title }}</h1>
<img src="{{ movie.image }}">
{{ movie.description|linebreaks }}
<p>Year: {{ movie.year }}</p>
<p>Director: {{ movie.director }}</p>
Last thing you should do is to put right url in html with movie list
# Your initial html
<body>
{% for movie in movies %}
{{ movie.title }}
{% endfor %}
</body>
And that's all
add a unique field in ur model like id foreg:-
class movieTitle(models.Model):
id = models.Charfield(max_length=50)
title = models.Charfield(max_length=50)
image = models.URLField(max_length=100)
description = models.TextField()
year = models.PositiveSmallIntegerField()
director = models.Charfield(max_length=50)
and amke a url like this:
supppose app name is:
app_name = 'movie'
url(r'^movie-list/show/(?P<university_id>[0-9]+)/$', view.view_list, name='view-list'),
and pass ur url in template like this:
<body>
{% for info in movies %}
{{info.title}}
{% endfor %}
</body>
I have the following models:
class Car(models.Model):
# some fields
class CarPhotos(models.Model):
image = models.ImageField(upload_to='somewhere')
car = models.ForeignKey(Car)
in views.py:
def car_list(request):
cars = Car.objects.all()
return render(request, 'borsa/car_list.html', {'cars': cars})
in car_list.html:
{% for car in cars %}
<a href="car/{{ car.pk }}/view/">
<img class="media-object tmbpic" src="{{ car.carphotos_set.all.0.image.url }}" alt="kola">
</a>
The first method I've used was to use inline formsets but I had no clue how to access the images. Can you please point me in the right direction of how to access images according to Car objects ? I mean every Car must have a couple of images and I need to display only the first one in list view, but then I need to display all of them in Detail view.
To display all of them, you need to do something like:
{% for car in cars %}
{% for photo in car.carphotos_set.all %}
<img src="{{ photo.image.url }}">
{% endfor %}
{% endfor %}
So loop through the cars, then nest-loop through the images per car.