Iterate through model fields in jinja template - flask

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

Related

Django querysets. How to prefetch only unique?

Models:
class House(Model)
class Flat(Model):
house = ForeignKey(House, related_name="houses")
owner = ForeignKey(User)
class User(Model)
queryset:
queryset = User.objects.prefetch_related(
Prefetch("flats", queryset=Flat.objects.select_related("houses"))
And then flats:
{% for flat in user.flats.all %}
<p>№ {{ flat.number }}, {{ flat.house.name }}</p>
{% endfor %}
It's fine. But for houses I need only unique ones
{% for flat in user.flats.all %}
<p>House {{ flat.house.name }}</p>
{% endfor %}
But this template gives me ALL houses, with duplicates.
How can I avoid duplicates, any ideas? I tried .distinct() but it's dosen't work, looks like I using distinct() wrong or etc.
It seems you may end up with duplicate houses if a user is the owner of multiple flats, which are all in the same house. To get all houses that the user is related to via Flats, you can use a different query, starting with House so that you can use distinct:
distinct_houses = House.objects.filter(flats__owner=user).distinct()
The above returns one House object for each flat where the user is an owner, and makes sure you don't have duplicates. (Note that the above assumes the related_name is changed to flats for house = ForeignKey(House, related_name="flats"), since "houses" as the related_name seems like a typo to relate back to the Flat model.)
Alternatively, you could do something in-memory in Python, if you still also need to know all Flats via your original queryset = User.objects.prefetch_related(...) and don't want an additional query. Like:
distinct_houses = {flat.house for flat in user.flats.all()}
Side note: it looks like you have a typo in needing to use Flat.objects.select_related('house') rather than Flat.objects.select_related('houses') based on the Foreign Key field name being house.
Only found solution with template filters.
Queryset:
queryset = User.objects.filter(status="ACTIVE").prefetch_related(
Prefetch("flats", queryset=Flat.objects.select_related("house"))
)
With this I can get all flats for each user:
{% for user in object_list %}
{% for flat in user.flats.all %}
{{ flat.number }}, {{ flat.house.name }}
{% endfor %}
{% endfor %}
And I am using template filter to get unique houses.
{% for user in object_list %}
{% for flat in user.flats.all|get_unique_houses %}
{{ flat.house.name }}
{% endfor %}
{% endfor %}
# extra_tags.py
#register.filter(name="get_unique_houses")
def get_unique_houses(value):
return value.distinct('house')

Django: get foreign key´s value instead of id

I´m trying to use foreign key values in a template.
When I try to show it, I get the value on screen correctly.
{% for x in dataset %}
<p> {{ x.fieldname }}
{% endfor %}
But when I try to use it for comparisons it uses the id, not the value.
{% for x in dataset %}
{% if x.fieldname == "The name I want" %}
<p> {{ x.fieldname }}
{% endif %}
{% endfor %}
I´ve been looking some posts relateing serializers with this, but (in my little knowledge) I understand serializers are to send or receive data from outside the application.
As the is only tu use the values within the application, is there another way to get the actual value for comparisons? Should I use the serializers approach?
Thanks!
I think you assume too much "magic" here. If you fetch the attribute that is related to a ForeignKey, it simply fetches the related instance. Now in case you render such instance, Django will typically fallback on the __str__ of the model instance.
So what you need to do is find out how the __str__ of the model that is referenced works. For example if Model uses the .name attribute to convert it to a string, you can write:
{% if x.fieldname.name == "The name I want" %}
In case it is unknown how it is rendered, you can use the stringformat template filter:
{% if x.fieldname|stringformat:"" == "The name I want" %}

How can I create a custom template for a TabularInline admin Formset in Django?

In my Django app (research database), when changing a person object in the admin, I'd like all of the sources for that person to be listed as hyperlinks to the file for that source. I'm trying to do this by creating a custom template for a stacked inline. Here is the custom template so far:
<p>Testing</p>
{% for form in inline_admin_formset %}
{% for fieldset in form %}
<h5>Fieldset</h5>
{% if fieldset.name %} <h2>{{ fieldset.name }}</h2>{% endif %}
{% for line in fieldset %}
<h6>Line</h6>
{% for field in line %}
<h6>Field</h6>
{{ field.field }}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
A lot of this is just for me to see what's going on. I used the links here and here as sort of a guide. What renders from the {{ field.field }} is what you'd expect from an inline element - a dropdown menu with the source names as choices and some icons for adding/changing.
What I really want, however, is just the source name rendered as a hyperlink. How do I get the source name (the actual name of the attribute is source_name) from what I have in the Django template language (i.e. the "field" object)?
In that context, {{ field.field }} is a BoundField object, and the value method is probably what you would want to use, as is in {{ field.field.value }}.
A more Django-ish approach (and more complicated) might involve creating a custom widget (start by subclassing one of their built-ins) that only displays text, and then hook that into the form being used in the ModelAdmin for your model. I think there's a bit of a rabbit hole there, in terms of needing to subclass the BaseInlineFormset and possibly a few others down that chain... I'm seeing that the BaseFormSet class has a .form attribute referenced in its construct_form method, but things are little murky from there.
Might also be useful to check out this past thread: Override a form in Django admin

django ModelChoiceField: how to iter through the instances in the template?

I am trying to get access to the instances of a ModelChoiceField to render them the way I want in a template by displaying several fields of the instances.
class MyForm(forms.Form):
mychoices = forms.ModelChoiceField(
queryset=ObjectA.objects.all(), widget=forms.RadioSelect(), empty_label=None
)
This does not work:
{% for choice in form.mychoices %}
{{ choice.pk}}
{% endfor %}
I also tried to use the queryset but it does not render anything
{% for choice in form.mychoices.queryset %}
{{ choice.pk}}
{% endfor %}
Any idea?
Thanks
{% for choice in form.mychoices.field.queryset %}
{{ choice.pk }}
{% endfor %}
Notice the extra .field. It's a bit strange the first time you come across it, but it gives you what you want. You can also access the choices attribute of that object, instead of accessing queryset directly, but you'll need to access the first element of the choice to get the PK of the instance, like this:
{% for choice in form.mychoices.field.choices %}
{{ choice.0 }}
{% endfor %}

Django Override Admin change_form.html Template - display associated model in template

I have a situation where I've overrided the admin_change.html template for a given model. I've created a file such as:
/myproject/templates/admin/myapp/mymodel/change_form.html
Then, in the overrided change_form.html template, where I am editing an existing object instance, I want to have access to that model instance variable so I can display more information about it.
{% extends "admin/change_form.html" %}
{% block after_field_sets %}{{ block.super }}
Print my model here: {{ mymodel }}
Print foreignkey related records of my model:
{% for item in mymodel.items_set.all %} {{ item }} {% endfor %}
{% endblock %}
However, I don't know what the template variable is called that I should use to access this model (if it is even passed at all). I've tried digging through the admin source code, but get lost quickly. Does anyone know how to access this model instance variable from within an extended django template?
(NOTE: in the above code, the reference to {{ mymodel }} is incorrect. But the point is that I want to be able to use a variable like that in my template code to reference the mymodel instance.)
Any advice is much appreciated.
Thanks,
Joe
Looks like I found a means to do this using this syntax.
{% extends "admin/change_form.html" %}
{% block after_field_sets %}{{ block.super }}
Print my model here {{ original }}
Print foreignkey related records of my model:
{% for item in original.items_set.all %} {{ item }} {% endfor %}
{% endblock %}
Sorry to answer so soon, but thanks to anyone who started researching. Perhaps someone will find this helpful. If you see a better way to do this, feel free to comment.
Just poking around, it seems that {{adminform.form.instance}} works.