Use is_authenticated using OneToOneField relation with User - python

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

Related

Display different content based on Boolean

I want to allow only manager to create view a content on my website, So I added an entry to my model Profile
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
manager = models.BooleanField(default=False)
By default is false
If false the user cannot see the content.
So I tried :
{% if profile == manager %}
SHOW THE CONTENT
{% else %}
Does not show the content
{% endif %}
But nothing change.
What did I do wrong ?
You need to do it like that :
{% if user.profile.manager %}
SHOW THE CONTENT
{%else%}
Does not show the content
{% endif %}
You forgot to add user since it is connected
You added a property on your model.
Your Profile object won't == manager. You need to access the property on your Profile object.
Try
{% if profile.manager %}
This will check is that profile object's manager property is True
If you have not overridden your view context you need to access your fields with object.
{% if object.manager %}
SHOW THE CONTENT
{%else%}
Does not show the content
{% endif %}

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: generic ListView, only show when in other model via ForeignKey

I have two models: Creator and Piece. Per default every user is a Creator. But only if the user uploads a Piece he actually created something. Hence I would like to show in the generic Creator ListView only those Creators that uploaded a Piece. How can I filter the generic ListView to only show Creators that are present in the model Piece via a ForeignKey?
models.py
class Creator(models.Model):
...
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
class Piece(models.Model):
...
creator = models.ForeignKey('Creator', on_delete=models.SET_NULL, null=True)
...
views.py
class CreatorListView(generic.ListView):
model = Creator
paginate_by = 10
creator_list.html
{% block content %}
<h1>Creators</h1>
{% if creator_list %}
<ul>
{% for creator in creator_list %}
<li>
{{ creator }}
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}
You can define the queryset such that it filters out Creators with no related Piece:
class CreatorListView(generic.ListView):
model = Creator
queryset = Creator.objects.filter(piece__isnull=False).distinct()
paginate_by = 10
Django will create a query that looks like:
SELECT DISTINCT creator.*
FROM creator
LEFT OUTER JOIN piece ON piece.creator_id = creator.id
WHERE piece.id IS NOT NULL
(It is possible that the Django ORM optimizes this to a JOIN instead of a LEFT OUTER JOIN).
So the JOIN will ensure that there is at least one related Piece object.

How to order by a field from the `through` table for a M2M relationship in Django?

I have two models — Note and Pinboard — with a many to many relationship in a Django app. Those two models are related trough another model — Pin — so I can store additional information about the relationship.
I want to show the related Note instances in the DetailView for Pinboard. That's not the problem. But I want to prefetch the notes and also order them on the position field from the through table.
Any hints on how to archive this (prefetch + ordering on third table)?
Example
This is what I have so far… it works in the sense, that I don't have to query for each entry, but I found no way to order the Note instances by their position without more queries for each instance.
Models
from django.db import models
class Note(models.Model):
title = models.CharField(max_lenght=200)
content = models.TextField()
class Pinboard(models.Model):
title = models.CharField(max_lenght=200)
notes = models.ManyToManyField(
Note, blank=True, related_name='pinboards', through='Pin'
)
class Pin(models.Model):
class Meta:
ordering = ['position', ]
get_latest_by = 'position'
pinboard = models.ForeignKey(Pinboard, related_name='note_pins')
note = models.ForeignKey(Note, related_name='pinboard_pins')
position = models.PositiveSmallIntegerField(default=0)
View
from django.views.generic import DetailView
class PinboardDetailView(DetailView):
model = Pinboard
queryset = Pinboard.objects.prefetch_related('notes')
Template
{% extends 'base.html' %}
{% block content %}
<h1>{{ pinboard.title }}</h1>
{% if pinboard.notes.all.exists %}
<ol>
{% for note in pinboard.notes %}
<li>{{ note.title }}</li>
{% endfor %}
</ol>
{% else %}
<p>Nothing here yet…</p>
{% endif %}
{% endblock content %}
I suggest you use a Prefetch object.
class PinboardDetailView(DetailView):
model = Pinboard
queryset = Pinboard.objects.prefetch_related(
Prefetch(
'notes',
Note.objects.order_by('pinboard_pins__position'),
)
)
By the way, you don't need to use prefetch_related at all on a DetailView as it will result in the same number of queries.
Plus, since you're already fetching the pinboard.notes I suggest you use {% if pinboard.notes.all %} instead of {% if pinboard.notes.all.exists %}.

Backward relationship in Django templates

I am trying to display data from different models in my template and I have no idea why it doesn't work properly.
Models.py:
class Company(models.Model):
name = models.CharField(max_length=100)
class Location(models.Model):
company = models.ForeignKey('Company',
related_name='locations')
Views.py:
def sth(request):
loc_list = Location.objects.all()
return render(request, 'blabla/index.html', {'loc_list': loc_list})
Template:
{% for loc in loc_list %}
{% for entry in loc.company_set.all %}
{{entry.name}}
{% endfor %}
{% endfor %}
Why the name of the company is not showing?
You foreign key relationship is the wrong way around... the way you have currently defined them, a Location can have only one Company associated with it, but in your template you are trying to fetch a list of Companys.
For your template logic to work, your models would have to be defined as follows:
class Company(models.Model):
name = models.CharField(max_length=100)
location = models.ForeignKey('Location')
class Location(models.Model):
# You probably need some other fields in here.
Then this would work:
{% for loc in loc_list %}
{% for entry in loc.company_set.all %}
{{entry.name}}
{% endfor %}
{% endfor %}
Only use "_set" when accessing backwards relationships. There is no
loc.company_set.all
because each location only has one company (that is what a ForeignKey is. You can access the location's company by doing loc.company). If you want location to have multiple companies, either see solarissmoke's answer (where you put the ForeignKey attribute on Company and not on Location), or use a ManyToManyField relationship (this will allow companies to have multiple locations and each location to have multiple companies).
Then you can access all the companies of a given location by doing this in the template:
{% for loc in loc_list %}
{% for entry in loc.company.all %}
{{entry.name}}
{% endfor %}
{% endfor %}
You can also access all the locations for a given company by using the related_name (company.locations).
Short answer:
{% for loc in loc_list %}
{{loc.company.name}}
{% endfor %}

Categories

Resources