I have the following model:
class JobView(models.Model):
job = models.ForeignKey(Job)
user = models.ForeignKey('auth.User')
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created']
And in a view i'm grabbing the job info:
model = Job.objects.get(pk=id)
In a template I'm doing the following:
{% regroup model.jobview_set.all by user as user_list %}
{% for u in user_list %}
{{ u.grouper.email }}
{% endfor %}
The problem I'm having is that is not really grouping by the user, or more to the point, randomly grouping them by the user.. Sometimes it display the user twice, sometimes 3 times..Is there something I'm missing?
Cheers!
This is because {% regroup %} does not order its inputs. You're trying to group by user, but model.jobview_set.all is not ordered by user, so you get random groups depending on how user is ordered.
The easiest solution is sort model.jobview_set.all before passing it to the template.
Taken directly from django doc's:
Note that {% regroup %} does not order its input!
https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#regroup
Related
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.
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 model that stores teams and members. I want to check if a requested user is in this team or not. How do i do that?
Model
class TeamPlayer(models.Model):
team = models.ForeignKey(Team, related_name='players', on_delete=models.CASCADE)
player = models.OneToOneField(User, related_name='player', on_delete=models.CASCADE)
approved = models.BooleanField(default=False)
Template
{% if request.user in object.players.all %}
Leave
{% else %}
Join
{% endif %}
View is just simple DetailView.
If you want to do that in the template, just use the teamplayer_set like this:
{% if request.user in object.teamplayer_set.all %}
You have it 1-on-1, so it should be
players = TeamPlayer.object.all()
if request.user.player in players
# Do what you need to
or in template
{% if request.user.player in players %}
provided players is sent to the template
When it's one-on-one, it's always user.player to get related player and player.user for the related user.
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 %}.
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/