How to display elements of a Many-To-Many relation in Django? - python

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

Related

Setting Up Category and Forum and Loop it

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).

Showing entries associated with topic Django

I am trying to display the most up to date posts on the homepage. I can query the most recents topics and display them on the homepage however I am having trouble querying the entries associated with that topic. my plan is to display the first 50 or so words of the entries.
Models.py
class Topic(models.Model):
"""A topic that is associated with a certain Category"""
category = models.ForeignKey(Category, on_delete=models.CASCADE)
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'Topics'
def __str__(self):
"""Return string represtation of the model."""
return self.text
class Entry(models.Model):
"""A entry associated with a certain topic"""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'Entries'
def __str__(self):
"""Return string represtation of the model."""
return self.text[:50] + "..."
views.py index view:
def index(request):
"""The home page for the blogging website"""
topics = Topic.objects.order_by('-date_added')[:3]
entries = Entry.objects.filter(id__in=topics)
context = {'topics': topics, 'entries': entries}
return render(request, 'blogging_logs/index.html', context)
index.html
{% for entry in entries %}
<li>
{{ entry }}
</li>
{% empty %}
<li>
empty
</li>
{% endfor %}
{% for topic in topics %}
<li>
{{ topic }}
</li>
{% empty %}
<li>
empty
</li>
{% endfor %}
Thanks for the help in advance.
When querying the entries you should filter via topic_id field of the Entry not via id field. So you should do entries = Entry.objects.filter(topic_id__in=topics) in your index view.

django querysets in templates

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 %}

How should I add category in template?

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.

How to Access Many to many field in class based views in Django?

I have two models, Author & Book in models.py
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
age = models.IntegerField()
def __str__(self):
return '%s %s' %(self.first_name, self.last_name)
def __unicode__(self):
return '%s %s' %(self.first_name, self.last_name)
class Book(models.Model):
title = models.CharField(max_length=100) #name = title
pages = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
rating = models.FloatField()
author = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
def __str__(self):
return self.title
def __unicode__(self):
return self.title
Now i'm listing all the books using ListView. And on click of a book i'm getting information the book using following method
class BookDetailView(generic.DetailView):
template_name = 'books/book_detail1.html'
model = Book
context_object_name = 'book_detail'
I'm able to access title, pages, price, rating, publisher & publication_date but not getting all the Authors(Author list). While i'm going to simply print it, it prints None in template. I even try to iterate using For Loop but not done in template
views.py
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<ul>
<li>Price:{{ book_detail.price }}</li>
<li>Pub Date:{{ book_detail.publication_date }}</li>
<li>Title: {{ book_detail.title }}</li>
<li>Pages: {{ book_detail.pages }}</li>
<li>Rating: {{ book_detail.rating }}</li>
<li>Publisher: {{ book_detail.publisher }}</li>
<li>Author: {{ book_detail.author }}</li>
</ul>
</body>
</html>
Can anyone help me to out from this?
You have defined a many-to-many relationship between Book and Author which means that a book can have any number of authors. In order to display them you need to loop through the set of authors:
Authors:
<ul>
{% for author in book_detail.author.all %}
<li>{{ author }}</li>
{% endfor %}
</ul>
You might want to change the name of that field to authors to be less confusing.
Alternatively if you want only one author for a book then you need to use a ForeignKey instead of a ManyToManyField. In that case your existing template logic would work.
Or if you use Function-Based Views, define this view:
#views.py
def book_detail_view(request, id):
book = get_object_or_404(Book, id=id)
authors_of_book = book.questions.all()
template = 'books/book_detail1.html'
context = {'authors_of_book': authors_of_book}
return render(request, template, context)
And also in your template:
#html.py
<ul>
{% for author in authors_of_book %}
<li>{{ author }}</li>
{% endfor %}
</ul>
For more detail read this document.

Categories

Resources