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.
Related
I'm a starter with Python/Django and I'm currently trying to make a basic app for making a collection log. First the user chooses a category and then inserts objects under that category.
I have two classes in models.py with the following code:
class object_category(models.Model):
"""This class contains the users different categories"""
CATEGORY_CHOICES = [
('category1', 'category1'),
('category2', 'category2'),
('category3', 'category3'),
]
"""Plural for multiple categories"""
class Meta:
verbose_name_plural = 'Categories'
"""Returns the above stated choices"""
category = models.CharField(max_length=50, choices=CATEGORY_CHOICES)
def __str__(self):
return self.category
class object_name(models.Model):
"""This class contains the object name that is housed within a certain category"""
"""Links the object to one of the chosen categories"""
category = models.ForeignKey(object_category, on_delete=models.CASCADE)
# Placeholder for connection with a plant database API
object = models.CharField(max_length=50)
"""Return the object input from the user"""
def __str__(self):
return self.object
and here is the views.py code:
def collection(request):
"""The page that opens the collection of objects"""
object_category = Object_category.objects.order_by('category')
object_name = Object_name.objects.order_by('object')
context = {
'object_category': object_category,
'object_name': object_name
}
return render(request, 'plntz_main/collection.html', context)
finnaly here is my html doc code:
{% for category in object_category %}
{% for object in object_name %}
<h3>{{ category }}</h3>
<li>{{ object }}</li>
{% empty %}
<li>No category has been added yet.</li>
{% endfor %}
{% endfor %}
This displays the category and the objects but they are not linked.
What I'm trying to display is:
The categories the user selected
Each object in that specific category
Can anyone explain how I alter the code do get this result ?
Sorry is this is a mess. I'm new and this is my first try on a project and my first question on stackoverflow.
Thanks!
You should read the documentation first and do some step by step basic tutorials, read docs here about filtering querysets Django Querysets.
You can do something like this to achieve it
First, set related_name to your category foreign key
class object_name(models.Model):
"""This class contains the object name that is housed within a certain category"""
"""Links the object to one of the chosen categories"""
category = models.ForeignKey(object_category, on_delete=models.CASCADE, related_name='object_names')
# Placeholder for connection with a plant database API
object = models.CharField(max_length=50')
"""Return the object input from the user"""
def __str__(self):
return self.object
Then in your views.py you just need to do
object_categories = Object_category.objects.all().order_by('category')
context = {
'object_categories': object_categories,
}
and then I your template file do something like this:
{% for category in object_categories %}
<h3>{{ category }}</h3>
{% for name in object_categories.object_names.all %}
<li>{{ name.object }}</li>
{% endfor %}
{% empty %}
<li>No category has been added yet.</li>
{% endfor %}
I did not test it, try yourself and see if it works :)
I am a beginner in Django. I am building a Django app, named PhoneReview. It will store reviews related to the latest mobile phone. It will also display phone brands, along with the associated phone models. I have managed to do some protion of the app. Right now, I am a bit confused with a line of code.
I have a code like this in one of my template files:
{% extends 'gamereview/base.html' %}
{% block title%}
Detail
{% endblock %}
{% block content %}
<h3>This is the review for {{game.title}} </h3>
<ul>{% for review_item in game.review_set.all %}
<li>{{ review_item.review }}</li>
{% endfor %}
</ul>
{% endblock %}
I don't understand this portion:
<ul>{% for review_item in game.review_set.all %}
What does this line mean?
Here are the codes in models.py:
from django.db import models
from django.template.defaultfilters import slugify
# Create your models here.
class Tag(models.Model):
label = models.CharField(max_length=20)
def __str__(self):
return self.label
class Game(models.Model):
title = models.CharField(max_length=100)
developer = models.CharField(max_length=100)
platform = models.CharField(max_length=50, default='null')
label_tag = models.ManyToManyField(Tag)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
class Review(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
review = models.CharField(max_length=1000)
date = models.DateField(auto_now=True)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.review
def save(self):
super(Review, self).save()
self.slug = '%i-%s' % (
self.id, slugify(self.game.title)
)
super(Review, self).save()
Here are the codes in views.py:
from django.views import generic
from .models import Game
class GameListView(generic.ListView):
template_name = 'gamereview/gamelist.html'
context_object_name = 'all_games'
def get_queryset(self):
return Game.objects.all()
class ReviewView(generic.DetailView):
model = Game
template_name = 'gamereview/review.html'
Here are the codes in urls.py:
from . import views
from django.urls import path
app_name = 'gamereview'
urlpatterns = [
path('gamereview/', views.GameListView.as_view(), name='gamelist'),
path('gamereview/<slug:slug>/', views.ReviewView.as_view(), name='review'),
]
I am a bit confused by this line: <ul>{% for review_item in game.review_set.all %}. Would you please help me to clarify?
Look at the models; there is a Game class. Apparently you receive an instance of that class in your template under the name game.
The Game class is referenced as a foreign key by Review class. This, due to the way Django ORM works, gives Game class a reverse link .review_set; it contains all review objects that refer to the particular game. Hence game.review_set.
That .review_set attribute is not a list but a DB result set. You can filter it, sort it, etc, but in your case you just fetch all the records from it. Hence game.review_set.all.
Please take some time to read an intro to how Django works, a number of things there cannot be derived from mere common sense.
I'd like to add something that was helpful for me when referencing the related database result set:
In your class, you can specify a "related_name" for the foreign key and use it to reference that result set in your template.
For instance,
class Review(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name="game_reviews")
And, now you reference this in your template (assuming the context name is all_games):
{% for x in all_games %}
{% all_games.title %} </br>
<ul>{% for review_item in game.game_reviews.all %}
<li>{{ review_item.review }}</li>
{% endfor %}
</ul>
{% endfor %}
Of course, you can further simplify the related_name to just "reviews" and shorten your code that much more. And, it's all very logical when you read it.
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')
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
This may not be relevant but just wanted to ask,
IF an object is passed from views to template and in the template will i be able to query many to many fields
Models code:
class Info(models.Model):
xls_answer = models.TextField(null=True,blank=True)
class Upload(models.Model):
access = models.IntegerField()
info = models.ManyToManyField(Info)
time = models.CharField(max_length=8, null=True,blank=True)
error_flag = models.IntegerField()
def __unicode__(self):
return self.access
Views:
// obj_Arr contains all the objects of upload
for objs in obj_Arr:
logging.debug(objs.access)
logging.debug(objs.time)
return render_to_response('upload/new_index.html', {'obj_arr': obj_Arr , 'load_flag' : 2})
In template is it possible to decode the many to many field since we are passing the object
Thanks..
In general, you can follow anything that's an attribute or a method call with no arguments through pathing in the django template system.
For the view code above, something like
{% for objs in obj_arr %}
{% for answer in objs.answers.all %}
{{ answer.someattribute }}
{% endfor %}
{% endfor %}
should do what you're expecting.
(I couldn't quite make out the specifics from your code sample, but hopefully this will illuminate what you can get into through the templates)
It's also possible to register a filter like this:
models.py
class Profile(models.Model):
options=models.ManyToManyField('Option', editable=False)
extra_tags.py
#register.filter
def does_profile_have_option(profile, option_id):
"""Returns non zero value if a profile has the option.
Usage::
{% if user.profile|does_profile_have_option:option.id %}
...
{% endif %}
"""
return profile.options.filter(id=option_id).count()
More info on filters can be found here https://docs.djangoproject.com/en/dev/howto/custom-template-tags/