Loop over related model's children in Django template - django

I have a model for a company.
Then I have a base model for company posts. It contains common posts attributes. An attribute is the company that publishes the posts. It refers to the Company model with a ForeignKey.
Finally I have a child model (based on the CompanyPost base model) for posts of type A:
class Company(models.Model):
name = models.CharField(...)
...
class CompanyPost(models.Model):
company = models.ForeignKey(Company,...)
...
class PostA(CompanyPost):
name = ...
In a template I want to loop over posts of type A published by a specific company.
I tried these variants:
1)
{% for postA in company.companyposts_set.all.postA_set.all %}
...
2)
{% for companyposts in company.companypost_set.all %}
{% for postA in companyposts.postA_set.all %}
...
{% endfor %}{% endfor %}
I tried other sub-variants of the above. None seems to work.
I know that I can easily prepare the set in the view, like:
postsA = PostA.objects.filter(company__pk=pk)
And pass postsA to the template context, but I'm wondering whether there is a way to loop over related models' children in the template.
(note: looping over companyposts works. But I get of course all types of posts, like postB etc.:
{% for post in company.companypost_set.all %}
That is why I tried variant 2) above to loop again over the results.)
Thank you in advance.
UPDATE:
Thank you all for your answers.
I understand that, by choosing model inheritance, I chose a convoluted solution.
In the present post I'm asking whether displaying related model's children in a template is possible. In order not to confuse questions, in this question I explain why I used concrete model inheritance and ask what would be a better solution.

If you don't want to define it in the views, you could define it as a property of Company objects.
#property
def post_a_set(self):
return PostA.objects.filter(company__pk=self.pk)
I'm pretty sure it's the model inheritance that is causing the problems, and dimly remember seeing something like his documented. I'd echo, do you really need concrete model inheritance here? Other approaches are wider CompanyPost objects with a post_type choices field and other fields null or blank if inappropriate; or a post_type field and the data that applies only to that type stored as a JSON string or (if you are using Postgresql) a JSONField.

Related

How do I display Django ManyToMany on a template? Simple code needed

I am trying to display ManyToMany field on the template in reversed order.
Here is what I mean:
I managed to display ManyToMany field on template when ManyToMany field was a field in model used so for example:
<br/>{% for tag in post.tag.all %}{{ tag }}<br/>{% endfor %}
will display all of the tags(meaning categories) that the post belongs to based on this model:
class Post(models.Model):
tag = models.ManyToManyField(Tag,blank=True,null=True,related_name='tag')
Now I want something opposite - display authors of the post when ManyToMany field is in the Author model (Post model above stays the same):
class Person(models.Model):
post=models.ManyToManyField(Post,blank=True,null=True,related_name='post')
I am quite sure it has something to do with Related Object Reference ( https://docs.djangoproject.com/en/2.2/ref/models/relations/)
Just can not make it work.
I have tried the following on the template.
{% for post in posts %}
{% for author in post.person_set.all %}{{author}}<br/>{% endfor %}
{% endfor %}
Also, shall I do this kind of searches on the template like above or is it a better practice put this kind of searches in views...resourcewise.
Thanks for help.
You have a misunderstanding on what the related_name= parameter [Django-doc] does. Like the documentation says:
The name to use for the relation from the related object back to this one. (...)
So it is the name of the relation in reverse. In order to make your models "sound", you thus should name it like:
class Person(models.Model):
posts = models.ManyToManyField(Post, blank=True, null=True, related_name='authors')
It also makes sense here to use the plural, so posts instead of post.
In that case, you thus can render this with:
{% for post in posts %}
{% for author in post.authors.all %}{{author}}<br/>{% endfor %}
{% endfor %}
Note that if you want to render all the values for ManyToManyFields, you better use .prefetch_related(..) in the queryset to prefetch the Person,s otherwise rendering the template will result in a lot of extra queries.

related items in loop creates many duplicate queries

Rendering a page gets really slow when related objects are requested in a template.
class Item(models.Model):
name = models.CharField(max_length=160)
...
class Box(models.Model):
...
items = models.ForeignKey(Item, on_delete=models.CASCADE, null=True)
#template
{% for item in items %}
{{ item.box_set.first }}
{{ item.box_set.latest }}
{% endfor %}
Debug toolbar shows there many duplicate queries.
Why is this happening? Is there a way to speed this up?
The Django ORM has to make a request to the database when accessing a related field unless it's already cached. The main ways of caching the related objects are via select_related and prefetch_related.
What you're trying to do is a bit more difficult; you're trying to get two specific items from a collection. You can use .annotate() and Subquery to pull singular fields from a related model. This would be useful if you just wanted to display single field from box, but if you need the whole box instance this won't work.

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.

Django: defining a model to make a template

I am new to creating models in Django and I want to make a model, which allows you to fill in the title, some texts for on the template and the path to this template. I am trying to get the answer off of the Django Example Project, but I just don't understand the models, is there anybody who can help me on how to write such a model?
My code in the models.py
class Project(models.Model):
project_name = models.Charfield(max_length=20)
project_title = models.Charfield(max_length=100)
project_information = models.Charfield(max_length=400)
where project_name is the link to the template
I don't know if this is a correct begin or that it should be something completely different.
I'm assuming that you're asking ways to use the information in a model (object data) in a template.
If so, template isn't created in a model, you can send your object(s) to a template by rendering a template in the corresponding view of the model. You'll append the necessary information (queryset and other optional dict elements) in the view and send it to the template.
How you can use the information in the template depends on your view method. For function based views like this:
def home_view(request):
contests = Contests.objects.filter(id<5)
context={'contests':contests}
return render(request, 'home/home.html', context)
You can access the data in a queryset in the template, like this:
{% for contest in contests %}
//do something with the single contest data
{% endfor %}
You can directly access the non-plural values like this:
{% if not random_contest == None %}
//do something with the random contest data
{% endif %}
Additionally, there are class based views but it looks like you are at a very early stage of learning Django, so you won't need it for now.
See the docs about views.
See the docs about shortcut functions.

How to display multiple forms of a single model in Django templates?

I have this model Note:
class Note(models.Model):
category = models.ForeignKey(Category)
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=40)
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
And I want to display this form:
class NoteEditForm(forms.ModelForm):
class Meta:
model = Note
fields = ('title', 'text')
in a template, but I want it to appear for each existing Note object in the database (it has to be that way). I've done something like that but then I hardcoded the form, pointing to the edit view URL with each object pk as a parameter; but I'm sure it has to be a clearer way, just I haven't found it. Could you guys help me with that? Thanks!
The easiest way to do this is to use a formset (also see model formsets).
For your Note model and NoteEditForm you could do something like this. You'd usually put this wherever you've defined your NoteEditForm but it can go in another file, such as views.py.
from django.forms import modelformset_factory
NoteEditFormSet = modelformset_factory(Note, form=NoteEditForm)
Using NoteEditFormSet in a view and template is almost the same as using a regular form, but there are a few differences to be aware of if you want to do anything complicated so have a look at the docs (view and template). If that's not clear enough, add a few details of what you're trying to do in your view and template and I'll try to help.
By default the formset will use Note.objects.all() as its queryset, which is what you say you want, but you can change that (details are covered in the docs).
Update:
To save an individual Note with an AJAX request I would add a second view to handle those requests. So if your formset for all Notes is served by a view at /notes/, you could add a view to handle your AJAX request at /notes/<note_id>/ (obviously just an example, adjust to fit your URL structure).
Then your JS on the /notes/ page is responsible for serializing the data for a single note and making the request to /notes/<note_id>/ (remember the CSRF token).
The HTML inputs generated for the formset have their IDs prefixed with something like id_form-<number>- and there are hidden inputs containing Note primary keys which will let you work out which ID prefix applies to each note.
I would think about doing it like this
{% for note in Notequeryset %}
<form action={% url 'url_name_to_form' pk={{note.pk}} %}>
{{form.as_p}}
</form>
{% endfor %}
Let me know what you think