Django,getting only unique values from related object - python

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.

Related

Use is_authenticated using OneToOneField relation with User

I have built a model which have a OneToOne relation with the User object in Django like this :
class Student(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
But in the HTML file, the filter {% if user.student.is_authenticated %} does not work but the filter {% if user.is_authenticated %} works. I thought that the Student class inherits the attributes from the User class.
Is there an other possibility to create custom users from the User class with the possibility to use {% if user.student.is_authenticated %} ? I want also to have the possibility to use for example {% if user.teacher.is_authenticated %}.
I thought that the Student class inherits the attributes from the User class.
No, it does not inherit, these are just two models (tables) where one of the tables refers to the others by specifying the primary key.
You thus check this with:
{% if user.is_authenticated %}
…
{% endif %}
or if you want to know whether the user of a student is authenticated, you can work with:
{% if mystudent.user.is_authenticated %}
…
{% endif %}

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

Getting and displaying related objects in Django

I know this is simple, but I can't get my head around how to join some models together to display in my template in Django. I have "groups" that can have several "contacts".
So far I've got:
class Group(models.Model):
group_name = models.CharField()
class Contact(models.Model):
contact_name = models.ForeignKey(Group)
In my view, at first I assumed that simply getting my groups would also get any attached contacts, however that doesn't appear to be happening as expected:
def get_queryset(self):
groups = Group.objects.all()
return groups
I was expecting to do something like this in my template:
{% for group in groups %}
<h2>{{ group.group_name }}</h2>
{% for c in group.contact %}
<h3>{{ c.contact_name }}</h3>
{% endfor %}
{% endfor %}
This isn't working - what am I doing wrong? What is the correct query in my view to make sure the contact(s) for each group is getting retrieved?
Well, it looks like you've got some of your code from a different place so just so you can fully understand, you can do this in 2 different ways:
1) To access a related object of any kind, being a simple ForeignKey or ManyToMany you just need to go from the opposite model and use _set like this example:
class Group(models.Model):
group_name = models.CharField()
class Contact(models.Model):
contact_name = models.ForeignKey(Group)
{{ group.contact_set.all }}
2) You can set up a name different than the default _set changing Contact like this:
class Contact(models.Model):
contact_name = models.ForeignKey(Group, related_name='contacts')
So, related_name kwarg set a new name for you instead of the _set one:
{{ group.contacts.all }}
I hope I manage to make it clearer about simple access on models related objects.

Querying Many to many fields in django template

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/

Categories

Resources