How can I display a Django model in a Django template? - django

Trying to figure out Django... I'd like to build a table in a template that is largely all the data from a Django model. My preference would be to send the model queryset PersonDynamic.objects.all() to the template and have it figure out the column names and then loop through each row, adding the fields for that row. This kind of approach would give me a lot of flexibility because if the model changes the template will automatically change with it.
One approach could be to just move the column headers and each record into lists which I then send to the template but this seems less efficient than just sending the queryset.
What is the best way to do this?
Am I right about efficiency?
Does sending lists rather than model instances come with a larger risk of problems if there are a bazillion records or does that risk exist, regardless of approach?
Thanks!

Try looking at the code for django.contrib.admin, and how the built-in admin site makes tables. The admin site adds a lot of extra features to change how fields are displayed, and what fields to display, but it should give you some idea of how things work.
The absolute simplest way I can think to do what you want is to send the template a list of headers you want to use, and the queryset called with the values_list method. values_list takes a series of field names, and returns another queryset that will yield a tuple of field values instead of a model object. For example, say your PersonDynamic looks like this:
class PersonDynamic(Model):
id = AutoField()
name = CharField()
likes_python = BooleanField()
You could then send something like this to your template:
fields = ['id', 'name', 'likes_python']
qs = PersonDynamic.objects.values_list(*fields)
context = Context({'headers': fields, 'rows': qs})
template.render(context)
And your template could do:
<table>
<tr>{% for item in header %}<th>{{ item }}</th>{% endfor %}</tr>
{% for row in rows %}
<tr>{% for cell in row %}<td>{{ cell }}</td>{% endfor %}</tr>
{% endfor %}
</table>
For your other questions, this will run into problems for larger tables; it takes time and memory to loop over large lists like this, so generally a strategy like pagination is used, where only X items are displayed per page.

Related

Is there a way to hide an object in a queryset in Django models?

Like you see the codes below, I'm manually using an attribute status in Store model to show my stores in HTML Templates. The problem is the more codes I make, the more repeating codes happen.
I'm trying to find a way to avoid those inefficient repetitions. Is it possible to set up in models.py to show only active stores to be shown in HTML templates?
I'm asking this because I've already done a similar thing to this. I have a string type attribute, but I need it in a list format in my templates, so I made a function to call it only with a list format. But, I can't figure out how to hide inactive stores in the same way.
Can anyone give me some suggestins for this?
models.py
class Store(models.Model):
status = models.CharField(max_length=20,
choices=(
('active', 'Active'), # ('Stored value', 'Label shown on the UI')
('inactive', 'Inactive'),
),
default='inactive')
...
HTML Templates
{% if store.status == 'active' %}
... Show store
{% else %}
... Do not show store
{% endif %}
Usually, though, People need to select only a subset of the complete set of objects. To refine the initial QuerySet, Here are the two most common ways to proceed:
Use filter() or exclude() with your queryset before sending it to the template.
filter(kwargs**)
Returns a new QuerySet containing objects that match the given lookup parameters.
active_stores = Store.objects.filter(status='active')
# send active_stores to template
or
exclude(kwargs**)
Returns a new QuerySet containing objects that do not match the given lookup parameters.
active_stores = Store.objects.exclude(status='inactive')
# send active_stores to template
In your template, you can loop through without a problem of inactive stores
{% for store in active_stores %}
{{ store }}
{% empty %}
No stores
{% endfor %}
See further explanations in the Django Documentation
Do not filter in the template language, filter in Python. From within the view function and/or class, make sure to filter the queryset:
objs = stores_queryset.filter(status='active')
Then work with the filtered objs in the template, and just iterate over objs without a conditional. It's best to keep logic out of templates entirely, always passing in the correctly prepared context data.

Get distinct objects for foreign key in existing Django query

Consider the following many-to-one relationship. Users can own many widgets:
class Widget(models.Model):
owner = models.ForeignKey('User')
class User(models.Model):
pass
I have a fairly complicated query that returns me a queryset of Widgets. From that I want to list the distinct values of User.
I know I could loop my Widgets and pull out users with .values('owner') or {% regroup ... %} but the dataset is huge and I'd like to do this at the database so it actually returns Users not Widgets first time around.
My best idea to date is pulling out a .values_list('owner', flat=True) and then doing a User.objects.filter(pk__in=...) using that. That pulls it back to two queries but that still seems like more than it should need to be.
Any ideas?
Use backward relationship:
User.objects.distinct().filter(widget__in=your_widget_queryset)

Django gets unnecessary fields executing QuerySet

This QuerySet (Let's say Model model has 12 field):
objects = Model.objects.filter(...)
And this template:
{% for object in object %}
<a href='{{ object.get_absolut_url }}'>Foo: {{ object.bar }}</a>
{% endfor %}
perform SQL query which gets unnecessary fields (every 12 fields + relations). I want Django to get only 'bar' field. How can I do this?
By the way I know about values() method, but as it returns dict, I can't call Model methods such as get_absolute_url().
You want to use only():
objects = Model.objects.select_related().only("bar").filter(...)
Keep in mind however if you limit too much of the data down and then use the objects in other ways you can actually cause the ORM to execute extra queries, so make sure to be using something like django-debug-toolbar to ensure you aren't removing these unnecessary fields to only incur the hit of lots of unnecessary queries which is a worse situation.
FYI you can also use defer() to list the fields you don't want loaded if you want to think about it in the other direction.

django-taggit: Is there a way to produce less db queries?

Say I have a model:
class Entry(models.Model):
...
tags = TaggableManager()
When I iterate over Entry.objects.all() in a template, entry.tags.all produces one more query to the database. Is it possible to reduce queries number? Using something like select_related() (I know it won't work, since django-taggit uses manytomany relation, but I am sure there should be a way to select all entries with related tags in 1 hit)?
From Django 1.4 onward, you can use prefetch_related to retrieve one-to-many relations on a queryset in a single query. Unfortunately this doesn't work brilliantly with django-taggit, because the 'tags' property is a manager rather than a true relation, and so prefetch_related can't make sense of it. Instead, you need to follow the tagged_items relation:
entries = Entry.objects.prefetch_related('tagged_items__tag')
You then need to go through some similar contortions in the template code to access the prefetched tags, because entry.tags.all will run another query rather than making use of the prefetch:
{% for tagged_item in entry.tagged_items %}
<li>{{ tagged_item.tag.name }}</li>
{% endfor %}
Try using Select Reverse it's designed to grab the entirity of a many2many relationship with a single query.

Using Django Forms to display and edit?

I'm wrestling with how to best create HTML pages in Django that can either be used for displaying or editing data. That is, I'd like the field's values to appear as text in display mode, but in their widgets when in edit/add mode. It appears that Django wasn't designed to do this: the fields always appear in their widgets (eg, text input, text area,
etc).
Is there a common technique for handling this, short of using forms for one, and not the other?
I was thinking of a custom templatetag filter that could be used for every form field, like:
{{ form.field_name|render_field:mode }}
where render_field would either return the field's HTML widget, or just the value as text, based on the mode.
Have I missed something, or is this a viable solution?
Answering my own question, apparently. I ended up with a three-part solution:
attach the model to the form, so I'll have the default display widgets for each field
write the form using a template tag, passing it the form.field, user, and action
write a render template tag to handle #2
Step one:
form.model = Model(...)
Step two:
{{form.field1.label}}
{% render form.field1 user action %}
{{form.field2.label}}
{% render form.field2 user action %}
Step three:
Something like:
def render(formfield, user, action, default_text="Private"):
if not user.is_authenticated():
action = "view"
if action == "view":
if user.is_authenticated():
fieldname = formfield.name
retval = str(getattr(formfield.form.model, fieldname))
else:
retval = default_text
else:
retval = formfield.as_widget()
return retval
Since you are saving the data, you must have a model attached to the form somehow, a modelform or not. So you can just use that model directly to get the values and render it in a template like you want.
The above suggestion would be possible, but since forms can be rather complex, it's probably not an easy task or worth the bother. Depends on how often you want to do this. But then it would probably be easier to create a filter for the model instead of the form.
Have I missed something
Forms not only display the field widgets but also handle the post data. A post sent would cause it to process the data cleanup, form and field error handling, etc.
It's kind of breaking the pattern - why create and render a form object only to tweak it not to look like a form?
If you are worried about too much work on templates, try to solve it with template inheritance in a best possible way. If you are very sure you want to change only the field tag, you can make sth like
{% if form %}
{% for error in form.field.errors %}
{{ error|escape }}
{% endfor %}
{{ form.field }}
{% else %}
{{ object.field }}
{% endif %}
for every field, but IMO that's not the point, YMMV.
Also what comes to mind (thinking of your solution) is dynamically attach widgets to form fields, but that'd be overengineering.
I have the same problem. Right now I have separate templates for display and editing; the former renders object fields, the latter form fields (but sometimes also object fields, for things which are not editable). The HTML structure can be quite complex: for example, on some pages I have large tables representing a multi-level hierarchy of objects. As a result, I end up with a large amount of duplicated code in the two templates, which is the opposite of DRY.
I have used template filters with form fields before, to save code when displaying errors together with the field. I think it would be a viable solution in this case. Another possibility may be to use a ModelForm subclass which can be told to render uneditable versions of the fields. This would help keep the templates simple. You could even render both the static and the editable version of a field, and use JavaScript to switch between them, activating and deactivating an editing mode.