I'm creating a forum software in Django and I'm having a hard time figuring out how to relate the forums table to the categories. I want to display this to the index page:
Category 1
--forum 1
--forum 2
--forum 2
Category 2
--forum 1
--forum 2
--forum 3
These are my models:
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Forum(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='forums')
def __str__(self):
return self.name
Here are my views:
class HomeView(ListView):
context_object_name = 'name'
template_name = 'index.html'
def get_context_data(self, *args, **kwargs):
context = super(HomeView, self).get_context_data(*args, **kwargs)
context['forums'] = Forum.objects.all()
context['categorys'] = Category.objects.all()
return context
This is what I currently have on the home page, the only problem is, it's simply looping through all the categories and forums. I want the category to be looped in the first for loop, and in the second one to pull all the forums that belong to that category.
{% for category in categorys %}
--code
{% for forum in forums %}
--code
{% endfor %}
{% endfor %}
How do I fix this so that it displays properly and the relation is correct? A category can have many forums but a forum can only have one category. So a one-to-many relationship I believe.
~Thanks,
Taz
Its simple:
{% for category in categorys %}
{{ category.name }}
{% for forum in category.forums.all %}
{{ forum.name }}
{% endfor %}
{% endfor %}
Also, you do not need to pass forums through context variable(via get_context_data method).
Related
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 %}
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')
I have following models:
class Topic(models.Model):
title = models.CharField(max_length=140)
def __unicode__(self):
return self.title
class Meta:
verbose_name = _('topic')
verbose_name_plural = _('topics')
class TopicLabel(models.Model):
name = models.CharField(max_length=256)
order = models.IntegerField(null=True, blank=True)
def getTopics():
return TopicLabelConnection.objects.filter(labelId=self.id).orderby('order')
def __unicode__(self):
return self.name
class TopicLabelConnection(models.Model):
topicId = models.ForeignKey(Topic, related_name='connection_topic')
labelId = models.ForeignKey(TopicLabel, related_name='connection_label')
def __unicode__(self):
return self.labelId.name + ' / ' + self.topicId.title
There are
Topics,
TopicLabels and
connections between them (TopicLabelConnection).
One label can be assigned to many topics.
I want to display an ordered list with following structure:
Label 1
Topic 1
Topic 2
Topic 3
Label 2
Topic 4
Topic 5
Topic 6
where topics 1, 2 and are assigned to label 1 and topics 4, 5 and 6 - to label 2.
In order to do this, I created view function and HTML template fragment shown below.
View function
def home(request):
labels = TopicLabel.objects.filter(connection_label__isnull=False).distinct().order_by('order')
return TemplateResponse(request, 'home.tpl.html', locals())
Template fragment
<ol>
{% for cur_label in labels %}
<li>{{ cur_label.name }}</li>
<ol>
{% for cur_topic_label_connection in cur_label.getTopics %}
<li>{{ cur_topic_label_connection.topicId.title }}</li>
{% endfor %}
</ol>
{% endfor %}
</ol>
The result: Only the labels are displayed, but not their topics.
How should I change the code in order for both the labels and the topics to be displayed in the hiearchical list?
You should use a proper ManyToMany field:
class TopicLabel(models.Model):
...
topics = models.ManyToManyField(Topic, through=TopicLabelConnection)
Now your getTopics method can be removed, and in the template you can just do:
{% for topic in cur_label.topics.all %}
<li>{{ topic.title }}</li>
{% endfor %}
Note that the order_by call in getTopics makes no sense, because the only model that has an order field is TopicLabel, but you're trying to get topics, which has no order field.
You aren't properly filtering.
The problem is in the getTopics method
Try this instead:
return TopicLabelConnection.objects.filter(labelId__id=self.id).order_by('order')
Notice labelId is a TopicLabel, not its id
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")