Django templates iterate model fields with indices - django

I have the follow model:
class UserProfile(...):
...
photo1 = models.URLField()
photo2 = models.URLField()
photo3 = models.URLField()
photo4 = models.URLField()
photo5 = models.URLField()
And in the Create/Update template, I have to write five copies of the following div for file1 to file5:
<div>
Photo 1:
{% if userProfileForm.instance.file1 %}
<a href="{{ userProfileForm.instance.file1 }}" target=_blank>View</a>
{% endif %}
<input type=file name=file1>
{% if userProfileForm.instance.file1 %}
Delete
{% endif %}
</div>
Is there a way to iterate field file<i>?
{% for i in '12345' %}
<div>
Photo {{ forloop.counter }}:
...
</div>
{% endfor %}

in django you have _meta API. So I think this solves your problem if you use get_fields method (and maybe you will filter desired fields out).
hope it helps.
update with example
let me show how you should solve your problem:
desired_fields = []
for field in UserProfile._meta.get_fields()
if "photo" in field.name:
desired_fields.append(field.name)
context.update(fields=desired_fields) # pass it in the context
at this point you have your desired fields which should be used with for loop in the template. And one more thing, you would need to add some template tag to get real field from string representation:
# custom template tag
def from_string_to_field(instance, field_str):
return getattr(instance, field_str, None)
and in the template code will look like this
{% for field in fields %}
{{userProfileForm.instance|from_string_to_field:"field"}}
{% endfor %}

Related

Receiving a list of related models from the list of objects (bound by a foreign key). Django

I have two very simple classes for products and photos. I would like to present products on the main page of my store along with photos related to a foreign key. But I do not know how to do this, I found many answers to get this in the view using a join like 'prefetch_related' but my ID changes for each case. So how do you present your photos for each product? are there any Django tags about which I do not know?
my models.py
class Product(models.Model)
name = models.CharField(max_length=10)
class Image(models.Model)
image = models.ImageField()
name_related = models.ForeignKay(Product, on_delate=models.CASCADE)
views.py
def home(request):
product_list = Product.objects.all()[:12]
#img = ??
context = {'product_list': product_list,
}
return render(request, 'home.html', context)
home.html
{% for product in product_list %}
{{ product.name }}
<!-- {{ product.imge }} ' element presenting the first photo for each 'product' model'???-->
{% endfor %}
Any help will be appreciated.
Since a foreign relation has been established you can iterate through the related models from the parent model.
The related models are already accessible from the parent model without doing anything explicit.
In your template you can do this:
{% for product in product_list %}
{{ product.name }}
{% for product_image in product.image.all %}
<!-- Iterate through this products related images -->
{{ product_image.image.url }}
{% endfor %}
{% endfor %}
For performance reasons, you will want to prefetch the relations, or else for every product you will do an additional query, so, in your view you will want to add this:
product_list = Product.objects.all().prefetch_related('image')[:12]
Just do
{% for product in product_list %}
{{ product.name }}
{% for image in product.image.all %}
<!-- {{ image.image.url }} -->?
{% endfor %}
{% endfor %}

Displaying an Image in ModelForm

I have the following model:
class Portfolio(models.Model):
id = models.AutoField(primary_key=True)
member = models.ForeignKey(Member, on_delete=models.CASCADE)
image = models.ImageField(upload_to='portraits/', default='/images/some_image.png')
For that I made the ModelForm:
class PortfolioForm(forms.ModelForm):
class Meta:
model = Portfolio
exclude = ['id']
I need many of those in one template so I am creating them in the Following way in my view
def portfolio_form(request, pk):
...
formset = PortfolioFormSet(queryset=Portfolio.objects.filter(pk__in=list_of_ids))
for x in p]
finally in the html I have this:
<form enctype="multipart/form-data" action="{% url 'elec:portfolio_form' pk=id %}" method="POST">{% csrf_token %}
...
{{ formset.management_form }}
{% for form in formset.forms %}
<tr>{% for field in form %}
{% if field.name == 'image' %}
<td><img src="{{field.image.url}}"></td>
{% else %}
<td align="center" valign="middle">{{field}}</td>
{% endif %}
{% endfor %}</tr>
{% endfor %}
<input type="submit" class="btn btn-default" value="Save and Refresh" />
</form>
Now what I expect to see is the image that I am saving into that Model. The if statement is coming back as false but why?. What am I doing wrong? Everywhere I looked it should work like this.
Also I have added the request.FILES in PortfolioForm with that the instance is not called how can I fix that?
OK I found the solution in Stackoverflow since I used some other words to find a solution.
what you need to do in the template instead of :
{<td><img src="{{field.image.url}}"></td>}
is this :
{<td><img src="{{form.instance.image.url}}"></td>}

Django getting properties of model dynamically

I have a model like
class Document(models.Model):
comment = models.TextField(null=True,blank=True)
upload_date = models.DateTimeField(blank=False)
Document objects are listed in a template as
{% for doc in doc_list %}
{{ doc.comment }}
{{ doc.upload_date }}
{% endfor %}
However, I'd like to reach the properties of doc dynamically like
{{ doc."comment" }}
or
{{ doc|getField:"comment" }}
How can I do that?
Thanks
I assume you mean you want to access fields on a model by using another variable, which you don't necessarily know at the time. So you might have some_var be passed into the template, and this is the field in the model that you'd like to display, such as comment or upload_date.
You could build a template tag to do this:
#register.simple_tag
def get_model_attr(instance, key):
return getattr(instance, key)
Now in your template you can do stuff like:
{% for doc in doc_list %}
{% get_model_attr doc "comment" %}
{% get_model_attr doc some_var %}
{% endfor %}

django filter a regroup within a forloop

I have a model called Subtopic. One of my templates runs a forloop on an object, returning a different field for each cell of a table row.
Two of the table cells look up a field which is a ManytoMany foreign key, both to the same foreign model, Resource. I want each to display different results, based on the value of a boolean field within the Resource model.
What you see below is currently working fine, but doesn't attempt to filter by the boolean field.
models.py:
class ICTResourceManager(models.Manager):
def get_query_set(self):
return super(ICTResourceManager, self).get_query_set().filter('is_ict': True)
class NonICTResourceManager(models.Manager):
def get_query_set(self):
return super(NonICTResourceManager, self).get_query_set().filter('is_ict': False)
class Resource(models.Model):
subtopics = models.ManyToManyField(Subtopic)
external_site = models.ForeignKey(ExternalSite)
link_address = models.URLField(max_length=200, unique=True, verify_exists=False)
requires_login = models.BooleanField()
is_ict = models.BooleanField()
flags = models.ManyToManyField(Flag, blank=True)
comment = models.TextField()
def __unicode__(self):
return u'%s %s' % (self.external_site, self.link_address)
objects = models.Manager()
ict_objects = ICTResourceManager()
nonict_objects = NonICTResourceManager()
class Meta:
ordering = ['external_site', 'link_address']
views.py:
def view_ks5topic(request, modulecode, topicshortname):
listofsubtopics = Subtopic.objects.filter(topic__module__code__iexact = modulecode, topic__shortname__iexact = topicshortname)
themodule = Module.objects.get(code__iexact = modulecode)
thetopic = Topic.objects.get(module__code__iexact = modulecode, shortname__iexact = topicshortname)
return render_to_response('topic_page.html', locals())
My template:
{% for whatever in listofsubtopics %}
<tr>
<td>
{{ whatever.objective_html|safe }}
<p>
{% if request.user.is_authenticated %}
{% with 'objective' as column %}
{% include "edit_text.html" %}
{% endwith %}
{% else %}
{% endif %}
</td>
<td>
{% regroup whatever.resource_set.all by external_site.name as resource_list %}
{% for external_site in resource_list %}
<h4>{{ external_site.grouper }}</h4>
<ul>
{% for item in external_site.list %}
<li>{{ item.comment }}</li>
{% endfor %}
</ul>
{% endfor %}
</td>
</tr>
{% endfor %}
As you can see, I've added extra managers to the model to do the filtering for me, but when I replace the appropriate lines in the template, I just get blanks. I have tried: for external_site.ict_objects in resource_list and for item.ict_objects in resource_list and <a href="{{ item.ict_objects.link_address }}">. If this were in the view I could probably do the filter just by .filter('is_ict': True), but with this being inside a forloop I don't know where to do the filtering.
I also tried writing regroup whatever.resource_set.filter('is_ict': True) in the template, but the syntax for regrouping seems to use resource_set.all rather than resource_set.all() (and I don't know why) so the filter text doesn't work here.
Turns out it was possible to do it using a custom template filter. The original efforts to filter within the template weren't working, given that as is well documented the template language is not a fully-fledged python environment. My original question remains open for anyone who knows an alternative method that more directly addresses the question I was originally asking, but here's how I did it:
myapp_extras.py:
from django import template
register = template.Library()
def ict(value, arg):
"filters on whether the is_ict Boolean is true"
return value.filter(is_ict=arg)
register.filter('ict', ict)
My template, note the use of the custom filter in line 2:
<td>
{% regroup whatever.resource_set.all|ict:1 by external_site.name as resource_list %}
{% for external_site in resource_list %}
<h4>{{ external_site.grouper }}</h4>
<ul>
{% for item in external_site.list %}
<li>{{ item.comment }}</li>
{% endfor %}
</ul>
{% endfor %}
</td>
After this I was able to remove the additional custom managers from the model.
One further question, when filtering for the boolean is_ict field, I found that I had to use filter(is_ict=1) and filter(is_ict=0). Is that the only way to refer to a True or False value?

How to display a list of objects containing many-to-many relations in Django template?

I have the following models:
class Tag(models.Model):
name = models.CharField(max_length=20)
class Entry(models.Model):
title = models.CharField(max_length=100)
date = models.DateField()
tags = models.ManyToManyField(Tag)
In a view I create a list of Entry object and want to show the elements in the template:
{% for entry in entries %}
{{ entry.title }}
{{ entry.date }}
<!-- {% for tag in entry.tags %} {{ tag }} {% endfor %} -->
{% endfor %}
And with this template code it generates the following TemplateSyntaxError pointing to the template's first line (for tag):
Caught TypeError while rendering: 'ManyRelatedManager' object is not iterable
The entries variable is a list:
entries = Entry.objects.filter(user=user_id)
entries = list(entries)
entries.sort(key=lambda x: x.id, reverse=False)
Do you know what can be the problem here and how to resolve this issue?
I'm new to Django, so any suggestions how to debug the templates may be helpful.
Update
I get the same error even with this template:
{% for entry in entries.all %}
<!-- everything is commented out here -->
{% endfor %}
There is no need to turn the entries QuerySet into a list. Additionally, you can let the DB do the sorting using order_by.
entries = Entry.objects.filter(user_id=user_id).order_by('id')
Add .all to get all the values from a relationship (just like Entry.objects.all()).
entry.tags.all
You can try this in the shell as well (I use ipython so your output may look different):
$ ./manage.py shell
# ...
In [1]: from yourproject.models import Entry, Tags
In [2]: entry = Entry.objects.all()[0]
In [3]: entry.tags
Out[3]: <django.db.models.fields.related.ManyRelatedManager object at 0x...>
In [4]: entry.tags.all() # for an entry with no tags.
Out[4]: []
In [5]: # add a few tags
In [6]: for n in ('bodywork', 'happy', 'muscles'):
...: t, created = Tag.objects.get_or_create(name=n)
...: entry.tags.add(t)
In [7]: entry.tags.all()
Out[7]: [<Tag: ...>, <Tag: ...>, <Tag: ...>]
And if you want to call out the entries with zero tags use for..empty.
{% for tag in entry.tags.all %}
{{ tag.name }}
{% empty %}
No tags!
{% endfor %}
Here is the solution of your query,
Verifying your solution by giving an example
Suppose a book have number of tags, so in order to display all the tags of a book on template can be like this
{% for tag in book.tags.all %}
{{ tag.name }}
{% endfor %}
where the model of Tag is like,
class Tag(models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return "%s" % unicode(self.name)
OK. I found the problem. I had some incorrect code which was commented out. But Django processed that code. So html comments didn't work here. I fixed this and it all worked like a charm.
So if you didn't know - the html comments don't prevent template processing.
This is because the template is being processed by Django first then HTML is rendered by browser.
The above from istruble is correct but if your question contains all of your code you need to specify a property in your template:
{% for entry in entries %}
{{ entry.title }}
{{ entry.date }}
{% for tag in entry.tags.all %} {{ tag.name }} {% endfor %}
{% endfor %}
or a default unicode function to your model:
class Tag(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name