I am trying to make specific queries by using some model entry fields.
I have the following model entry:
models.py
class Work(models.Model):
categories =(
('cat1', 'cat1'),
('cat2', 'cat2'),
('cat3', 'cat3'),
('cat4', 'cat4'),
('cat5', 'cat5'),
)
title = models.CharField(max_length=200)
description = RichTextUploadingField(config_name='awesome_ckeditor')
date = models.DateTimeField(default=timezone.now)
category = models.CharField(max_length=200, choices = categories, default = 'projects')
thumb = models.ImageField(upload_to = 'works/thumbs', blank = True)
content = models.FileField(upload_to = 'works/content_media', blank = True)
published = models.BooleanField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("work_detail",kwargs={'pk':self.pk})
#property
def thumb_url(self):
if self.thumb and hasattr(self.thumb, 'url'):
return self.thumb.url
#property
def content_url(self):
if self.content and hasattr(self.content, 'url'):
return self.content.url
here is the view:
views.py
class WorksListView(ListView):
template_name = 'template.html'
model = Work
def get_queryset(self):
return Work.objects.filter(published=True).order_by('-date')
and I am trying to query first by the category field then by entry in the following template:
template.html
{% for category in works_list.category %}
<ul data-category-name={{category.name}}>
{% for work in category.works %}
<li data-thumbnail-path={{thumbnail.url}} data-url={{content.url}} >
<div>
<p class="gallery1DecHeader">{{work.title}}</p>
<p class="gallery1DescP">{{work.description}}</p>
</div>
</li>
{% endfor %}
{% endfor %}
what do I need to change?
Okay, from what I can see there are a few problems. First, try adding context_object_name = 'works_list' That way you will be able to refer to the object_list as works_list like you do in the template outer for loop. The bigger problem is you are iterating over works_list.category, which according to your Work model is a Charlist(). I think you might be getting confused about what the choices kwarg does and expecting {% for category in works_list.category %} to iterate over your choices and giving you the list of cats you defined in categories. As far as I know, that's not how choices works.
If you go to your admin panel and add a new entry for your Work model, you'll see that category has a dropdown list that contains your list of cats. So, choices defines a set of legal category options for new Work objects, not a list in existing Work objects.
I think what you actually want is an additional model Category which defines: work = models.ForeignKey(Work, on_delete=models.CASCADE) as a one-to-many relationship. Basically, you want is for Work to have a subset of Category objects that you can iterate over. This will involve redesigning the way you structure and access your data though.
You need to change at least your views.py and template.html. Add a context_object_name and an extra context(Doc Link)
views.py
class WorksListView(ListView):
template_name = 'template.html'
model = Work
context_object_name = 'work_list'
def get_queryset(self):
return Work.objects.filter(published=True).order_by('-date')
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(WorksListView, self).get_context_data(**kwargs)
# Insert categories so that it can be used in template
context['categories'] = Work.categories
return context
template.html
{% for category in categories%}
<ul data-category-name={{category.0}}>
{% for work in work_list %}
{% if category.0 == work.category %}
<li data-thumbnail-path={{work.thumb_url}} data-url={{work.content_url}} >
<div>
<p class="gallery1DecHeader">{{work.title}}</p>
<p class="gallery1DescP">{{work.description}}</p>
</div>
</li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}
Related
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 %}
I have 2 models:
class Director(models.Model):
director_name = models.TextField(primary_key=True)
director_firstname = models.CharField(max_length=32)
def __str__(self):
return f'{self.director_name}'
def get_absolute_url(self):
return reverse('director-detail', args=[str(self.director_name)])
class Meta:
managed = False
db_table = 'director'
ordering = ['director_name']
class Connection(models.Model):
director_connect = models.ForeignKey('Director', models.DO_NOTHING, db_column='director_connect')
worker_connect = models.ForeignKey('Worker', models.DO_NOTHING, db_column='worker_connect')
class Meta:
managed = False
db_table = 'connection'
unique_together = (('director_connect', 'worker_connect'),)
ordering = ['director_connect']
def __str__(self):
return f'{self.director_connect}'
def get_absolute_url(self):
return reverse('director-detail', args=[str(self.director_connect)])
This is my view.py file:
class DirectorDetailView(generic.DetailView):
model=Director
template_name = 'company/director_detail.html'
def get_context_data(self, **qwargs):
a = super(DirectorDetailView, self).get_context_data(**qwargs)
a['cons'] = Connection.objects.all()
return a
When I am trying to match 2 columns in html with for loop and if statement, they do not match, although they are similar one to one copies of each other:
{% extends "index.html" %}
{% block content %}
<h1>{{ director.director_name }}</h1>
<p>{{ director.firstname }}</p>
{% for con in cons %}
{% if object.director_name == con.director_connect %}
<li>{{con.id}}, {{con.director_connect}}, {{con.worker_connect}}</li>
{% endif %}
{% endfor %}
{% endblock %}
How could I fix it? I would like to bring the list of workers under director's name. Any link to probable answer (I was looking for it, but I couldn't find it) or slight push towards the right direction would be nice.
The issue is that in your template you're comparing the director's name with the primary key that's referenced in the Connection by means of the director_connect ForeignKey. The simplest solution would be to change the if expression:
{% extends "index.html" %}
{% block content %}
<h1>{{ director.director_name }}</h1>
<p>{{ director.firstname }}</p>
{% for con in cons %}
{% if object == con.director_connect %} // Change it so it compares the primary key
<li>{{con.id}}, {{con.director_connect}}, {{con.worker_connect}}</li>
{% endif %}
{% endfor %}
{% endblock %}
Other way to achieve this would be to filter the workers by director in the view, using filter:
class DirectorDetailView(generic.DetailView):
model=Director
template_name = 'company/director_detail.html'
def get_context_data(self, **qwargs):
a = super(DirectorDetailView, self).get_context_data(**qwargs)
director = self.get_object()
a['cons'] = Connection.objets.filter(direct_connect=director).all()
return a
This is better because it will filter the workers in the database using SQL which is probably faster than the template engine.
Other solution would be to use a ManyToManyField and let django ORM take care of the relations for you, but that would require refactoring the code and maybe some database migrations.
I have two models one is club details and the other is player structure. so my plan is to based on the club iteration which has values like'test', 'odi', 'listA' i need to represent its respective player structure in my Django template.
models.py
class Clubstate(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class PlayerStructure(models.Model):
clubstate = models.ForeignKey('Clubstate', on_delete=models.CASCADE, related_name='clubstate')
country = models.ForeignKey('TeamStructure', on_delete=models.CASCADE, null=True, related_name='identifier1')
firstname = models.CharField(max_length=255)
lastname = models.CharField(max_length=255)
imageUri = models.ImageField(upload_to='images/', verbose_name='image')
JerseyNumber = models.IntegerField()
def __str__(self):
return self.firstname + self.lastname
In views.py I'm using DetailView to represent the data.
class PlayerStatistics(DetailView):
context_object_name = 'Clubstate_details'
model = models.Clubstate
template_name = 'CricketDetails/playerstatistics_details.html'
Template html
<div class="jumbotron">
<div class="container">
<div class="row">
{% for club in Clubstate_details %}
<h1>{{club.name</h1>
{% endfor %}
</div>
</div>
</div>
My thought, logic is like
{% for club in clubs %}
{{club.name}}
{{club.player_structure.firstname}}
{{club.player_structure.lastname}}
{% endfor%}
So that for indivdual club i get its respective player structure.
I get TypeError: 'Clubstate' object is not iterable error.
Hope that makes sense...
Detail view won't give a list of items, instead it gives a single view only so change the DetailView to ListView and iterate through the output
class PlayerStatistics(ListView):
context_object_name = 'Clubstate_details'
model = models.Clubstate
template_name = 'CricketDetails/playerstatistics_details.html'
if you want detail view can get it by
{{ Clubstate_details.name }}
with DetailView
FK instance can be accessed by
{% for obj in Clubstate_details.playerstructure_set.all %}
{{ obj.firstname }}
{% endfor %}
Models:
class Category(models.Model):
name = models.CharField(max_length=20)
slug = models.SlugField(max_length=200, unique=True )
def __unicode__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length = 100)
content = models.TextField(max_length = 600, default = 'cool' )
date_of_creating = models.DateTimeField(auto_now=False, auto_now_add=True)
image = models.ImageField(
upload_to=upload_location,
null=True,
blank=True,
height_field="height_field",
width_field="width_field"
)
height_field = models.IntegerField(default=0)
width_field = models.IntegerField(default=0)
category = models.ForeignKey('Category')
slug = models.SlugField(unique=True, blank=False)
def __unicode__(self):
return self.title
Views:
def category(reguest, slug):
category = Category.objects.get(slug=slug)
post = Post.objects.filter(category=category)
html = 'category.html'
context = {
'category': category,
'post': post,
}
return render(reguest, html, context)
def listofposts(request):
query_set_list = Post.objects.all()
context = {
"list" : query_set_list,
}
html = 'base.html'
return render(request, html, context)
I dont know what should I write in template to filter my posts by categories.
I need to choose category and display posts of this category. How can I filter it by category?
I believe you are asking how to show your posts, arranged by categories? If that is what you are after, your template should look something like this:
template.html
{% if category %}
{% if post %}
{% for c in category %}
{% for p in post %}
{% if p.category == c %}
<div>{{ p.title }}</div> <!-- and whatever else you want to display and however you want to style it I am just giving an example -->
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}
{% endif %}
Based on what you do in your views, this is one way to display by category. Note there are better ways of how you can traverse the data, and group them up, but you are asking for this. So I hope this helps you out!
EDIT 1
After reading your question again (i believe there was an edit) I saw that you are asking how to show the posts for the selected category. I am assuming that since you have a slug the category selected is in the URL. So indeed in the view you are selecting the correct posts. So in order to display the posts from the selected category you simply have to do this in your template:
{% if post %}
{% for p in post %}
<div>{{ p.title }}</div> <!-- and whatever else you want to display and however you want to style it I am just giving an example -->
{% endfor %}
{% endif %}
Hope this helps!
You can try to use Ajax to filter by category (Tutorial here).
Make a selection box containing all the categories, then when a new option is selected, trigger the Ajax query to select all posts from that category.
I have been trying to do this nested loop for couple of hours, but so far none of my results worked. Here is what i have so far:
index.html
{% for category in categories %}
<div class="col-md-12 column category list-group">
<p><b>{{ category.name }}</b></p>
{% for subcategory in subcategories %}
<div class="list-group-item">
<h5 class="mb-1">{{ subcategory.name }}</h5>
<small>{{ subcategory.description }}</small>
<small>{{ subcategory.count_threads }}</small>
</div>
{% endfor %}
</div>
{% endfor %}
views.py
class IndexView(ListView):
template_name = 'myForum/index.html'
context_object_name = 'SubCategory_list'
queryset = SubCategory.objects.all()
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['subcategories'] = SubCategory.objects.all()
return context
models.py
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class SubCategory(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
category = models.ForeignKey('Category', default=0)
def __str__(self):
return self.name
Output
Output
My problem is that SubCategory - News, belongs to Infromation, category Off-Topic belongs to General. For some reason loop displays all of subcategories, and i can't figure out how to narrow it to current Category.
You can change the inner loop to:
{% for subcategory in category.subcategory_set.all %}
This will loop over the subcategories for the current category.
Since you are looping over categories in the outer loop, you can change the queryset in the list view to use the Category model. You can cut down the number of queries by prefetching the subcategories.
It also looks as if you can remove the get_context_data method, since the list view already makes the queryset available in the template context.
class IndexView(ListView):
template_name = 'myForum/index.html'
context_object_name = 'catrgories'
queryset = Category.objects.all().prefetch_related('subcategory_set')