Django - ListView - populate table dynamically - django

I am trying to create a generic table (that I can reuse for other Objects as well) by passing in the views the context "table"
As part of view.py I have the following information passed on as Context:
object_list = User.objects.all()
table.columns that contains the Column Accessor (the property of the User object I want to access) and the Header Name
e.g. {'last_name': 'Last Name', 'first_name': 'First Name'}
In my template I want to loop through the objects, and find the "Column Accessor" and get the property of the object.
So far I have the following, but I am not sure how I can access the property of the object from the template. I can imagine I could create a filter tag, but any help on how to accomplish that would be appreciated.
#template.py
{% for object in object_list %}
{% for column in table.columns %}
<td>{{object.????}}</td>
{% endfor %}
{% endfor %}

Related

How can I limit what m2m items are displayed in django template-side?

I have a queryset in a view limiting on an m2m field. The query appears to work correctly and gets the right parent objects, but when I loop through the parent.object_set.all in the template I see m2m objects that were technically not included in the initial query (or shouldn't be anyway), but now included because they are part of parent's child m2m object_set.
To try put this in a more generic example...I'm completely making up parents and children:
class Parent(models.Model):
name = models.CharField(max_length=42)
children = models.ManyToManyField(Child)
class Child(models.Model):
...
# no, I'm not serious on this as a data structure, but for the example....
male = models.BooleanField()
female = models.BooleanField()
class ParentListView(ListView):
...
def get_queryset(self):
...
parents = Parent.objects.filter(children__male = True)
In the template.
{% for parent in parents %}
{{ parent.name }}
{% for child in parent.child_set.all %}
{% comment %}
oops, I got all the children of this parent....I only want those that were male from the original queryset. I need the child_set to only be those that met the criteria in that first queryset. How do I accomplish this?
{% endcomment %}
{% endfor %}
{% endfor %}
You can't provide filter arguments with the Django template language.
In Django 1.7+, you can use prefetch_related with a custom Prefetch object when defining the queryset in the view.
def get_queryset(self):
...
parents = Parent.objects.filter(
children__male=True
).prefetch_related(
Prefetch('children', queryset=Child.objects.filter(male=True), to_attr='male_children')
)
Note that in the example above, you probably need to use distinct() to prevent duplicated parents for each male child.
We have used the to_attr argument as recommended in the docs. In the template, we then loop through the male children.
{% for parent in parents %}
{{ parent.name }}
{% for child in parent.male_children %}
{{ child }}
{% endfor %}
{% endfor %}

Filtering related objects for each item in a queryset

I have a model (we'll call it 'item') that can be tagged by users. Tags are specific to each user. So, for example, an item can have multiple tags by multiple users. I need to display a list of all items to a user. Within that list I'd like to be able to only show the tags for each item that the currently logged in user owns. I'm hoping that there is a fairly easy way of achieving this, but I've been unable to find anything helpful in the docs. Thanks in advance.
class Item
tags = ManyToManyField('tags.Tag')
class Tag
user = ForeignKey('auth.User')
So I collect a queryset of items to display on a page, and list through them in the template. I'd like to be able to only show the tags owned by the currently logged in user for each item in the queryset.
{% for item in items %}
{% for tag in item.tags %}
DISPLAY TAGS OWNED BY LOGGED IN USER
{% endfor %}
{% endfor %}
This is what I'd like to achieve ^
One approach is to add a property to your Item class, user_tags which you can set in your view and then filter the tags to only those who match the logged in user.
# models.py
class Item(models.Model):
...
user_tags = []
...
# views.py
items = Item.objects.all().select_related('tags')
for item in items:
item.user_tags = [tag for tag in item.tags if tag.user = logged_in_user]
Then in your template you can iterate over them:
{% for item in items %}
{{ item }}
{% for tag in item.user_tags %}
{{ tag }}{% ifnot forloop.last %}, {% endif %}
{% endfor %}
{% endfor %}
Caveat: I'm not the best at SQL anymore, so it's entirely possible that there's a way to pass an extra WHERE parameter to select_related to only join tags for that user id. Unfortunately I'm out of time this morning, but I will check back in on you.

how to extract model object from form in template using django

I have a formset. I want to extract out the id of the model object that each form in the set represents.
This is because I plan to use a template tag to pass this id to a function and return a related object.
How can I do this?
{% for each in model_formset %}
{{each.instance.id}}
{% endfor %}

Iterate through model fields in jinja template

I have several models in my application, and as I will have some views doing the same thing (form + tables showing existing records in my model), but implemented dfferently because of the model, which will be different in each case, I was wondering if it was possible to make it generic.
I googled a bit and was not able to find anything relevant to my case.
What I would like to achieve:
In my view, I want to go through each object from the model that I passed to the template, for example:
return render_template('addstatus.html', form=form, statuses=Status.query.all(),
target_model="Status", fields=Status.__mapper__.c.keys())
But I want to have only one view, whatever the model will be, so I am not able to know in advance the fields of my model, the number of columns and so on.
I want to have something like that in my view:
{% for obj in objects %}
{% for field in obj.fields %} (not existing)
<h1> {{ field }} :: {{ obj.fields.field.value }}
{% endfor %}
{% endfor %}
Is it something possible? How can I achieve that?
You can add this method to your db.Model class (either by subclassing or by monkeypatching):
from sqlalchemy.orm import class_mapper, ColumnProperty
def columns(self):
"""Return the actual columns of a SQLAlchemy-mapped object"""
return [prop.key for prop in class_mapper(self.__class__).iterate_properties
if isinstance(prop, ColumnProperty)]
Then you can use {% for col in obj.columns() %} to iterate over the column names.
I had a similar issue, and wanted something purely Jinja2 based. This will return all of the keys/fields in your "obj" table, and worked well for me:
obj.__table__.columns._data.keys()
Here it is implemented in your sample code
{% for obj in objects %}
{% for field in obj.__table__.columns._data.keys() %}
<h1> {{ field }} :: {{ obj[field] }} </h1>
{% endfor %}
{% endfor %}

Determining whether a user is associated with an object through another class in a django template

I have a user model, an item model, and a possession model to store data about a user possessing an item. When a user is logged in and viewing an item, I want to display an 'add to my items' button, unless the user already has the item.
I was trying this code in the template:
{% if not user.possession_set.filter(item=item.id) %}
<input type='submit' value='add to my items' />
{% endif %}
where
item is the foreign key name for the item object in my possession model and
item.id is the primary key for the item being displayed to the user
but I get this error:
Could not parse the remainder: '(item=item.id)'
I'm thinking I can't use the .filter() function since that is for querying the database? I found django's template filters, like this one:
http://docs.djangoproject.com/en/1.1/ref/templates/builtins/#get-digit
but there aren't any that can be combined to filter for a certain value of item. It seems I would have all the information in the template to do this, since I'm getting the user and it's possession_set, which should have the item field for each possession, so I'm thinking it's just a syntax thing?
Thanks for the help!
You could write a custom template filter for this.
def owns(user, id):
return not user.possession_set.filter(item=id)
Then, in your template:
{% load mystuff %}
{% if user|owns:item.id %}
Check the Django docs at http://docs.djangoproject.com/en/1.1/howto/custom-template-tags/ for more info. Not sure if passing item.id as the filter argument will work, though.
You can't use such complicated expressions in the template, only in the view. Compute the information, whether user can have this button in the view and pass a single True or False value to the template. For example:
in view
allow_addition = not user.possession_set.filter(item=item.id)
and in template:
{% if allow_addition %}
<input type='submit' value='add to my items' />
{% endif %}