I'm working on a Django blog, and having implemented category for detail page. I've stumbled upon an issue.
I want to display some categories of a product by using many to many field, I use the following code.
This my model.py file
from django.db import models
from django.urls import reverse
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('myshop:product_list_by_category', args=[self.slug])
class Product(models.Model):
category = models.ManyToManyField(Category)
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=200)
image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField()
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('myshop:product_detail', args=[self.slug])
This is views.py file
from django.shortcuts import render, get_object_or_404
from .models import Category, Product
# Create your views here.
def product_list(request, category_slug=None):
category = None
categories = Category.objects.all()
products = Product.objects.filter(available=True)
if category_slug:
category = get_object_or_404(Category, slug=category_slug)
products = products.filter(category=category)
return render(request, 'shop/product/list.html', {'category': category, 'categories': categories, 'products': products})
def product_detail(request, product_slug):
product = get_object_or_404(Product, slug=product_slug, available=True)
category = product.category.filter(is_active=True)
return render(request, 'shop/product/detail.html', {'product': product}, {'category': category})
and this is for detail.html page file
{% extends "shop/base.html" %}
{% load static %}
{% block title %}
{% if category %}{{ category.title }}{% else %}Products{% endif %}
{% endblock %}
{% block content %}
<div class="product-detail">
<img src="{% if product.image %}{{ product.image.url }}{% else %}{% static "img/no_image.png" %}{% endif %}">
<h1>{{ product.name }}</h1>
{% for category in category %}{{ category.name }}{% if not forloop.last %}, {% endif %}
{% endfor %}
<p class="price">${{ product.price }}</p>
{{ product.description|linebreaks }}
</div>
{% endblock %}
I followed the tutorial from Django by Example book.
in the book includes a code to display just one category of each product with the following code
<a href="{{ product.category.get_absolute_url }}">{{ product.
category }}</a>
and I change it to code like above in detail.html file.
Thank you for the help
Try
{% for category in product.category.all %}
{{ category }}
{% endfor %}
Related
Good day.
So i'm going through Django 2 by Example and i've encoutered a strange error when visiting a object from list of objects.
TypeError at /1/black-tea/
product_detail() got an unexpected keyword argument 'id'
The type it recieves is correct so small help would be appriciated.
code from views.py
def product_detail(request, product_id, slug):
product = get_object_or_404(Product, slug=slug,
id=product_id, available=True)
return render(request, 'shop/product/detail.html',
{'product': product})
models.py
class Product(models.Model):
category = models.ForeignKey(Category,
related_name='products',
on_delete=models.CASCADE)
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True)
image = models.ImageField(upload_to='products/%Y/%m/%d',
blank=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('name',)
index_together = (('id', 'slug'))
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('shop:product_detail',
args=[self.id, self.slug])
urls.py
path('<int:id>/<slug:slug>/', product_detail, name='product_detail'),
Code from template
<div id="main" class="product-list">
<h1>{% if category %} {{ category.name }}{% else %}Products{% endif %}</h1>
{% for product in products %}
<div class="item">
<a href="{{ product.get_absolute_url }}">
<img src="{% if product.image %}{{ product.image.url }}
{% else %}{% static 'img/images.jpeg' %}{% endif %}">
</a>
{{ product.name }}
<br>
${{ product.price }}
</div>
{% endfor %}
</div>
product_detail arguments should match your url mapping parameters in urls.py. The function definition should has arguments id, slug:
def product_detail(request, id, slug):
...
Am working with django 1.5.4 with python 2.7 I have tried to figure out what is the cause of this image to show like this
view.py
def single(request,slug):
product = Product.objects.get(slug=slug)
images = product.productimage_set.all()
categories = product.category_set.all()
context = {
"product":product,
"categories":categories,
"edit":True,
"images":images,
}
return render_to_response("products/single.html", context, context_instance=RequestContext(request))
and the single.html
{% extends "base.html" %}
{% block content %}
<h1>{{ product }}</h1>
{% for abc in images %}
<img src="{{ MEDIA_URL }}{{ abc.image }}" />
{% endfor %}
<ul>
{% for category in categories %}
<li>{{ category }}</li>
{% endfor %}
</ul>
{% if edit %}
Edit this product
{% endif %}
{% endblock %}
But it shows is this
picture
please help out with this issue is the problem form code or ?
models.py
class Product(models.Model):
user = models.ForeignKey(User,null=True,blank=True)
title = models.CharField(max_length=180)
description = models.CharField(max_length=500)
price = models.DecimalField(max_digits=20,decimal_places=2)
sale_price = models.DecimalField(max_digits=20,decimal_places=2,null=True,blank=True)
slug = models.SlugField()
order = models.IntegerField(default=0)
timestamp = models.DateTimeField(auto_now_add=True,auto_now=False)
updated = models.DateTimeField(auto_now_add=False,auto_now=True)
active = models.BooleanField(default=True)
def __unicode__(self):
return str(self.title)
class Meta:
ordering = ['-order']
class ProductImage(models.Model):
product = models.ForeignKey(Product)
image = models.ImageField(upload_to="products/image/")
title = models.CharField(max_length=120,null=True,blank=True)
featured_image = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True,auto_now=False)
updated = models.DateTimeField(auto_now_add=False,auto_now=True)
def __unicode__(self):
return str(self.title)
For django 1.5.4 you have to setting.py and edit MEDIA_ROOT
MEDIA_ROOT = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),"static","media")
MEDIA_URL = '/media/' ###This depends on the location of your media files### As this was for the location of my files
and on urls.py
url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root':settings.MEDIA_ROOT}),
I'm new to Django and I'ma building a basic blog application.
I cant show manytomany field (in tags) and a foreignkey field (comments) in my details page.
models.py
class BlogContent(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
content = models.TextField()
date_published = models.DateField(auto_now=True)
image = models.ImageField(upload_to='media/')
def __str__(self):
return self.title
class TagName(models.Model):
tag = models.ManyToManyField(BlogContent, null=True)
name = models.CharField(max_length=100, blank=True, null=True)
def __str__(self):
return self.name
class Comment(models.Model):
comt_text = models.TextField()
comments = models.ForeignKey(BlogContent, on_delete=models.CASCADE)
date_published = models.DateField(auto_now=True)
name = models.CharField(max_length=200, blank=True, null=True)
def __str__(self):
return self.name
views.py
def details(request, blogcontent_id):
data_blog = get_object_or_404(BlogContent, pk=blogcontent_id)
data_tag = get_object_or_404(TagName, pk=blogcontent_id)
data_comment = Comment.objects.select_related()
return render(request, 'details.html',
{'data_blog': data_blog, 'data_tag':data_tag, 'data_comment':data_comment})
details.html
{% extends 'base.html' %}
{% block body_base %}
<img class="card-img-top img-responsive" src={{ data_blog.image.url }} alt="Card image cap">
<h2 class="blog-post-title">{{ data_blog.title }}</h2>
<p class="blog-post-meta">{{ data_blog.date_published }} {{ data_blog.author }}</p>
<p>{{ data_blog.content }}</p>
{% endblock %}
how do i show foreignkey and manaytomany fieds after this?
TBH this is much easier if you use class based views.
The view would simply be:
class BlogContentDetail (DetailView):
model = BlogContent
The url call would be url(r'^blog-detail/(?P<pk>\d+)/$, BlogContentDetail.as_view(), name="blog_detail")
Your html file should be called blogcontent_detail.html and held within the app subfolder in the templates folder
The template would then be:
{% extends 'base.html' %}
{% block body_base %}
<img class="card-img-top img-responsive" src={{ object.image.url }} alt="Card image cap">
<h2 class="blog-post-title">{{ object.title }}</h2>
<p class="blog-post-meta">{{ object.date_published }} {{ object.author }}</p>
<p>{{ object.content }}</p>
{% for tag in object.tags_set.all %}{{ tag }}{% endfor %}
{% endblock %}
You can iterate the ManyToMany Field in this way
{% for tags in data_tag.tag.all %}
<p > {{tags}} </ p>
{% endfor %}
For foreign key
{{data_comment.comments}}
I'm having a little conundrum with sorting some items. I have a field called featured thats a boolean. I'm trying to display the featured coins first and then the remaining will be sorted by a different metric. In this code im using pub_date.
However, when I put an if statement in my template for the featured items it's still showing those that are set to false as well. I'll post code below.
index.html loops and if's
{% if featured_coins_list %}
{% for coin in featured_coins_list %}
<div class="large-6 medium-6 cell">
<h2>{{ coin.name }}</h2>
<p>Ticker: {{ coin.ticker }}</p>
<p>{{ coin.summary }}</p>
More Info</strong>
</div>
{% endfor %}
{% endif %}
{% if latest_coins_list %}
{% for coin in latest_coins_list %}
<div class="large-6 medium-6 cell">
<h2>{{ coin.name }}</h2>
<p>Ticker: {{ coin.ticker }}</p>
<p>{{ coin.summary }}</p>
More Info</strong>
</div>
{% endfor %}
</div>
{% else %}
<p>No coins are available.</p>
{% endif %}
views.py for index
def index(request):
featured_coins_list = Coin.objects.order_by('-featured')[:4]
latest_coins_list = Coin.objects.order_by('-pub_date')[:8]
context = {'featured_coins_list': featured_coins_list,
'latest_coins_list': latest_coins_list}
return render(request, 'coins/index.html', context)
models.py
class Coin(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
ticker = models.CharField(max_length=5)
featured = models.BooleanField(default=False)
logo = models.ImageField(upload_to='uploads/', verbose_name='image')
website = models.URLField(max_length=200, default="https://example.com/")
reddit = models.URLField(max_length=200, default="https://reddit.com/r/")
twitter = models.URLField(max_length=200, default="https://twitter.com/")
summary = models.CharField(max_length=500, blank=True)
description = models.TextField()
pub_date = models.DateTimeField('date published')
def __str__(self):
return self.ticker
def is_featured(self):
return self.featured
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
How should I go about listing the featured coins that are set to true first and then display the remaining items after?
You need to filter for featured=True in your queryset.
def index(request):
featured_coins_list = Coin.objects.filter(featured=True).order_by('-featured')[:4]
latest_coins_list = Coin.objects.order_by('-pub_date')[:8]
context = {'featured_coins_list': featured_coins_list,
'latest_coins_list': latest_coins_list}
return render(request, 'coins/index.html', context)
I have two models news and category, and in news I have foreignkey of category. I know how to display news with same category in a single template. but furthermore, in my home page I'm trying to display featured news of each category. this is where I'm having problem.
this is my models.py
class News(models.Model):
title = models.CharField(max_length=120)
content = models.TextField()
category = models.ForeignKey("Tag")
active = models.BooleanField(default=True)
featured = models.BooleanField(default=False)
top = models.BooleanField(default=False)
slug = models.CharField(max_length=255, unique=True)
featuredInCat = models.BooleanField(default=False)
objects = StoryManager()
class NewsQueryset(models.query.QuerySet):
def active(self):
return self.filter(active=True)
def featuredInCat(self):
return self.filter(featuredInCat=True)
class NewsManager(models.Manager):
def get_queryset(self):
return NewsQueryset(self.model, using=self._db)
def get_featuredInCat(self):
return self.get_queryset().active().featuredInCat()
def all(self):
return self.get_queryset().active()
class Category(models.Model):
title = models.CharField(max_length=120)
description = models.TextField(max_length=5000, null=True, blank=True)
In views.py
def category_list(request):
categoryList = NewsCategory.objects.all()
featuredInCat = News.objects.get_featuredInCat()
context = {
"featuredInCat":featuredInCat
"categoryList":categoryList,
}
return render(request,"news/category_list.html", context)
In my template
{% for category in categoryList %}
<div class='col-sm-4'>
<div id="container">{{category.title}}</h1>
<ul>
{% for x in featuredInCat %}
{{x.title}}</li>
{% endfor %}
</ul>
</div>
<hr>
</div>
{% endfor %}
then this shows the featuredInCat in every category where featuredInCat should be shown only in its Category section.
how do I fix this?
Take a look at the built-in regroup template tag of django. You will need to change your template to something like this:
{% regroup featuredInCat by category as news_list %}
<ul>
{% for news in news_list %}
<li>{{ news.grouper.title }}
<ul>
{% for item in news.list %}
<li>{{ item.title }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
You can change your for loop to iterate over the correct objects
{% for x in category.news_set.get_featuredInCat %}
You won't need the context variable anymore