I am developing an ecommerce website with Django. In my home page displayed product cards as you see in the below image.
This product image in each card I take from my Product model (image field). When I hover over this image on the home page, the image is changing to another image. That is for I need another image, and I want to take the next image (display when I hover over) from my Product_images model. But I don't know how to do that.
urls.py
from django.urls import path
from . import views
from django.conf.urls.static import static
urlpatterns =
path('', views.home_page, name='amd-home'),
path('product/<int:id>/', views.product_detail, name='product-detail'),
path('about/', views.about, name='amd-about'),
]
views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView
from django.http import HttpResponse
from .models import Product, Product_image, Product_details
def home_page(request):
products = Product.objects.all()
images = Product_image.objects.all()
context = {'products':products, 'images':images}
return render(request, 'product/home.html', context)
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=200)
parent_id = models.IntegerField(default=0)
description = models.TextField()
image = models.ImageField(upload_to='uploads/')
def __str__(self):
return f'{self.name}'
class Brand(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=400)
image = models.ImageField(upload_to='uploads/')
def __str__(self):
return f'{self.name}'
class Product(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
sku = models.CharField(max_length=200)
price = models.IntegerField(default=0)
price_old = models.IntegerField(default=0)
description = models.TextField()
status = models.BooleanField(default=False)
date_posted = models.DateTimeField(auto_now_add=True)
internal_storage = models.CharField(max_length=50, blank=True, null=True, default=None)
ram = models.CharField(max_length=50, blank=True, null=True, default=None)
user = models.ForeignKey(User, on_delete=models.CASCADE)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return f'{self.title}, {self.description}'
class Product_image(models.Model):
image = models.ImageField(upload_to='uploads/')
product = models.ForeignKey(Product, on_delete=models.CASCADE)
def __str__(self):
return f'{self.product.title} image'
home.html
my template file is a very large file, so I only insert the element where I get an image from the Product model (this code word fine), but I don't know how to write code to take images from my Product_image model.
{% for product in products %}
<img alt="" src="{{ product.image.url }}">
{% endfor %}
First, in your model you can give a related_name field like :
class Product_image(models.Model):
image = models.ImageField(upload_to='uploads/')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="product_images)
def __str__(self):
return f'{self.product.title} image'
Then you can access the product's images in template like:
{% for product in products %}
{% for image in product.product_images.all %}
<img alt="" src="{{ image.image.url }}">
{% endfor %}
{% endfor %}
PS: You dont have to return all Product_image quesryset from the view
Expanding on the answer, if you want to order the images there are different approaches you can take:
Method1:
class Product_image(models.Model):
image = models.ImageField(upload_to='uploads/')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="product_images)
time_created = models.DateTimeField(null=True, blank=True)
def __str__(self):
return f'{self.product.title} image'
class Meta:
ordering = ['time_created']
This will order the query set from first created to last . If you don't want to add a time created field you can also choose to order by id.
Method2:
Add a property to your Product model:
class Product:
....
#property
def sorted_image_set(self):
return self.product_images.order_by('time_created')
Then you can access this property from the template
{% for image in product.sorted_image_set %}
Method 3 :
Creating a custom template tag to support order_by in template
#register.filter
def order_by(queryset, args):
args = [x.strip() for x in args.split(',')]
return queryset.order_by(*args)
Then you can do :
{% for image in product.product_images|order_by:"time_created" %}
After the list has been ordered you can access it just by the array indexes like for example images[0] or images[1]
change src in your HTML
{% for product in products %}
<a href="{% url 'product-detail' product.id %}"><img alt="" src="/media/uploads/{{
product.image }}"></a>
{% endfor %}
I have assumed that you have MEDIA_URL=/media/ in your settings. Change this in src if you have different MEDIA_URL
Related
I have a following models.py for my Django blog, I made a following views.py to pass the value of the slug for my URL parameter.
However I am struggling to create a model in views to get other data(person & description) from Category class.
I have tried some patterns by myself but can not pass them to HTML. (always Error or not showing)
Can you please give me some idea of how to solve this.
models.py
class Category(models.Model):
person = models.CharField(max_length=20)
description = models.TextField()
slug = models.SlugField()
def __str__(self):
return self.person
views.py
def blog_category(request, category):
posts = Post.objects.filter(categories__slug__contains=category).order_by("-created_on").distinct()
context = {"category": category, "posts": posts}
return render(request, "blog_category.html", context)
HTML(Localhost:8000/slug)
{{ person }}
{{ description }}
this is full code of my models.py
class Category(models.Model):
person = models.CharField(max_length=20)
description = models.TextField()
slug = models.SlugField()
def __str__(self):
return self.person
class Recommender(models.Model):
recommender_name = models.CharField(max_length=20)
slug = models.SlugField()
def __str__(self):
return self.recommender_name
class Post(models.Model):
book_title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
book_link = models.CharField(max_length=255)
recommenders = models.ForeignKey("Recommender", on_delete=models.CASCADE,)
source = models.TextField()
source_link = models.CharField(max_length=255)
created_on = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField("Category", related_name="posts")
slug = models.SlugField()
def __str__(self):
return self.book_title
posts = Post.objects.filter(categories__slug__contains=category).order_by("-created_on").distinct()
Is going to return a queryset. It can have more than one instance of the model class (since you are using filter). In your context you are sending this queryset as posts to your templates.
So in your HTML you can use something like this. You need to use a for loop since there can be more than one item in posts.
{% for post in posts %}
{% for category in post.categories.all %}
{{ category.person }}
{{ category.description }}
{% endfor %}
{% endfor %}
I would look at this example.
Namely, if you render the template like it is shown in the example, you should be able to do
{{ category.person }} {{ category.description }}
Guys i am new to Django but i am bit clued up on a few things. I need help with urls. So in my models.py i defined category, subcategory and product models and all these models fields have got a slugfield. I have also managed to define the index page url and its working but after the index page i want when the user clicks the category link he will be able to see subcategories that are under categories, and when the user click on subcategories he will able to see products in the subcategory. So for example i would love to create an url like www.127.0000/electronics , electronics is the category and under electronics the user will be able to see subcategories like TV's , Microwaves...when the user click on TV's link the url will be www.127.0000/electronics/tvs and at this page the user will see the products. This is my models.py
class Category(models.Model):
name= models.CharField(max_length=100)
slug= models.SlugField(max_length= 100, unique= True)
class Meta:
ordering = ('name',)
verbose_name= 'category'
verbose_name_plural= 'categories'
def __str__(self):
return self.name
class SubCategory (models.Model):
name = models.CharField(max_length= 200)
slug = models.SlugField (max_length= 200, unique= True)
category= models.ForeignKey(Category, on_delete= models.CASCADE)
class Meta:
ordering = ('name',)
verbose_name= "SubCategory"
verbose_name_plural= 'SubCategories'
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=250)
slug = models.SlugField(max_length=250)
price = models.DecimalField(max_digits=10 , decimal_places=2)
image = models.ImageField(max_length=20, blank= True)
subcategory = models.ForeignKey(SubCategory, on_delete= models.CASCADE)
class Meta:
ordering = ('name',)
verbose_name= 'product'
verbose_name_plural = 'products'
def __str__(self):
return self.name
and this is my app urls
urlpatterns= [
path('', views.index, name='Index'),
path('<slug:category_slug>/', views.category, name= 'Categories' )
views.py file
def index (request):
""""Defines homepage"""
return render (request, 'index.html')
def category (request, category_slug):
"""Defines categories slug"""
categories= get_object_or_404(Category, slug=category_slug)
context= {'categories': categories}
return render (request, 'category.html', context}
this is my category html gile
{% extends 'base.html' %}
{% block content %}
<p>
<ul>
{% for category in categories %}
<li> {{category}} </li>
{% empty %}
<li> No Category has been added yet</li>
{% endfor %}
</ul>
</p>
{% endblock content %}
Could you please guide in the right direction so that when i click on www.127.0000/clothes i see the subcategories in clothes and the i click on and subcategories the url will change to maybe 127.000/clothes/men clothes and display the products in men clothes subcategory.
I am new to Django. I am trying to display data from my Project model in my index view, using a template. I tried my best to structure this app similar to the polls app. I'm not sure what I am doing wrong. I am using python 2.7, and django 1.8.6
Here is my url:
from django.conf.urls import url
from . import views
app_name = 'project'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
]
Here is my Model:
import datetime
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.contrib.auth.models import User
from django.utils import timezone
#python_2_unicode_compatible # only if you need to support Python 2
class Contractor(models.Model):
#project
name = models.CharField(max_length=50)
address = models.CharField(max_length=100, blank=True)
phone = models.CharField(max_length=14, blank=True)
city = models.CharField(max_length=60, blank=True)
state = models.CharField(max_length=2, blank=True)
created_by = models.ForeignKey(User, related_name='Contractor_created_by')
created_date = models.DateTimeField()
modified_by = models.ForeignKey(User, related_name='Contractor_modified_by')
modified_date = models.DateTimeField()
def __str__(self):
return self.name
#python_2_unicode_compatible # only if you need to support Python 2
class Project(models.Model):
name = models.CharField(max_length=50)
jobNumber = models.CharField(max_length=8)
shopOut = models.DateTimeField(null=True)
shopIn = models.DateTimeField(null=True)
delivery = models.DateTimeField(null=True)
job1 = models.CharField(max_length=50, null=True)
job2 = models.CharField(max_length=50, null=True)
job3 = models.CharField(max_length=50, null=True)
contractor = models.ForeignKey(Contractor, on_delete=models.CASCADE, default=101)
created_by = models.ForeignKey(User, related_name='Project_created_by')
created_date = models.DateTimeField(auto_now_add=True)
modified_by = models.ForeignKey(User, related_name='Project_modified_by')
modified_date = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.id:
self.created_by = User.objects.get(id=1)
self.modified_by = User.objects.get(id=1)
super(Project, self).save(*args, **kwargs)
year = datetime.datetime.now().year
self.jobNumber = '{}{:04d}'.format(year, self.id)
self.modified_by = User.objects.get(id=1)
super(Project, self).save(*args, **kwargs)
Here is my View:
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views import generic
from django.utils import timezone
from .models import Project
# Create your views here.
class IndexView(generic.ListView):
model = Project
template_name = 'project/index.html'
def get_queryset(self):
return Project.objects
class DetailView(generic.DetailView):
model = Project
Here is my Template:
{% load staticfiles %}
<h1>Projects</h1>
<ul>
{% for projects in project.get_queryset %}
in for loop
<!-- <li>{{ projects.name }}</li> -->
<li>Test</li>
{% endfor %}
</ul>
end of list
When I go to the page I get a h1 Project, an empty ul, and a line that says 'end of list'
In your get_queryset, you should return Project.objects.all().
In your template, you don't need to do project.get_queryset, the get_queryset method is called for you and the values are passed to the template as object_list and <objectname>_list, along with other parameters. In your case, the object is Project so there should be a project_list variable too along with object_list.
You can do:
{% for project in project_list %}
<li>{{ project.name }}</li>
{% endfor %}
Or:
{% for project in object_list %}
<li>{{ project.name }}</li>
{% endfor %}
You can read more about it here: https://docs.djangoproject.com/en/1.9/ref/class-based-views/generic-display/#listview
Your get queryset doesn't return a query set at the minute, currently its just returning a related manager. you should make it return a queryset...
def get_queryset(self):
return Project.objects.all() # added all
You might want to try:
{% for project in object_list %}
<li>{{ project.name }}</li>
{% endfor %}
object_list is the default name of queryset if you use ListView, you can change that by defining context_object_name in your view. Here's django doc about that.
Working on an application were I have a One to Many relationship where I have many Products and a few particular products will be related to only one Website.
On my Home page is where I display my listed sites from my Website Model I would like to show products for when the user clicks on anyone of the slugs on my Homepage the are redirected to go into a product page ( another template ) where I have all of the objects related from my Product Model to that particular website to display only.
Here is a User flow of my situation
Homepage --> click on website ('/browse/website_slug') ---> Go To --> Product page (filled with only Product Objects from related clicked slug)
Models.py in my product_extend app
Product Model:
class Product(models.Model):
"""
The product structure for the application, the products we scrap from sites will model this and save directly into the tables.
"""
product_name = models.CharField(max_length=254, verbose_name=_('Name'), null=True, blank=True)
product_price = CurrencyField( verbose_name=_('Unit price') )
product_slug_url = models.URLField(max_length=200, null=True, blank=True)
product_category = models.CharField(max_length=254, blank=True, null=True)
product_img = models.ImageField('Product Image', upload_to='product_images', null=True, blank=True)
product_website_url = models.URLField(max_length=200, null=True, blank=True)
product_website_name = models.CharField(max_length=254, blank=True, null=True)
#For Admin Purposes, to keep track of new and old items in the database by administrative users
date_added = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date added'))
last_modified = models.DateTimeField(auto_now=True, null=True, blank=True, verbose_name=_('Last modified') )
#For Admin Purposes, to make sure an item is active by administrative users
active = models.BooleanField(default=True, verbose_name=_('Active') )
# Foreign Key
website = models.ForeignKey(Website, null=True, related_name='website_to_product')
Website Model
class Website(models.Model):
name = models.CharField(max_length=254, blank=True, null=True, unique=True)
description = models.TextField(null=True, blank=True)
website_slug = models.SlugField(verbose_name=_('Website Slug'), unique=True)
site_logo = models.ImageField('Websites Logo', upload_to='website_logo_images', null=True, blank=True)
menswear = models.BooleanField(default=False, verbose_name=_('Menswear'))
womenswear = models.BooleanField(default=False, verbose_name=_('Womenswear'))
active = models.BooleanField(default=True, verbose_name=_('Active'))
Views in my product_extend app
view.py
class ProductView(ListView):
context_object_name = 'product_list'
template_name = 'product_extend/_productlist.html'
# queryset = ProductExtend.objects.filter(id=1)
model = Product
def get_context_data(self, **kwargs):
context = super(ProductView, self).get_context_data(**kwargs)
return context
class WebsiteView(ListView):
context_object_name = 'home'
template_name = 'homepage.html'
queryset = Website.objects.order_by('name')
model = Website
def get_context_data(self, **kwargs):
context = super(WebsiteView, self).get_context_data(**kwargs)
return context
Templates
Homepage.html
{% for object in home %}
<li class="page_tiles-home home-website-reveal">
<a href="browse/website_slug" data-title="{{object.name}}" data-description="{{object.description}}">
<img alt="{{object.name}}" src="{{MEDIA_URL}}{{object.site_logo}}" />
</a>
</li>
{% endfor %}
Product.html
{% for object in product_list %}
<li class="col n-4">
<figure class="rollover site">
<div class="scrap-likes"><span class="icon-heart"></span></div>
<img src="{{object.product_img}}" width="470" height="700">
<!-- <div class="scrap-from"> Scrapped from:<a class="scrap-site" target="_blank" href="{{object.product_website_url}}">{{object.product_website_name}}</a></div> -->
<div class="scrap-designer"> Scrapped from: <a class="scrap-site" target="_blank" href="{{object.product_website_url}}">{{object.product_website_name}}</a></div>
<div class="scrap-title">{{object.product_name }}, <span class="scrap-price">${{object.product_price}}</span></div>
<a class="scrap-buy" target="_blank" href="{{object.product_slug_url}}">View Item</a>
</figure>
</li>
{% endfor %}
Urls
my apps urls.py
urlpatterns = patterns('',
url(r"^$", WebsiteView.as_view(), name="home"),
url(r'^browse/', include('product_extend.urls')),
)
my apps product_extend urls.py
urlpatterns = patterns('',
??? No clue what to put ???
)
You can add this in product_extend urls.py:
urlpatterns = patterns('',
url(r'^(?P<website_slug>[\w]+)$', ProductView.as_view(), name='products_list'),
)
Then in ProductView override the get_queryset method to use the website_slug for filtering the queryset:
class ProductView(ListView):
context_object_name = 'product_list'
template_name = 'product_extend/_productlist.html'
# queryset = ProductExtend.objects.filter(id=1)
model = Product
def get_context_data(self, **kwargs):
context = super(ProductView, self).get_context_data(**kwargs)
return context
def get_queryset(self):
qs = super(ProductView, self).get_queryset()
return qs.filter(website__website_slug__exact=self.kwargs['website_slug'])
after reading twice, think what you want is:
url(r'^product/website/(?P<slug>)$', "your_view_to_peform_product_search_for_slug_website_here"),
and in your view "HTML"
href="product/website/{{ website.slug }}"
something like this...
I am trying to figure out what I thought would be easy to find an answer for. I am using django-taggit in a project and I simply want to return a list of objects when a tag is selected. I have tried this:
How do I create list and detail views for django-taggit?
But I cannot get it to work. It just renders a blank page. I think the problem is in my template code. Maybe someone can point me in the direction. Any help would be appreciated..Thanks a lot.
Here is my code:
models.py
from taggit.managers import TaggableManager
from django.template.defaultfilters import slugify
from ckeditor.fields import RichTextField
from taggit.models import TaggedItemBase
class Tagged(TaggedItemBase):
content_object = models.ForeignKey('Shows')
class Shows(models.Model):
title = models.CharField(max_length=40)
slug = models.SlugField(null=True, blank=True, unique=True)
tags = TaggableManager(through=Tagged)
hosts = models.ManyToManyField('Host', blank=True, null=True)
featured = models.BooleanField(default=False)
thumbnail = FilerImageField(related_name="thumbnail", help_text="Image should be: 550 X 350.")
playing_next = models.DateTimeField(null=True, blank=True)
description = RichTextField()
views.py:
class TaggedList(ListView):
queryset = Shows.objects.all()
paginate_by = 10
template_name = "tagged.html"
def get_queryset(self):
return Shows.objects.filter(tags__name__in=[self.kwargs['tag']])
urls.py:
urlpatterns = patterns('radio.views',
url(r'^$', 'main', name='app_main'),
url(r'^(?P<slug>[^\.]+)/detail/$', 'detail_view', name='detailsview'),
url(r'^(?P<tag>\w+)/$', TaggedList.as_view()),
url(r'^tagged/(?P<tag>\w+)/$', TaggedList.as_view())
)
Template code:
{% for objects in object_list %}
{{ objects.title }}
{{ objects.tag }}
{{ objects.slug }}
------
{% endfor %}
did you iterate over object_list in template? Because default list name in LiseView is object_list: doc: https://docs.djangoproject.com/en/dev/ref/class-based-views/generic-display/#listview