Querying Many to many fields in django template - python

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/

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.

Looping through two models that contain lists and are connected Django

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

Django,getting only unique values from related object

I wish I could render in my website list of most used tags,which I do.The problem is,displayed tags often repeat and I'm sure it would be much better if only unique tags were displayed.
In my model I have writen method unique_tags
class ArticleTag(models.Model):
article = models.ForeignKey('ep.Article', on_delete=models.CASCADE, related_name='tags')
tag = models.ForeignKey('ep.Tag', on_delete=models.CASCADE)
def unique_tags(self):
return self.objects.values_list('tag').distinct()
and when I tested it in python shell it works fine.But it doesnt render any tag.My template looks like this:
<div class="widget-tags">
<ul>
{% for prev_article in object.articles.all %}
{% for article_tag in prev_article.article.tags.all.unique_tags %}
<li>{{article_tag.tag.verbose_name}}</li>
{% endfor %}
{% endfor %}
</ul>
</div>
Where object is from model that makes relation with Article table and Field table,so with object.articles.all I've got all instances of articles that are related to specific field.I use detail view in my views.
So,my first question is,Is this valid approach? I mean,adding new method in model class, or perhaps I should add this in views?.Also I still not comfortable with django template language so maybe there is problem.And I know there is this filter in template like {{ some_object | function}} but I've read that it is good practice to keep as little logic in template as it is possible.
I would add logic for this on the Article level if you want to do this:
class Article(models.Model):
def unique_tags(self):
return Tag.objects.filter(articletag__article=self).distinct()
and then query this with:
{% for article_tag in prev_article.unique_tags %}
<li>{{article_tag.tag.verbose_name}}</li>
{% endfor %}
By using a .values_list, you will obtain the value of the primary key, so usually an int (or another primitive type given you defined a primary key yourself).
That being said, I think it is better to simply make sure that this can never happen, by adding a unique_together [Django-doc] constraint in the model:
class ArticleTag(models.Model):
article = models.ForeignKey(
'ep.Article',
on_delete=models.CASCADE,
related_name='tags'
)
tag = models.ForeignKey('ep.Tag', on_delete=models.CASCADE)
class Meta:
unique_together = ('article', 'tag')
So now you can simply not tag the same article with the same tag twice. So once this is enforced, you can simply use:
<!-- given unique_together is enforced -->
{% for article_tag in prev_article.tags.all %}
<li>{{article_tag.tag.verbose_name}}</li>
{% endfor %}
and you can then use .prefetch_related(..) to load all the related objects with a constant number of queries.

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

Django foreignkey relationships in templates

I've seen some examples of how to do this on SO but none of them have guided me towards the glory I so desire.
Here are the fields I'm working with:
models.py
class ServerFunctions(models.Model):
server_function = models.CharField(max_length=12)
class Meta:
verbose_name_plural = "Server Function"
def __unicode__(self):
return self.server_function
class Inventory(models.Model):
server_function = models.ForeignKey(ServerFunctions, null=False, blank=False)
views.py
def show_details(request, host_id=1):
host_info = Inventory.objects.filter(id=host_id).values()
return render_to_response('templates/details.html', {'host_info': host_info})
templates/details.html
This gives me the column value from the Inventory table (3) like it should
{{ info.server_function_id }}
This gives me no output at all.
{% for func in info.serverfunctions_set.all %}
{{ func.server_function }}
{% endfor %}
I'm stuck, nothing I've tried seems to work. Thanks for reading.
You are using info.serverfunction_set.all in your template which would be correct if you were traversing a reverse relationship (i.e. you had a ServerFunction and wanted all the related Inventory items). But you aren't, you have a simple forward relationship between Inventory and a ServerFunction:
{% for info in host_info %}
{{ func.server_function.server_function }}
{% endfor %}

Categories

Resources