How do I sort by attribute in Django? - python

I am trying to sort objects passed to django, and I am passing them in like this:
{% for attendee in attendees|dictsort:"last_name" %}
{{ attendee.first_name }} {{ attendee.last_name }}
dictsort isn't working though, because they are objects and not a dictionary. How do I sort these? The goal is to have an angularjs button that you can click and it will change the sorting parameter like:
dictsort:{[{clickedTab}]}

Related

Assign multiple variables in a with statement after returning multiple values from a templatetag

Is there a way to assign multiple variables in a with statement in a django template. I'd like to assign multiple variables in a with statement after returning multiple values from a templatetag
My use case is this:
{% with a,b,c=object|get_abc %}
{{a}}
{{b}}
{{c}}
{% endwith %}
I don't think it's possible without a custom templatetag.
However if your method returns always the same length you can do it more compact like this:
{% with a=var.0 b=var.1 c=var.2 %}
...
{% endwith %}
I'm not sure that this as allowed, however from docs multiple assign is allowed.
But you can assign these 3 variables to 1 variable, which will make it tuple object, which you can easily iterate by its index.
{% with var=object|get_abc %}
{{ var.0 }}
{{ var.1 }}
{{ var.2 }}
{% endwith %}
Its not supported and its not a flaw of Django Template Language that it doesn't do that, its Philosophy as stated in the docs:
Django template system is not simply Python embedded into HTML. This
is by design: the template system is meant to express presentation,
not program logic
What you could do is prepare your data on Python side and return appropriate format which will be easy to access in template, so you could return a dictionary instead and use dotted notation with key name:
{# assuming get_abc returns a dict #}
{% with var=object|get_abc %}
{{ var.key_a }}
{{ var.key_b }}
{{ var.key_c }}
{% endwith %}

Convert user.id to user.username in Django template

I am implementing the very cool third party package Django-Simple-History into a project. Per the documentation I'm using the middleware to store which user makes changes to an object. This makes it easy to iterate over in the template like:
{% for x in object.history.all %}
{{ x.history_date }}, {{ x.history_user_id }} <br />
{% endfor %}
I am trying to use the available user.id to get the correlating user.username in the template. Any suggestions? (I'm still pretty new to Django/Python) Thanks!
history_user holds the ForeignKey for the related user.
You can use: {{ x.history_user.username }}

How to send hiddenfield in wtf flask form for database insert

How do i do specify the many database field when in a wtf form, so i can insert a row in the database correctly. I need something like this in my template
{{ wtf.form_field(gform.GHF(value="{{ project.name }}")) }}
because I'm iterating over one (Projects) to many (Goals)
Project-(has many goals)
-goal-
and my goal form shows up multiple times.
{% for project in P %}
{% for pgoal in project.goals.all() %}
<li>
Goal: {{ pgoal.goal }}<br>
{% if loop.last %}
<form class="form form-horizontal" method="post" role="gform">
{{ gform.hidden_tag() }}
{{ wtf.form_errors(gform) }}
{{ wtf.form_field(gform.goal) }}
Help here? do i need a hiddenfield to know which project?
{{ wtf.form_field(gform.submit) }}<br>
and so on...
Once I have the correct project, I will use it in my view here
u=models.Projects.query.get(correct project?)
p=models.Goals(goal=gform.goal.data,proj=u)
I wouldn't do it with a hidden field. I'd make each form submit a little differently.
You should have something like
<form class="form form-horizontal" method="post" role="gform"
action="{{ url_for('add_goal_to_project', project_id=project.id) }}">
And the route would be
#app.route('.../<int:project_id>', methods=['POST'])
def add_goal_to_project(project_id):
gform = GForm(....)
if gform.validate_on_submit():
project = models.Projects.query.get(project_id)
goal = models.Goals(gform.goal.data, proj=project)
# Do anything else you need to do, such as adding and committing
# the new object
return redirect(...)
return render_template(...)
I'm skipping the details in the form creation, redirect and render_template calls, but this should get the idea across. Each goal form's action points to a route built from the project id.
You could extend this to allow for the editing of goals, and you'd be able to make it a lot better with some nice ajax posts as well.

How to achieve list Iteration in django template [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Get list item dynamically in django templates
How to get list items with variable counter. The aim is to get the so item instead of list
not like this
{% for item in listModels %}
{{ item | safe }}
{% endfor %}
so work
{% for i in listModels|length|get_range %} // get size list
{{ listModels.i | safe }}
{% endfor %}
do not work
{% for i in listModels|length|get_range %} // get size list
{{ listModels.i | safe }}
{% endfor %}
Django templates will not allow you to do this. I'm not going to lecture you on keeping your logic out of your templates, because I think it's a stylistic choice. But understand that this is the easiest way. If you need to use the index, you can access it as a forloop property, as explained in the documentation.
If you really want variable indexing, you could make your own custom template tag to do it. But, in this case, I suggest you use a more powerful templating language, like Jinja2, instead of torturing the Django templating language.
Django Template Language provides you a way to do this...
{% for item in listModels %}
{{ forloop.counter }}
{% endfor %}
I fixed like I did the following:
{{ listModelsData|lookup:i|lookAttribute:"author" }} -// this equal listModelsData[i].author - this code in python
#register.filter
def lookAttribute (d, token):
     return getattr (d, token)

Sorting related items in a Django template

Is it possible to sort a set of related items in a DJango template?
That is: this code (with HTML tags omitted for clarity):
{% for event in eventsCollection %}
{{ event.location }}
{% for attendee in event.attendee_set.all %}
{{ attendee.first_name }} {{ attendee.last_name }}
{% endfor %}
{% endfor %}
displays almost exactly want I want. The only thing I want to change is I the list of attendees to be sorted by last name. I've tried saying something like this:
{% for event in events %}
{{ event.location }}
{% for attendee in event.attendee_set.order_by__last_name %}
{{ attendee.first_name }} {{ attendee.last_name }}
{% endfor %}
{% endfor %}
Alas, the above syntax doesn't work (it produces an empty list) and neither does any other variation I have thought of (lot's of syntax errors reported, but no joy).
I could, of course, produce some kind of array of sorted attendee lists in my view, but that is an ugly and fragile (and did I mention ugly) solution.
Needless to say, but I'll say it anyway, I have perused the on-line docs and searched Stack Overflow and the archives of django-user without finding anything helpful (ah, if only a query set were a dictionary dictsort would do the job, but it's not and it doesn't)
==============================================
Edited to add additional thoughts
after accepting Tawmas's answer.
Tawmas addressed the issue exactly as I presented it -- although the solution was not what I expected. As a result I learned a useful technique that can be used in other situations as well.
Tom's answer proposed an approach I had already mentioned in my OP and tentatively rejected as being "ugly".
The "ugly" was a gut reaction, and I wanted to clarify what was wrong with it. In doing so I realized that the reason it was an ugly approach was because I was hung up on the idea of passing a query set to the template to be rendered. If I relax that requirement, there is an un-ugly approach that should work.
I haven't tried this yet, but suppose that rather than passing the queryset, the view code iterated through the query set producing a list of Events, then decorated each Event with a query set for the corresponding attendees which WAS sorted (or filtered, or whatever) in the desired way. Something like so:
eventCollection = []
events = Event.object.[filtered and sorted to taste]
for event in events:
event.attendee_list = event.attendee_set.[filtered and sorted to taste]
eventCollection.append(event)
Now the template becomes:
{% for event in events %}
{{ event.location }}
{% for attendee in event.attendee_list %}
{{ attendee.first_name }} {{ attendee.last_name }}
{% endfor %}
{% endfor %}
The downside is the view has to "actualize" all of the events at once which could be a problem if there were large numbers of events. Of course one could add pagination, but that complicates the view considerably.
The upside is the "prepare the data to be displayed" code is in the view where it belongs letting the template focus on formatting the data provided by the view for display. This is right and proper.
So my plan is to use Tawmas' technique for large tables and the above technique for small
tables, with the definition of large and small left to the reader (grin.)
You can use template filter dictsort https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatefilter-dictsort
This should work:
{% for event in eventsCollection %}
{{ event.location }}
{% for attendee in event.attendee_set.all|dictsort:"last_name" %}
{{ attendee.first_name }} {{ attendee.last_name }}
{% endfor %}
{% endfor %}
You need to specify the ordering in the attendee model, like this. For example (assuming your model class is named Attendee):
class Attendee(models.Model):
class Meta:
ordering = ['last_name']
See the manual for further reference.
EDIT. Another solution is to add a property to your Event model, that you can access from your template:
class Event(models.Model):
# ...
#property
def sorted_attendee_set(self):
return self.attendee_set.order_by('last_name')
You could define more of these as you need them...
One solution is to make a custom templatag:
#register.filter
def order_by(queryset, args):
args = [x.strip() for x in args.split(',')]
return queryset.order_by(*args)
use like this:
{% for image in instance.folder.files|order_by:"original_filename" %}
...
{% endfor %}
regroup should be able to do what you want, but is there a reason you can't order them the way you want back in the view?

Categories

Resources