I'm new to Django. I'm trying to figure out how to get the tags property of MyModel (manytomany) onto the template (mytemplate.html) for each object in MyModel. Note: there are 4 possible tags in the Tags admin. Some of the objects from MyModel have 2 tags, some have 1. How do I get each object's unique selection of tags onto my template? Here are my files:
models.py
class Tag(models.Model):
CATEGORY_CHOICES = (
('dataset', 'dataset'),
('brand', 'brand'),
)
tag = models.CharField(max_length=100)
category = models.CharField(max_length=100, choices=CATEGORY_CHOICES)
class MyModel(models.Model):
id = models.CharField(max_length=30, primary_key=True)
publish_date = models.DateField()
tags = models.ManyToManyField(Tag)
views.py
from .models import MyModel, Tag
def index(request):
tags = Tag.objects.all()
infos = MyModel.objects.all().order_by('publish_date').reverse()
return render(request, 'mytemplate.html', {'infos': infos, 'tags':tags})
mytemplate.html
Right now this just creates 4 p elements- one for all 4 possible 'tags'
{% for info in infos %}
<small>{{info.publish_date}}</small>
{% for tag in tags %}
<p>{{tag.tag}}</p>
{% endfor %}
{% endfor %}
Try the following
{% for info in infos %}
<small>{{info.publish_date}}</small>
{% for tag in info.tags.all %}
<p>{{tag.tag}}</p>
{% endfor %}
{% endfor %}
Related
This is my models.py
class report(models.Model):
Citizennumber = models.IntegerField()
Subject = models.CharField(max_length=100)
Patientfile = models.FileField(upload_to="Report")
Description= models.CharField(max_length=200)
Hospitalname= models.CharField(max_length=50, default="blank")
uploaddate = models.DateField(default=datetime.date.today)
How do I loop through this models in django templates so that if 'report.Hospitalname' has same value in multiple objects and I only want to print it once in templates?
First you should order the queryset by Hospitalname.
reports = Report.objects.order_by('Hospitalname')
Then you can use the template tag {% ifchanged %} to print the Hospitalname when it changes through iterations:
{% for report in reports %}
{% ifchanged report.Hospitalname %}
<h1>{{ report.Hospitalname }}</h1>
{% endifchanged %}
...
{% endfor %}
What I would like to do is show a table listing all authors, and for each author list their respective books. I'm using a form as some of the fields will be modifiable from the page.
models.py
class Author(models.Model):
author_id = models.CharField(max_length=80, primary_key=True)
first_name = models.CharField(max_length=80)
last_name = models.CharField(max_length=80)
class Book((models.Model):
book_id = models.CharField(primary_key=True)
title = models.CharField(max_length=200)A
author_id = models.ForeignKey(Author, on_delete=models.CASCADE)
forms.py
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = '__all__'
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = '__all__'
So in the view, I'm passing context which contains the two formsets to the template.
views.py
if request.method == 'GET':
author_dbrecords = Author.objects.all()
author_fields = [ f.name for f in Author._meta.get_fields() if not f.is_relation ]
authors_detail_list = [{ field:getattr(author, field) for field in author_fields } for author in author_dbrecords ]
book_dbrecords = Book.objects.all()
book_fields = [ f.name for f in Book._meta.get_fields() if not f.is_relation ]
books_detail_list = [{ field:getattr(book, field) for field in book_fields } for book in book_dbrecords ]
author_formset = AuthorFormSet(initial=authors_detail_list, prefix='author')
book_formset = AuthorFormSet(initial=book_detail_list, prefix='book')
context = {
'author_formset':author_formset,
'book_formset':book_formset,
}
return render(request, 'index.html' context)
In the template I can loop through the individual formsets, but I can't work out how to show all book titles for each author. Just using dot notation as you would for a regular model doesn't work.
index.html
<form action="{% url 'index' %}" method="post">
{{ author_formset.management_form }} {{ book_formset.management_form }}
{% for author in author_formset %}
<tr>
{% for author_field in author %}
<td> {{ author_field }}</td>
{% endfor %}
{{% for book_field in book.author %}
<td> {{ book_field }}</td>
{% endfor %}
I was able to resolve this by effectively accessing the underlying model (rather than form) in the template using ".instance", and using _set for the reverse relationship. I wrote a custom method in order to retrieve all fields from the Book model.
{% for author_field in author %}
<td> {{ author_field }}</td>
{% endfor %}
{% for book in author.instance.book_set.all %}
{% for book_field in book.get_fields %}
<td> {{ book_field }}</td>
{% endfor %}
{% endfor %}
New method in models.py:
def get_fields(self):
return [(field.value_to_string(self)) for field in Book._meta.fields]
Only downside at the moment is that I'm unable to check BookForm's hidden fields in the template.
I would like to create a view for multiple object deletion. For this, I thought I could use a modelformset_factory.
This are my models:
class Item(models.Model):
rfid_tag = models.CharField()
asset = models.OneToOneField('Assets', default=None, null=True,
on_delete=models.SET_DEFAULT,)
date = models.DateTimeField(name='timestamp',
auto_now_add=True,)
...
class Assets(models.Model):
id = models.AutoField(db_column='Id', primary_key=True)
assettag = models.CharField(db_column='AssetTag', unique=True, max_length=10)
assettype = models.CharField(db_column='AssetType', max_length=150)
...
class Meta:
managed = False
db_table = 'Assets'
ordering = ['assettag']
def __str__(self):
return f"{self.assettag}"
def __unicode__(self):
return f"{self.assettag}"
Below is the form and formset factory:
class ItemDelete(forms.ModelForm):
asset = forms.CharField(required=True,
help_text= "Item asset tag",
max_length=16,
)
delete = forms.BooleanField(required=False,
label="Delete",
help_text='Check this box to delete the corresponding item',
)
class Meta:
model = Item
fields = ['asset']
ItemDeleteMultiple= forms.modelformset_factory(model=Item,
form=ItemDelete,
extra=0,
)
The view:
class DeleteMultipleView(generic.FormView):
template_name = *some html file*
form_class = ItemDeleteMultiple
success_url = *some url*
def form_valid(self, form):
return super().form_valid(form)
And the template:
{% extends "pages/base.html" %}
{% block title %}
<title>Delete Multiple</title>
{% endblock %}
{% block static %}
{% load static %}
{% endblock %}
{% block content %}
<h1>Delete Multiple Items</h1>
<form class="item_delete_multiple_form" action ="." method="POST"> {% csrf_token %}
<table border="2">
<tr><th colspan="3" scope="row">Select Items to Delete</th></tr>
{% for item_form in form %}
<tr>
{% if item_form.non_field_errors %}
<td>{{ item_form.non_field_errors }}</td>
{% endif %}
{% if item_form.asset.errors %}
<td>{{item_form.asset.errors}}</td>
{% endif %}
<td><label for="{{ item_form.asset.id_for_label }}">AssetTag {{forloop.counter}}:</label></td>
<td>{{item_form.asset}}</td>
{% if item_form.delete.errors %}
<td>{{item_form.delete.errors}}</td>
{% endif %}
<td>{{item_form.delete}}</td>
</tr>
{% endfor %}
</table>
</form>
{% endblock %}
{% block script %}
{% endblock %}
The template is not very easy to the eye, so here is the important part: <td>{{item_form.asset}}</td>.
The issue is the following:
If I don't add the asset = CharField() part in the ItemDelete form, the template will render what the __str__ / __unicode__ method of the Assets model will return (the assettag field) in a choice field.
If the asset field is a CharField in the form, the template will render the id of the Assets. The database entry in the Item table.
I would like to render asset.assettag in a CharField (read only text input). Is it possible to do this?
Or is there a better way to achieve the multiple delete operation, using a list of objects and a checkbox?
I have came to the following solution:
class ItemDelete(forms.ModelForm):
asset = forms.CharField(required=True,
help_text= "Item asset tag",
max_length=16,
disabled=True,
)
delete = forms.BooleanField(required=False,
label="Delete",
help_text='Check this box to delete the corresponding item',
)
def __init__(self, *args, **kwargs):
super(ItemDelete,self).__init__(*args, **kwargs)
self.initial['asset'] = Assets.objects.get(id=self.initial['asset']).assettag
class Meta:
model = Item
fields = ['asset']
Given that the input field is used just for presentation purposes (is disabled and cannot be edited), I think it will do. The downside is that it will hit the database for every object (being a formset, every object will have it's own form).
I am still open to better suggestions.
I have a category model and list of posts related to those category also some post with same category name but when i wanted to make list of category section in template,
it showing duplicate name of category as it related to posts like:
food,
food,
desert,
style,
desert,
but I want like:
food,
desert,
style,
here is my code:
views.py
class ListCategory(ListView):
model = Post
paginate_by = 2
template_name = 'shit.html'
context_object_name = 'queryset'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cate = Post.objects.all()
context['cate'] = cate
return context
models.py
class Category(models.Model):
title = models.CharField(max_length=20)
thumbnail = models.ImageField()
detail = models.TextField()
featured = models.BooleanField(default=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-category', kwargs={
'pk': self.pk
})
class Post(models.Model):
title = models.CharField(max_length=100)
overview = models.TextField()
featured = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(Author,on_delete=models.CASCADE)
thumbnail = models.ImageField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
tags = TaggableManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={
'pk': self.pk
})
templates
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="sidebar-box ftco-animate">
<ul class="categories">
<h3 class="heading mb-4">Categories</h3>
{% for cat in cate %}
<li>{{cat.category}}<span>(12)</span></li>
{% endfor %}
</ul>
</div>
{% endblock content %}
Thank you so much!
Seems like you want to group your Posts, based on their category; so you can achieve that by iterating over the Category (instead of Post), and use the backward relationship to find out the related Post objects.
views.py
class ListCategory(ListView):
model = Category
paginate_by = 2
template_name = 'shit.html' # :)
context_object_name = 'queryset'
template:
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="sidebar-box ftco-animate">
<ul class="categories">
<h3 class="heading mb-4">Categories</h3>
{% for category in queryset %}
<li>{{category}}<span>{{ category.posts_set.count }}</span></li>
<ul>
{% for post in category.post_set.all %}
<li>{{ post }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
</div>
{% endblock content %}
I also use {{ category.post_set.count }} instead of 12, since I think you are looking for the number of Post objects within each category.
You can use unique=True in desired field, to make every value unique. If you'll try to add new record with same value of unique field, a django.db.IntegrityError will be raised.
More about unique
More about model's fields options
I have a little problem. I want to create something like a webpages directory. In my model I've create a class Kategorie, and class Firma. Class Kategoria creating main categories and subcategories. In class Firma I can define in witch category and subcategory the new record will be belong. My question is: How to display in html on main page main categories and little lower the subcategories like in this picture
Here is my code
models.py
from django.db import models
from django.contrib.auth.models import User
class Kategoria(models.Model):
name = models.CharField(max_length=250, verbose_name='Kategoria')
slug = models.SlugField(unique=True,verbose_name='Adres SEO')
parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name = 'Kategoria'
verbose_name_plural = 'Kategorie'
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' / '.join(full_path[::-1])
class Firma(models.Model):
user = models.ForeignKey(User, default=1, verbose_name='Użytkownik', on_delete=models.CASCADE)
title = models.CharField(max_length=250, verbose_name='Nazwa firmy')
slug = models.SlugField(unique=True, verbose_name='Adres SEO')
category = models.ForeignKey('Kategoria', null=True, blank=True, verbose_name='Kategoria', on_delete=models.CASCADE)
content = models.TextField(verbose_name='Opis')
draft = models.BooleanField(default=False, verbose_name='Szablon')
publish = models.DateField(auto_now=False, auto_now_add=False)
class Meta:
verbose_name='Firma'
verbose_name_plural='Firmy'
def __str__(self):
return self.title
views.py
from django.shortcuts import render, get_object_or_404
from .models import Kategoria, Firma
def widok_kategorii(request):
kategorie = Kategoria.objects.filter().order_by('name')
context = {'kategorie': kategorie}
return render(request, 'ogloszenia/index.html', context=context)
index.html
{% include 'ogloszenia/header.html' %}
Wyświetl kategorie<br>
{% for kategoria in kategorie %}
<b>{{kategoria.name}}<br></b>
{% endfor %}
{% include 'ogloszenia/footer.html' %}
So the problem is sub categories, right?
You can use inclusion_tag in your template as i mentioned here once:
Tree Structure (Foreign Keys to itself) and templates
If you need to render multiple level of sub categories then just do as i mentioned in the link.
But if you just need the first level, then it's pretty simple:
views.py:
Getting categories without any parent (line #2)
def widok_kategorii(request):
### Get the categories without any parent.
kategorie = Kategoria.objects.filter(parent=None).order_by('name')
context = {'kategorie': kategorie}
return render(request, 'ogloszenia/index.html', context=context)
Template:
{% include 'ogloszenia/header.html' %}
Wyświetl kategorie<br>
<ul>
{% for kategoria in kategorie %}
<li>
{{kategoria.name}}
{% if kategoria.children.count > 0 %}
<ul>
{% for sub in kategoria.children.all %}
<li>{{ sub.name }}</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% include 'ogloszenia/footer.html' %}
You can just design it to be look like the picture you posted but this is the way to achive the tree structure for such a design.