Django relational database in view - python

I have a django application where I have few manytomany fields and im trying to show these relation records in my listing, however it all comes blank
so this is my model:
class Genre(models.Model):
genre_name = models.CharField(max_length=50)
genre_slug = models.SlugField(max_length=300)
genre_meta = models.TextField(max_length=300)
genre_description = models.TextField(max_length=300)
listing = models.BooleanField(default=True)
def __unicode__(self):
return self.genre_name
class Movie(models.Model):
movie_name = models.CharField(max_length=50)
movie_slug = models.SlugField(max_length=300)
movie_description = models.TextField(null=True, max_length=300)
movie_category = models.ManyToManyField(Category, related_name='category')
movie_genre = models.ManyToManyField(Genre, blank=True, related_name='genre')
listing = models.BooleanField(default=True)
def __unicode__(self):
return self.movie_name
this is my view
def AllMovies (request):
movies= Movie.objects.all().order_by('movie_name')
context = {'movies': movies}
return render_to_response('all.html', context, context_instance=
RequestContext(request))
and this is my template
{% for movie in movies %}
{{ movie }}
{% for genre in movies.genre.all %}{{ genre_name }}{% endfor %}
{% endfor %}
so there are three concerns here:
1- All I get is blank in my template
2- What would be the best option to show items which have been ticked as listed and hide the rest in template
3- This is a data bank so im sure we will need to use load more button in my template, but if i want to show 30 of items instead of all

Try this:
{% for movie in movies %}
{{ movie }}
{% for genre in movie.movie_genre.all %}{{ genre_name }}{% endfor %}
{% endfor %}
You were trying to iterate over movies.genre.all, which is wrong because:
movies is a queryset, you should use movie instead, as it is a Model instance
genre is not a field in the Movie model, you should use movie_genre, which is a ManyToManyField you are looking for
I believe you didn't understand the related_name attribute. It doesn't modify the behavior of the field (in this case: movie_genre). Instead, it changes the name of an attribute in a related model (in this case: Genre). For example, you can use it to get all related movies:
>>> genre = Genre.objects.get(name='Drama')
>>> genre.genre.all()
[<Movie>, <Movie>, ...]
As you can see, your choice of a related_name wasn't so good. Something like movies would be more appropriate.
Please, read the documentation once again, as you probably didn't understand Django's logic around relations (foreign keys, many-to-many, etc.).
Besides, here are a few style-related tips:
don't use movie_ and genre_ prefixes in field names - e.g. it is obvious that genre_name is a name of a genre, since it is defined inside Genre model
try to follow PEP8 (http://legacy.python.org/dev/peps/pep-0008/), as it is a standard in the Python community
read about class-based views (https://docs.djangoproject.com/en/dev/topics/class-based-views/) - you may find them very helpful

Related

For Loop in Django Template

I am working on a Django project. It has two Apps, one for explaining the topic, which has its own model, and views, and the second app is a quiz app, which has its own model and view, which will test the ability / learning of the students on each topics.
The main idea is that for each topic of each book, there will be a quiz to test the ability of the students.
my book explanation model is as follows:
class book_explination(models.Model):
title = models.CharField (max_length = 255)
slug = models.SlugField(max_length= 250, null = True, blank = True, unique= True)
lesson_no = models.CharField (max_length = 255)
book_text = models.TextField (blank=False, null=False)
book_text_explination = models.TextField (blank=False, null=False)
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, null= True, blank=True)
timestamp = models.DateField(auto_now=False, auto_now_add=True)
def __str__(self):
return self.title
my view for the course model is as follows:
def maths_topics(request):
content_list = book_explination.objects.filter(title = "maths")
return render(request, 'blog/maths_topics.html', {'content_list': content_list})
my url on url.py file is as under:
path('nahwa_meer_topics/', maths_topics, name = 'maths_topics'),
my quiz model is as follows:
class Quiz(models.Model):
title = models.CharField(max_length=255)
book_name = models.CharField(max_length=255)
slug = models.SlugField(blank = True)
topic = models.CharField(max_length=255)
number_of_questions = models.IntegerField()
class Meta:
verbose_name_plural = 'Quizes'
def __str__(self):
return self.title
my view for the quiz app is as follows:
def maths_quiz(request):
maths_quiz = Quiz.objects.filter(book__name = "maths")
return render(request, 'blog/maths_topics.html', {'maths_quiz': maths_quiz})
my django HTML template is as under:
{% for c in content_list %}
<ul class="list-group list-group-horizontal">
{% if c.title == "maths" %}
<li> {{ c.title }}</li>
<li> {{ c.lesson_no }}</li>
<li> {{ c.book_text }}</li>
{% endif %}
</ul>
{%endfor%}
{% for c in maths_quiz %}
<ul class="list-group list-group-horizontal">
{% if c.name == "maths" %}
<li> {{ c.topic }}</li>
<li> {{ c.title }}</li>
{% endif %}
</ul>
{% endfor %}
the HTML template is reflecting the maths book explanation, however, it is not showing the quiz part of the data.
My aim is to show the topics of a book in an html page, and in front of each topic there should be the quiz, related to that topic, bearing in mind that each topic will have it own separate quiz.
Thanks for your support and help. Looking forward for a positive response from your end.
Regards
If I understand this correctly, the maths_quiz variable holds a queryset of Quiz instances. In your template logic, you display a list item, if an instance's name attribute is "maths". However your Quiz model has no name field at all. Could it be that you meant to check the book_name attribute instead?
{% for c in maths_quiz %}
<ul class="list-group list-group-horizontal">
{% if c.book_name == "maths" %}
...
{% endif %}
</ul>
{% endfor %}
Additionally, I think you made an error in your query to begin with:
...
maths_quiz = Quiz.objects.filter(book__name = "maths")
There are two underscores in book__name, but your field has only one:
class Quiz(models.Model):
...
book_name = models.CharField(max_length=255)
Moreover, I just noticed that your whole if condition in the template seems to be redundant since you are doing that exact filtering in your query in the view function before passing the results to the template. So there is really no need to check the book_name again in the template. Every instance in that queryset will have book_name == "maths".
Same goes for the maths_topics view.
Finally, I assume that either the HTML is actually two separate templates or if not, that you are aware that only one of the two sections will ever be displayed depending on the view that is called, maths_topics or maths_quiz, not both.
As a side note: I would suggest using more descriptive variable names, if possible. Seeing as you are working with multiple Quiz instances, I would name the variable something like maths_quizzes and for the iteration I would use a name like quiz instead of c:
def maths_quiz(request):
maths_quizzes = Quiz.objects.filter(book__name = "maths")
return render(request, 'blog/maths_topics.html', {'maths_quizzes': maths_quizzes})
{% for quiz in maths_quizzes %}
...
{% endfor %}
EDIT
OK, now that you explained a bit more, it seems you do think that both querysets should appear somehow. You have two separate views. They render separate responses. If you want both querysets passed to your template data, then you should do that in one view:
def maths_view(request):
content_list = book_explination.objects.filter(title = "maths")
maths_quiz = Quiz.objects.filter(book_name = "maths")
return render(
request,
'blog/maths_topics.html',
{'content_list': content_list, 'maths_quiz': maths_quiz}
)
Then obviously you need to adjust your URL route to point to that view, something like this (for example):
path('yoururlpath/', maths_view, name = 'maths_view')
Also, it seems to me that if your two models are so intimately connected, you may want to consider creating some sort of a relationship between them. Namely, if a quiz is always related to exactly one book, this seems to call for a foreign key relationship from quiz to book.
I mean something like this:
class Quiz(models.Model):
book = ForeignKey(to=book_explination, ...)
...
This would allow you to get all the related objects in the same query.
You can read up on relationships in the Django documentation.

Django models referencing each other

I have two models and I want to put an if condition in my template to get the desired output.
I have the first model
class Subject(models.Model):
name = models.CharField(max_length=50)
books = models.ForeignKey('Books')
And the second one
class Books(models.Model):
name = models.CharField(max_length=50)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
And a view
def View(request):
subjects = Subject.objects.all()
return render('my_html.html",{'subjects':subjects})
In my model, I have wanted to do this.
{% for subject in subjects %}
{% if subject.books.count > 0 %}
{{ subject.name }}
{% endif %}
{% endfor %}
I thought the only way is to have the models referencing each other for my plan to work but I also think that looping will require more resources. Is there a better way of doing this either thro' the view or just in the models?
You only need to ForeignKey from Book to Subject, so:
class Subject(models.Model):
name = models.CharField(max_length=50)
# no books
class Books(models.Model):
name = models.CharField(max_length=50)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
then you can filter the subjects such that it only retrieves Subjects with at least one book:
def view(request):
subjects = Subject.objects.filter(books__isnull=False).distinct()
return render(request, 'my_html.html', {'subjects': subjects})
this will make a LEFT OUTER JOIN on the table of the Book model, and check if there is at least one book. You thus do not need to filter in the template (which is not a good idea anyway).

Is it possible to create a for loop inside a main for loop inside django templates?

I'm just learning django part time and I'm stuck on a problem that I just can't seem to find a solution to. I feel it is probably pretty simple, but I just can't seem to see it right now.
In django I have the following table
class category(models.Model):
category = models.CharField(max_length=100)
def __str__(self):
return f"{self.id} is {self.category}"
class knowledge(models.Model):
name = models.CharField(max_length=16)
location = models.URLField(max_length=1024)
title = models.TextField(blank=True, max_length=512)
description = models.TextField(max_length=1024)
category = models.ForeignKey(category, on_delete=models.CASCADE, related_name="content")
def __str__(self):
return f"{self.id}{self.name} {self.location} {self.description}"
class notes(models.Model):
note = models.TextField(blank=True, max_length=1024, default='none')
date_added = models.DateTimeField(blank=True, default=timezone.now)
knowledge = models.ForeignKey(knowledge, on_delete=models.CASCADE, related_name="mynotes")
def __str__(self):
return f"Added on {self.date_added}"
For my views I have
def kbs(request, category_id):
# get all the objects in the category table with the id specified
cate = category.objects.get(id=category_id)
# once you have the category id get all the objects from the knowledge table using the related name content
know = cate.content.all()
# for each id in know create a list of notes?
return render(request, "knowledge/category.html", {"cate": cate, "know": know,})
I can loop over all the items in the knowledge table and display them in my template. I wanted to add some notes to each knowledge article from another table. I added the notes table and referenced the knowledge table. In the template instead of displaying the id, I would like to loop over all the notes associated with that id and list those notes there.
here is my template
{% for knowledge in know %}
<h5{{knowledge.title}}</h5>
<p>{{ knowledge.description|linebreaks }}</p>
<p> {{ knowledge.id }}</p>
{{knowledge.name}}
{% endfor %}
I imagine there are two things that I need to do. I need to pass in a list of all the notes to my template from my views, then when it comes to the id I should be able to use templating to list all the items in my notes for that particular knowledge id. Perhaps a for loop nested in the main loop. I'm sure there must be an easy solution, I just can't seem to be able to figure it out. Can anyone assist?
Use the value of the related_name attribute to get the ForeignKey values of an object, in your template do something like this:
{% for knowledge in know %}
{{ knowledge }}
{% for note in knowledge.mynotes.all %} # btw I recommend renaming the 'mynotes' to something that is easier to understand like just 'notes'
{{ note }}
{% endfor %}
{% endfor %}

Django: Using backwards relationships in views.py

I'm trying to pass an object to my HTML template consisting of the parent object and all child objects that relate to it. For instance:
Model Chamber:
class Chamber(models.Model):
chamber_name = models.CharField(max_length=100)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
Has ChamberProperty:
class ChamberProperty(models.Model):
chamber = models.ForeignKey(Chamber, on_delete=models.CASCADE)
property_name = models.CharField(max_length=50)
property_value = models.CharField(max_length=100)
user_defined = models.BooleanField(default=True)
They are two separate models because the customer can add as many properties as they want to their chamber.
In my views.py
class ChambersView(generic.DetailView):
template_name = 'pages/chambers.html'
def get(self, request):
user = User.objects.get(username=request.user)
customer = Customer.objects.get(user=user)
chambers_list = list(Chamber.objects.filter(customer=customer))
try:
chamber_properties = list(ChamberProperty.objects.filter(chamber__in=chambers_list).order_by('id'))
except:
chamber_properties = "No properties"
form = ChambersFilterForm(request=request)
return render(request, self.template_name, {'filter_form':form, 'chambers_list': chambers_list, 'chamber_properties': chamber_properties})
Now, that does get me a list of all chambers, and a list of all chamber properties. Except they're not linked to each other. I'm not sure how to build a list of related objects. I read about backwards relationships just now, but I don't seem to grasp how to use them.
I tried the following:
chambers_and_props = Chamber.chamberproperty_set.all()
And I get the following error:
AttributeError: 'ReverseManyToOneDescriptor' object has no attribute 'all'
So I'm not quite sure how to use it. The threads I saw mentioned that a relationship in Django automatically add a reverse on ForeignKeys and the usage should be parent.child_set.all() with child in lower caps.
I get a ReverserManyToOneDescriptor object, but not sure how to turn that into a usable list I can pass on to my HTML template.
Any ideas as to what I'm doing wrong?
Your query does not work because you have not specified which Chamber you want to get the backwards relation for.
However, this is not the right approach. Presumably you want the ChamberProperty so you can list them against each Chamber in the template. So, you should follow the relationship there in the template - there's no need to query ChamberProperty separately in the view at all.
{% for chamber in chamber_list %}
{{ chamber.chamber_name }}
{% for property in chamber.chamberproperty_set.all %}
{{ property.property_name }} : {{ property.property_value }}
{% endfor %}
{% endfor %}
You are getting the error because you are trying Chamber.chamberproperty_set on the model Chamber. It will work on individual chamber instances:
You can do this in the view:
for chamber in chambers_list
properties = chamber.chamberproperty_set.all()
Or in the template:
{% for chamber in chambers_list %}
{{ chamber }}
{% for property in chamber.chamberproperty_set.all %}
{{ property }}
{% endfor %}
Then, in your view, you can use prefetch_related to reduce the number of SQL queries:
chambers_list = Chamber.objects.filter(customer=customer).prefetch_related('chamberproperty_set')

How to get a single object from a related model in django template

I have two models (Item and Album).
Album has a foreign key pointing to Item
like this
class Item(models.Model):
name = models.CharField(max_length=50)
price = models.IntegerField()
and
class Album(models.Model):
photo = ImageField(upload_to=item_directory_path)
caption = models.CharField(max_length=100, blank=True)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
default = models.BooleanField(default=True)
In my template
{% for item in item_tags %}
<div class="item-dock">
<a href="{{item.name}}">
<img src="{{item.album_set.photo.filter(default=True)}}" class="img-responsive">
<p class="item-name text-center">{{item.name|capfirst}}</p>
</a>
Now the real issue is. Given that querying on a related model will always return a queryset. How can I retrieve a single object(in this case, a single photo from this queryset where the "default=True") from the Album model for an Item in my template.
I know about queryset and related manager!
Any help, suggestions will be much appreciated
Just query the first element if that would suffice.
# something like this in your template
{% item.album_set.first.photo %}
You can use a custom template filter for this:
Details about creating and using custom template tags and filters can be found here:
https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/
from django import template
register = template.Library()
def default_image(queryset):
return queryset.get(default=True).photo.url
register.filter('default_image', default_image)
You can use it in the template like this:
{{ item.album_set|default_image }}
Remember you need to {% load your_custom_template_tags %} before using the the filter

Categories

Resources