How to access child class object that inherits parent class? - python

I have parent class and child class, that inherits parent class. And that is okay, I can iterate with for loop. Now I want to access child class (example: 'product_type' So basically, I'm confused how we inherits stuff from child class inside the same loop...
views.py
from django.views import generic
from . models import Category
from django.shortcuts import render
class CategoryListView(generic.ListView):
model = Category
template_name = 'category_list.html'
models.py
from django.db import models
import uuid
class Category(models.Model):
name = models.CharField(max_length=100, help_text='Category name')
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'Categories'
class Product(models.Model):
product_name = models.CharField(max_length=255, help_text='Product name')
# product_spec = models.TextField(max_length=5000, help_text='Product specs')
product_type = models.ForeignKey('Category', on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.product_name
category_list.html
{% extends 'base.html' %}
{% block body %}
{% for page in category_list %}
<li>{{ page.name }}</li>
<li>{{ page.product_name }} # <--------------- Now this is the point of
#my problem, I want to get
#product name from child
#class
#this returns empty <li>
{% endfor %}
{% endblock %}

you can to this
{% extends 'base.html' %}
{% block body %}
{% for page in category_list %}
<li>{{ page.name }}</li>
<li>{{ page.product_set.first.product_name }}
product name from child class, this returns empty <li>
{% endfor %}
{% endblock %}

First off change your product_type name to just category its way easier to understand and add an attribute related_name to it like this:
class Product(models.Model):
name = models.CharField(max_length=255, help_text='Product name')
category = models.ForeignKey('Category', on_delete=models.SET_NULL, null=True, related_name='products')
then in your template
{% for category in category_list %}
{{ category.name }}
{% for product in category.products.all %}
{{ product.name}}
... other product data
{% endfor %}
{% endfior %}

product_typein Product is a ForaignKey which means their will be multiple products having same Category so their exist two solutions
make product_type in Product one to one key, with this you shuld get single name by {{ page.product.product_name }}
print list of all products of the category, you can do this by iterating page.product_set as it is a list (iterable) with for loop.

Related

Filtering through relationship in views or template

I have one simple model called Product
class Product(models.Model):
title = models.CharField(max_length=150)
def __str__(self):
return self.title
And one other model called ExternalProduct
class ExternalProduct(models.Model):
title = models.CharField(max_length=100)
external_id = models.CharField(max_length=25)
internal_product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
blank=True,
related_name='external_products',
)
price = models.IntegerField()
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, blank=True)
image_url = models.CharField(max_length=255)
product_url = models.CharField(max_length=255)
store = models.CharField(max_length=50)
active = models.BooleanField(default=True)
class Meta:
ordering = ['price']
def __str__(self):
return self.title
On the detailed view of the Product, I want to display the price of all ExternalProduct related to the Product. It works all fine with this view
# products/views.py
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
and this template
# product_detail.html
{% extends '_base.html' %}
{% block title %}{{ product.title }}{% endblock title %}
{% block content %}
<div class="book-detail">
<h2>{{ product.get_active_external_products }}</h2>
</div>
<div>
<ul>
{% for ep in product.external_products.all %}
<li>{{ ep.price }}</li>
{% endfor %}
</ul>
</div>
{% endblock content %}
The problem is that I just want to display the price of ExternalProduct which has active=True
Been trying to solve it with a custom method in products views.py
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_active_external_products(self):
return self.external_products.objects.filter(active=True)
And a modified for loop inside product_detail.html
{% for ep in product.get_active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}
But unfortunately without any success. The site does not crash, just doesn't show anything other than the rest of the html file.
Any advice?
You can use prefetch_related with Prefetch to filter only active external products. For this you need to override get_queryset() method in your view:
from django.db.models import Prefetch
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_queryset(self):
return Product.objects.prefetch_related(Prefetch("external_products", queryset=ExternalProduct.objects.filter(active=True), to_attr="active_external_products"))
Note to_attr="active_external_products" part. It tells that active external products will be available as active_external_products attribute. So in template you can get them like this:
{% for ep in product.active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}
Or you can just override get_context_data() to insert external products to the context directly:
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(****kwargs)
context["active_external_products"] = self.object.external_products.filter(active=True)
return context
In this case in template you can use active_external_products variable name directly:
{% for ep in active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}

Django query does not select related_name

I do have the following models in django:
class UserPayments(models.Model):
_DATABASE = "payments"
id = models.AutoField(primary_key=True)
paym_psp_id = models.ForeignKey(ConfigPSP, null=True, on_delete=models.SET_NULL)
[...]
class InvoiceNumbers(models.Model):
_DATABASE = "payments"
id = models.AutoField(primary_key=True)
inv_date = models.DateField(null=False)
inv_payment_id = models.ForeignKey(UserPayments, null=True, on_delete=models.DO_NOTHING, related_name='paym_invoice_meta')
[...]
As you can see they are related with "related_name" definition in the InvoiceNumbers model.
When I no do a query in django like:
payments = UserPayments.objects.filter(paym_user_id=user_id)
And iterate through this query in a template
{% for inv in payments %}
{% for meta in inv.paym_invoice_meta %}
{{ meta.inv_invoice_hash }}
{% endfor %}
{% endfor %}
I get the template error that pay_invoice_meta is not iterable. With a manual select statement including the left outer join I get all the results.
What am I missing here? How can I force the query to select the values from InvoiceNumbers?
Call the all method of the related object to return a QuerySet:
{% for inv in payments %}
{% for meta in inv.paym_invoice_meta.all %}
{{ meta.inv_invoice_hash }}
{% endfor %}
{% endfor %}

Display detail page for model using django-mptt-urls

Im trying to access the detailpage of a photo instance, but I can't seem to get it to work. (The category detail page works great!)
I would like to access http//domain.com/main-category/sub-cat/sub-sub-cat/photo-slug/
Models.py:
from mptt.models import MPTTModel, TreeForeignKey
class Category(MPTTModel):
name = models.CharField('category name', max_length=32)
parent = TreeForeignKey('self', null=True, blank=True, verbose_name='parent category', related_name='categories')
slug = models.SlugField(unique=True)
def get_absolute_url(self):
return reverse('gallery', kwargs={'path': self.get_path()})
class Photo(models.Model):
name = models.CharField('photo name', max_length=32)
parent = TreeForeignKey(Category, verbose_name='parent category', related_name='photos')
slug = models.SlugField()
class Meta:
unique_together = ('slug', 'parent')
urls.py:
url(r'^(?P<path>.*)', mptt_urls.view(model='gallery.models.Category', view='gallery.views.category', slug_field='slug'), name='gallery'),
views.py:
def category(request, path, instance):
return render(
request,
'gallery/category.html',
{
'instance': instance,
'children': instance.get_children() if instance else Category.objects.root_nodes(),
}
)
How is it possible to access the photo model using mtpp-urls ?
Edit 1:
Sorry, when I try a url like http//domain.com/main-category/sub-cat/sub-sub-cat/photo-slug The template display the Gallery main page because there is no instance when I try the url with the photo-slug (and yes, the photo has the category as a parent :)
Here is the category.html template:
<html>
<head>
<title>{% if instance %}{{ instance.name }}{% else %}Gallery main page{% endif %}</title>
</head>
<body>
<h3>{% if instance %}{{ instance.name }}{% else %}Gallery main page{% endif %}</h3>
Gallery main page
{% for ancestor in instance.get_ancestors %}
> {{ ancestor.name }}
{% endfor %}
> <strong>{{ instance.name }}</strong>
<h4>Subcategories:</h4>
<ul>
{% for child in children %}
<li>{{ child.name }}</li>
{% empty %}
<li>No items</li>
{% endfor %}
</ul>
<h4>Photos:</h4>
<ul>
{% for object in instance.photos.all %}
<li>{{ object.name }}</li>
{% empty %}
<li>No items</li>
{% endfor %}
</ul>
</body>
</html>
The reason why it doesn't work is that MPTT only works on one model at a time, in that case the gallery, not the photo.
So actually, in your models the Gallery is NOT the parent of the photo, but just a foreign key on the photo.
If you know that the last element of the slug is always a photo, you could probably create an intermediate view like this:
urls.py:
url(r'^(?P<path>.*)', views.photo, name='photo'),
views.py:
def photo(self, path=''):
path_parts = path.split('/')
gallery_path = '/'.join(path_parts[:-1])
photo_slug = path_parts[-1]
return mptt_urls.view(model='gallery.models.Category', view='gallery.views.photo_in_category', slug_field='slug')({'path': gallery_path, 'photo_slug': photo_slug})
def photo_in_category(request, path, gallery_instance, photo_slug=None):
photo_instance = Photo.objects.get(parent=gallery_instance, slug=photo_slug)
...
Otherwise, you may need to differentiate the URLS (which would also bring simpler code):
urls.py:
url(r'^photo/(?P<path>.*)/(?P<photo_slug>[-\w]+)/$', mptt_urls.view(model='gallery.models.Category', view='gallery.views.photo_in_category', slug_field='slug'), name='photo'),
url(r'^gallery/(?P<path>.*)/$', mptt_urls.view(model='gallery.models.Category', view='gallery.views.category', slug_field='slug'), name='gallery'),

how to filter story by it's belonging tag

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

How to use a ForeignKey field as filter in Django?

I have 2 models:
class Category(models.Model):
title = models.CharField(max_length=250)
### other fields
class Album(models.Model):
category = models.ForeignKey(Category)
subject = models.CharField(max_length=200)
### other fields...
.
I just wrote a view for filtering albums by specefic category, also I want them all in home.html template:
#views.py
def commercial(request):
commercial_subjects = Album.objects.filter(category__title__contains="commercial" )
return render(request, 'gallery/index.html', {'commercial_subjects': commercial_subjects})
And it works fine for just commercial category. It seems just like hardcoding if I want to write multiple views for each category like this one. What I need is a view or filtering process which shows all categories and their related album.subject automaticly. So the final result must be like this:
Personal
ALBUM 1
ALBUM 2
Commercial
ALBUM 4
ALBUM5
How can I do that?
Its easy. First of all give a related_name to the foreign key:
class Album(models.Model):
category = models.ForeignKey(Category, related_name='albums')
From view pass all categories:
def myView(request):
categories = Category.objects.all()
return render(request, 'gallery/index.html', {'categories': categories})
Then in template:
<ul>
{% for category in categories %}
<li>{{ category.title }}</li>
{% with category.albums.all as albums %}
{% if albums %}
<ul>
{% for album in albums %}
<li>{{ album.subject }}</li>
{% endfor %}
<ul>
{% endif %}
{% endwith %}
{% endfor %}
</ul>
#views.py
def commercial(request):
commercial_subjects = Album.objects.filter(category__title="commercial")

Categories

Resources