I seem to have a problem with calling a method from a django template. Given the model:
class Gallery(models.Model):
class Meta:
verbose_name_plural = "Galerie"
def photo_count(self):
return self.photo_set.count()
def random_image(self):
return self.photo_set.get(id=random.randint(1,self.photo_count()))
title = models.CharField(max_length=50)
imgur_id = models.CharField(max_length=15)
date_created = models.DateTimeField('Data utworzenia', auto_now=True)
is_active = models.BooleanField()
def __unicode__(self):
return self.title
where Gallery is foreignKey for Photo
views.py:
def index(request):
galleries = Gallery.objects.get(is_active=True)
if galleries.count() is 0:
messages.warning(request, "Niestety, żadna galeria nie jest aktywna, zaglądnij niebawem!")
return redirect(reverse(home))
elif galleries.count() is 1:
return render(request, 'gallery/home.html', {'gallery': galleries})
else:
return render(request, 'gallery/index.html', {'galleries': galleries})
I want to do this in template:
{% for gallery in galleries %}
{{ gallery.random_image }} <br />
{% endfor %}
The result I get is:
[ Photo object ]
[]
[]
[]
But when i call
{% for gallery in galleries %}
{{ gallery.photo_count }} <br />
{% endfor %}
The result is correct.
Why does this not work?
In my opinion you are complicated a little bit.
class Gallery(models.Model):
class Meta:
verbose_name_plural = "Galerie"
def random_image(self):
return self.photo_set.order_by('?')[0] if self.photo_set.count() else None
title = models.CharField(max_length=50)
imgur_id = models.CharField(max_length=15)
date_created = models.DateTimeField('Data utworzenia', auto_now=True)
is_active = models.BooleanField()
def __unicode__(self):
return self.title
and in the template,
{% for gallery in galleries %}
{% if gallery.random_image %}
{{ gallery.random_image }} <br />
{% else %}
No images found.
{% endif %}
{% endfor %}
Read more on order_by here
Note that ? could prove slightly heavy. If so, get thelist of ids of the photo_set associated with this gallery object, and get a random id from this list, rather than doing a
random.randint(1,self.photo_count())
Add #property to your fonction.
#property
def photo_count(self):
return self.photo_set.count()
Related
I have two models: CaseRequest and Case. The CaseRequest object has values name and datebirth. I need to check if the same values exists for name and datebirth in Case. For instance, The result should be like this:
Case:
Name DateBirth
Don Honn 01.03.1959
Freak Hu 04.11.1993
Wend Don 06.03.1988
CaseRequest:
Name DateBirth
Tom Hawk 01.05.1999 - no
Don Honn 01.03.1959 - yes
Fold Len 03.07.1967 - no
Freak Hu 04.11.1993 - yes
Wend Don 13.12.1956 - no
My code:
Models.py
class CaseRequest(models.Model):
name = models.CharField(max_length=255)
datebirth = models.DateField()
status = models.CharField(max_length=255)
timeapplication = models.DateField()
category = models.CharField(max_length=255)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('caserequest')
class Case(models.Model):
name = models.CharField(max_length=255)
datebirth = models.DateField()
def __str__(self):
return self.name + ' | ' + str(self.datebirth)
def get_absolute_url(self):
return reverse('case_list')
Views.py
class CaseRequestView(ListView):
model = CaseRequest
template_name = 'caserequest.html'
caserequest.html
<div>
{% for caserequest in object_list %}
<div>
<p>{{ caserequest.name }}, {{ caserequest.datebirth }}</p>
<!-- The result should be here: YES or NO -->
</div>
{% endfor %}
</div>
You can simply create a #property decorator, say for example case_exists in the models so:
class CaseRequest(models.Model):
name = models.CharField(max_length=255)
datebirth = models.DateField()
status = models.CharField(max_length=255)
timeapplication = models.DateField()
category = models.CharField(max_length=255)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('caserequest')
#property
def case_exists(self):
return Case.objects.filter(name=self.name, datebirth=self.datebirth).exists()
Then in the template use it as:
<div>
{% for caserequest in object_list %}
<div>
<p>{{ caserequest.name }}, {{ caserequest.datebirth }} - {% if caserequest.case_exists %} Yes {% else %} No {% endif %}</p>
</div>
{% endfor %}
</div>
I think you must to create a py file called utils.py or similar in same directory where you have view.py with following code:
from django import template
from ptii.models import Case
register = template.Library()
#register.simple_tag
def get_case(caserequest_name, caserequest_datebirth):
if Case.objects.filter(name=caserequest_name).filter(
datebirth=caserequest_datebirth).exists():
return('Yes')
else:
return('No')
and in your html you have to load the file utils.py and use the function like this:
{% load utils.py %} {# load the file #}
{% get_case caserequest.name caserequest_datebirth as exist %} {# get Yes or No #}
Then you can use exist field in yout html as this:
{{ exist }}
I want to show my articles but
My article doesn't display on article_details.html
This is what my site looks like. You can see only my article's title works.
My models.py:
class Article(models.Model):
title = models.CharField(max_length=50, verbose_name="Title")
mini_description = models.TextField(max_length=100, default='', verbose_name='Mini_description')
content = models.TextField(blank=True, verbose_name="Content")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Date of add")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Update date")
photo = models.ImageField(upload_to='photos/', verbose_name="Photo")
is_published = models.BooleanField(default=True, verbose_name="Is published")
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name="Category")
def get_absolute_url(self):
return reverse("article_details", kwargs={'pk': self.pk})
def __str__(self):
return self.title
def __repr__(self):
return f"Article(pk={self.pk}, title='{self.title}')"
class Meta:
verbose_name = "Article"
verbose_name_plural = "Articles"
ordering = ['-created_at']
My views.py:
class ArticleListByCategory(ListView):
model = Article
context_object_name = 'articles'
template_name = 'blog/details.html'
def get_queryset(self):
sort_filed = self.request.GET.get('sorter')
articles = Article.objects.filter(category_id=self.kwargs['pk'])
if sort_filed:
articles = articles.order_by(sort_filed)
return articles
class ArticleDetails(ArticleListByCategory):
model = Article
context_object_name = 'article'
template_name = 'blog/article_details.html'
def get_queryset(self):
article = Article.objects.filter(pk=self.kwargs['pk'], is_published=True).select_related('category')
return article
def get_context_data(self, **kwargs):
context = super().get_context_data()
article = Article.objects.get(pk=self.kwargs['pk'])
context['title'] = f"Article: {article.title}"
return context
My article_details.html:
{% extends "base.html" %}
{% load static %}
{% block style %}
<link href="{% static 'blog/css/main.css' %}" rel="stylesheet">
{% endblock style %}
{% block title %}
{{ title }}
{% endblock %}
{% block main %}
<section class="articles" id="articles">
<div class="card_details">
<h1>{{ article.title }}</h1>
<p><em>{{ article.mini_description }}
</em>
</p>
<img src="">
<div class="description">
<p>
{{ article.content }}
</p>
</div>
<div class="card-footer text-muted">
{{ article.created_at| timesince }} ago
</div>
</div>
</section>
{% endblock main %}
I don't know why my article doesn't display.
What I'd suggest here is using the Django Generic View called DetailView. For example, you could do the following:
from django.views.generic import DetailView
class ArticleDetails(DetailView):
context_object_name = 'article'
template_name = 'blog/article_details.html'
# Using the get_object method here, not get_queryset.
def get_object(self, queryset=None):
article = Article.objects.filter(pk=self.kwargs['pk'], is_published=True).select_related('category')
return article
Ideally, that should work.
I'm trying to loop over my FeatureCatergories, FeatureSubcategories and Features. I'm able to loop over my feature categories just fine. Now I want to loop over my feature subcategories and finally features. I'm not sure what to call in my template for subcategories.. Should it be {{featuresubcategory.title}}? What about features?
views.py
def features_view(request):
context = {
"feature_categories": FeatureCategory.objects.prefetch_related('featuresubcategory_set').all(),
}
return render(request=request, template_name="main/features.html", context=context)
template.html
{% for category in feature_categories %}
{{category.title}}
{% for subcategory in featuresubcategory %}
{{ subcategory.title }}
{% endfor %}
{% endfor %}
models.py
class FeatureCategory(models.Model):
title = models.CharField(max_length=50)
featured_image = models.ImageField(blank=True, upload_to="features/")
category_slug = AutoSlugField(null=True, default=None,
unique=True, populate_from='title')
class Meta:
verbose_name_plural = "Feature Categories"
def __str__(self):
return self.title
class FeatureSubcategory(models.Model):
title = models.CharField(max_length=50)
category = models.ForeignKey('FeatureCategory', on_delete=models.CASCADE)
category_slug = AutoSlugField(null=True, default=None,
unique=True, populate_from='title')
class Meta:
verbose_name_plural = "Feature Subcategories"
def __str__(self):
return self.title
class Feature(models.Model):
title = models.CharField(max_length=150)
category = models.ManyToManyField(FeatureSubcategory)
description = models.TextField()
featured_image = models.ImageField(upload_to=image_dir)
class Meta:
verbose_name_plural = "Features"
def __str__(self):
return self.title
In your template, you must change
featuresubcategory with category.featuresubcategory_set.all()
also,
use prefetch_related('featuresubcategory') instead of prefetch_related('featuresubcategory_set')
{% for category in feature_categories %}
{{category.title}}
{% for subcategory in category.featuresubcategory_set.all %}
{{ subcategory.title }}
{% endfor %}
{% endfor %}
For features of subcategory, add related_name to your category field
category = models.ManyToManyField(FeatureSubcategory,related_name='subcategory_features')
and now, you can use it in your template, and don't forget to make migrate
{% for category in feature_categories %}
{{category.title}}
{% for subcategory in category.featuresubcategory_set.all %}
{{ subcategory.title }}
{% for feature in subcategory.subcategory_features.all %}
{{feature.title}}
{% endfor %}
{% endfor %}
{% endfor %}
You can see related objects reference here
I have a list that is generated by a method on one of my models. On the home page it works wonderfully, however when I go to a detail view of one project I can access all the parts of that project as they are direct fields of the Model, but I can't access the items in the list.
Model:
class Project(models.Model):
date_published = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
area = models.ForeignKey(Area, on_delete=models.PROTECT)
title = models.CharField(max_length=128, unique=True)
slug = models.SlugField(max_length=64)
summary = models.CharField(max_length=256)
others = models.CharField(max_length=128, blank=True)
deadline = models.DateField(null=True, blank=True)
priority = models.ForeignKey(Priority, on_delete=models.PROTECT)
closed = models.DateTimeField(null=True, blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Project, self).save(*args, **kwargs)
#property
def updates(self):
updates = []
sequence_id = 1
categories = set(self.update_set.all().values_list(
'category__id', flat=True))
for cat_id in categories:
a = Update.objects.filter(
project=self, category__id=cat_id).order_by('added').last()
if cat_id == sequence_id:
updates.append(a)
else:
for i in range(cat_id - sequence_id):
updates.append('null')
updates.append(a)
sequence_id = cat_id
sequence_id += 1
return updates
class Update(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
category = models.ForeignKey(UpdateCategory, on_delete=models.PROTECT)
update = models.TextField(max_length=240, blank=True)
added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.update
The view is simple:
class ProjectDetailView(DetailView):
template_name = 'project_portal/project_detail.html'
queryset = Project.objects.all()
and here is the dynamic url that I am using:
path('project/<int:pk>/',
ProjectDetailView.as_view(), name='project_detail'),
As for the template, I'm lost, here is one of the things I have tried:
<!DOCTYPE html>
{% extends "project_portal/base.html" %}
{% block home %}
<div id="main">
<div id="content">
<div>
<h1>{{ object.title }}</h1>
<h1>hello</h1>
{% if object_list %}
{% for item in updates %}
<p>{{ item }}</p>
{% endfor %}
{% else %}
<h2>No records found for this project</h2>
{% endif %}
</div>
</div>
</div>
{% endblock %}
What do I need to do to access the "updates" list that gets generated?
update is a property of the model instance, you need to access it from there like any other attribute. Also note, there is no object_list in a detail view.
<div>
<h1>{{ object.title }}</h1>
<h1>hello</h1>
{% for item in object.updates %}
<p>{{ item }}</p>
{% endfor %}
</div>
I have a little problem. I want to create something like a webpages directory. In my model I've create a class Kategorie, and class Firma. Class Kategoria creating main categories and subcategories. In class Firma I can define in witch category and subcategory the new record will be belong. My question is: How to display in html on main page main categories and little lower the subcategories like in this picture
Here is my code
models.py
from django.db import models
from django.contrib.auth.models import User
class Kategoria(models.Model):
name = models.CharField(max_length=250, verbose_name='Kategoria')
slug = models.SlugField(unique=True,verbose_name='Adres SEO')
parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name = 'Kategoria'
verbose_name_plural = 'Kategorie'
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' / '.join(full_path[::-1])
class Firma(models.Model):
user = models.ForeignKey(User, default=1, verbose_name='Użytkownik', on_delete=models.CASCADE)
title = models.CharField(max_length=250, verbose_name='Nazwa firmy')
slug = models.SlugField(unique=True, verbose_name='Adres SEO')
category = models.ForeignKey('Kategoria', null=True, blank=True, verbose_name='Kategoria', on_delete=models.CASCADE)
content = models.TextField(verbose_name='Opis')
draft = models.BooleanField(default=False, verbose_name='Szablon')
publish = models.DateField(auto_now=False, auto_now_add=False)
class Meta:
verbose_name='Firma'
verbose_name_plural='Firmy'
def __str__(self):
return self.title
views.py
from django.shortcuts import render, get_object_or_404
from .models import Kategoria, Firma
def widok_kategorii(request):
kategorie = Kategoria.objects.filter().order_by('name')
context = {'kategorie': kategorie}
return render(request, 'ogloszenia/index.html', context=context)
index.html
{% include 'ogloszenia/header.html' %}
Wyświetl kategorie<br>
{% for kategoria in kategorie %}
<b>{{kategoria.name}}<br></b>
{% endfor %}
{% include 'ogloszenia/footer.html' %}
So the problem is sub categories, right?
You can use inclusion_tag in your template as i mentioned here once:
Tree Structure (Foreign Keys to itself) and templates
If you need to render multiple level of sub categories then just do as i mentioned in the link.
But if you just need the first level, then it's pretty simple:
views.py:
Getting categories without any parent (line #2)
def widok_kategorii(request):
### Get the categories without any parent.
kategorie = Kategoria.objects.filter(parent=None).order_by('name')
context = {'kategorie': kategorie}
return render(request, 'ogloszenia/index.html', context=context)
Template:
{% include 'ogloszenia/header.html' %}
Wyświetl kategorie<br>
<ul>
{% for kategoria in kategorie %}
<li>
{{kategoria.name}}
{% if kategoria.children.count > 0 %}
<ul>
{% for sub in kategoria.children.all %}
<li>{{ sub.name }}</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% include 'ogloszenia/footer.html' %}
You can just design it to be look like the picture you posted but this is the way to achive the tree structure for such a design.