Use of related field name in django template - django

I have tow models like below:
class A(models.Model):
a = models.BooleanField(default=False)
q = models.BooleanField(default=False)
class B(models.Model):
c = models.Foreignkey('A', related_name='bb')
d = models.BooleanField(default=False)
e = models.BooleanField(default=False)
Here is my view:
class Myview(ListView):
model = A
template_name = 'admin/layer.html'
def get_context_data(self, *args, **kwargs):
context = super(ListView, self).get_context_data(*args, **kwargs)
context['mylist'] = A.objects.filter(bb__e=False)
return context
Everything is working fine except In my template 'admin/layer.html' I am trying this:
{% for list in mylist %}
{{ list.bb.d }}
{% endfor %}
but I do not get any value for {{ list.bb.d }} Can I use related field name in this way in django template ?

Note that list.bb will only give you the RelatedManager. Here an instance of A can be related to multiple instances of B.
So to get them all you need to use following syntax:
{% for a_obj in mylist %}
{% for b_obj in a_obj.bb.all %}
{{ b_obj }}
{% endfor %}
{% endfor %}
More details provided here:
You can override the FOO_set name by setting the related_name parameter in the ForeignKey definition. For example, if the Entry model was altered to blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries'), the above example code would look like this:
>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.

Related

How to pass a custom model manager queryset to a template

I am trying to pass a custom query to a template but the query results are not being displayed.
I had a working solution for this but I wanted to implement a custom model manager to simplify the code but I cant get the final step work - that is displaying the results on the template
I have a custom manager:
from django.db import models
class ProfileQuerySet(models.QuerySet):
def get_users_follows(self, user):
print(user)
return self.filter(followed_by__user__username=user)
class ProfileManager(models.Manager):
def get_queryset(self):
return ProfileQuerySet(self.model, using=self._db)
def get_users_follows(self, user):
return self.get_queryset().get_users_follows(user)
which is instantiated in the model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
follows = models.ManyToManyField('self', related_name='followed_by', symmetrical=False)
objects = ProfileManager()
my view is as follows:
class FollowsListView(LoginRequiredMixin, ListView):
# Follow.follow_data = [[],[]]
model = Profile
template_name = 'blog/follows.html' # <app>/<model>_<viewtype>.html
# paginate_by = 6
def get_queryset(self):
follow_data = Profile.objects.get_users_follows(self.request.user)
context = {'follow_data', follow_data}
return context
In the template i am calling the follow_data like so:
{{ follow_data }}
{% for p in follow_data %}
{% if p.user %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ p.user.profile.image.url }}" alt="">
{{ p.user.profile.user }}
{% else %}
<p>You are not following any users</p>
{% endif %}
</article>
{% endfor %}
but no results are being displayed so I am not quite sure what I am doing wrong here. Can anyone provide me weith a quick pointer on what ive done wrong?
There is a typo:
context = {'follow_data', follow_data}
You've created a set, but what you really want is a dict
context = {'follow_data': follow_data}
edit:
but you shouldn't be returning a context here anyway, you should return a queryset. simply do return follow_data should work.
in your template, you can refer to the queryset with {% for user in object_list %}
If you don't want to call it "object_list" you'll need to override get_context_data
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["follow_data"] = context.pop("object_list")
return context
doc: https://docs.djangoproject.com/en/2.2/ref/class-based-views/generic-display/#listview

Display foriegnkey fields in Django template for a CreateView

I am trying to display a checklist in the CreateView using the values in the ForeignKey fields for descriptions.
models.py
class Structure(models.Model):
name = models.CharField(max_length = 30)
description =models.CharField(max_length = 300, null=True, blank=True)
def __str__(self):
return self.name
class SelectedFramework(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
structure = models.ForegignKey(Structure)
selected = models.BooleanField(default = False)
views.py
class FrameworkCreateView(generic.CreateView):
model = SelectedFramework
fields =['structure', 'selected']
template_name = 'catalogue/structure.html'
def form_valid(self, form):
form.instance.user = self.request.user
return super(FrameworkCreateView, self).form_valid(form)
structure.html
{% extends 'catalogue\base.html' %}
{% block container %}
<h2>{% block title %}Structures{% endblock title %}</h2>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="col-sm-10">{{form.structure}} {{form.selected}}</div><br>
{% endfor %}
</div>
</form>
{% endblock %}
The code above works but will display the ForeignKey 'structure' as a dropdown list with the values of __str__. Is there a way to display string for structure.name and structure.description with the checkbox from selected in the CreateView?
In your template use:
{{ form.structure.name }}
{{ form.structure.description}}
You can write custom form, override the save method and create Structure object manually there:
class FrameworkForm(forms.ModelForm):
structure_name = forms.CharField(required=True)
structure_description = forms.CharField(required=False)
class Meta:
model = SelectedFramework
fields = [
'structure_name', 'structure_description', 'selected'
]
def save(self, commit=False):
instance = super(FrameworkForm, self).save(commit=False)
structure = Structure(
name=self.cleaned_data.get('structure_name'),
description=self.cleaned_data.get('structure_description')
)
structure.save()
instance.structure = structure
instance.save()
return instance
Also add form_class = FrameworkForm to your view instead of fields = ['structure', 'selected']
EDIT:
Perhaps you want something like this:
<ul>
{% for structure in form.fields.structure.choices.queryset %}
<li>{{ structure.name }} - {{ structure.description }}</li>
{% endfor %}
</ul>
If you want to get fields by iterating in the template. You have to use-
{% for field in form %}
{{ field }}
{% endfor %}
don't have to use any dot notation to get the field. If you want to get the label of the field you can use {{ field.label}} usually before {{field}}

Show ManyToManyField attributes of a given model in template

How can I show in a template the attributes related by a ManyToManyField of a model?
models.py
class Group(models.Model):
name = models.CharField()
user = models.ManyToManyField(User, related_name='user_group')
class Team(models.Model):
name = models.CharField()
group = models.ManyToManyField(Group, related_name='group_team')
views.py
class Index(ListView):
template_name = 'App/index.html'
model = User
def get_queryset(self):
return User.objects.filter(...)
template
{% for user in user_list %}
{{ user.username }}
{{ user.user_group.name }}
{{ user.user_group.group_team.name }}
{% endfor %}
I can show the username of the user but not the rest of the fields.
Since a ManyToMany relationship may have many objects all I had to do is iterate over them:
{% for group in user.user_group.all %}
{{ group.name }}
{% endfor %}
Update: As Todor suggests adding prefetch_related avoid unnecessary database queries
User.objects.filter(...).prefetch_related('user_group')

Django - get in template reverse related many to many field

I have 3 models, Entry model and Category model, and I have created intermediate model CategoryEntry.
class Entry(models.Model):
entry_text = models.TextField()
user = models.ForeignKey(User)
class Category(models.Model):
user = models.ForeignKey(User)
category_text = models.CharField(max_length=200)
entries = models.ManyToManyField(Entry, through='CategoryEntry')
class CategoryEntry(models.Model):
category = models.ForeignKey(Category)
entry = models.ForeignKey(Entry)
viewed = models.BooleanField(default=False)
I have created a View with get_queryset method like this
def get_queryset(self):
order_by = self.request.GET.get('order_by')
if not order_by:
order_by = '-pub_date'
return Entry.objects.filter(category__id=self.kwargs.get('category_id', None), category__user__id=self.request.user.id).order_by(order_by)[:].select_related('user')
I have category ID from kwargs. The problem is - how to get every entries related field "viewed", so I can show its value in template. For example related User field I can get like
{% for entry in entries %}
{{ entry.entry_text }}
{{ entry.user.name }}
{% endfor %}
What I need is to access "viewed" field something like "entry.categoryentry.viewed"
Tried prefetch_related, but doesn't seem to work or don't get how to know right name to access field in template
Entry.objects.filter(category__id=self.kwargs.get('category_id', None), category__user__id=self.request.user.id).order_by(order_by)[:].select_related('user').prefetch_related(Prefetch("categoryentry_set", queryset=CategoryEntry.objects.filter(category__id=self.kwargs.get('category_id', None))))
Thanks!
You would do:
{% for entry in entries %}
{{ entry.entry_text }}
{{ entry.user.name }}
{% for ce in entry.catalogentry_set.all %}
{{ce.category.category_text}}
{# or whatever.. #}
{% endfor %}
{% endfor %}
Basically, if no related_name is present, you could access the reverse lookup element by lowercase model name + _set - which returns a queryset manager object.

Django - custom attributes for model fields

Is there a way in Django to add custom attributes to a model's fields (without resorting to subclassing fields)?
I would like to only display certain fields in certain sections of my template. This is because eventually each type of field will be displayed in a separate tab. I thought about adding a custom attribute to each field to identify which section/tab it should go in. But, so far, I've had no luck.
I have a few field types:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
FieldTypes = Enum(["one","two","three",])
And a few models:
class Model1(models.Model):
a = models.CharField()
b = models.ForeignKey('Model2')
c = models.IntegerField()
a.type = FieldTypes.one # this obviously doesn't work
b.type = FieldTypes.two # this obviously doesn't work
c.type = FieldTypes.three # this obviously doesn't work
class Model2(models.Model):
d = models.CharField()
And a form:
class Form1(forms.ModelForm):
class Meta:
model = Mode1
And a template:
{% for fieldType in FieldTypes %}
<div class="{{fieldType}}">
{% for field in form %}
{% if field.type = fieldType %}
{{ field }}
{% endif %}
{% endfor %}
</div>
{% endfor %}
But this doesn't work.
Ideas? Or other suggestions for only placing certain fields in certain sections of the page?
Thanks.
In general, I would keep this logic outside of the model class. Models shouldn't be tangled up with presentation elements if you can help it, and choosing which fields to display in a form seems like a presentation concern. Fortunately, the Form class provides a nice, UI-focused layer between the data layer (the model) and the presentation layer (the view and template).
Here's how I've addressed this in the past. In my Form class, I created a list of field groups, each with a title and a list of the names of the fields they contain:
class MyModelForm(forms.ModelForm):
field_groups = (
{'name':'Group One', 'fields':('a', 'b', 'c')},
{'name':'Group Two', 'fields':('d', 'e')},
)
class Meta:
model = MyModel
Then in the template, I looped through the groups, and within that loop conditionally included those fields:
{% for group in form.field_groups %}
<h3 class="groupheader">{{group.name}}</h3>
{% for field in form %}
{% if field.name in group.fields %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endif %}
{% endfor %}
{% endfor %}
This allows you to control the grouping and display of form fields within the MyModelForm class, which is a reasonable place for presentation logic to live.
It's posible!
class Model1(models.Model):
a = models.CharField()
b = models.ForeignKey('Model2')
c = models.IntegerField()
And a form:
class Form1(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Form1, self).__init__(*args, **kwargs)
self.fields['a'].type = FieldTypes.one
self.fields['b'].type = FieldTypes.two
self.fields['c'].type = FieldTypes.three
class Meta:
model = Mode1
Turns out, that I do want this logic in my model class; the field types are used for more than just working out where/how to display them (though they are used for that too). I have come up with the following solution.
I defined some classes to store a set of field types:
class FieldType(object):
def __init__(self, type=None, name=None):
self._type = type
self._name = name
def getType(self):
return self._type
def getName(self):
return self._name
class FieldTypeList(deque):
def __getattr__(self,type):
for ft in self:
if ft.getType() == type:
return ft
raise AttributeError
FieldTypes = FieldTypeList([FieldType("ONE","The Name Of Field One"),FieldType("TWO","The Name Of Field Two")])
And a few models each of which has a set of mappings from field types to particular field names (In this example, fields 'a', 'b', and 'c' are of type 'ONE' and field 'd' is of type 'TWO'):
class ParentModel(models.Model):
_fieldsByType = {}
a = models.CharField()
b = models.CharField()
def __init__(self, *args, **kwargs):
super(ParentModel, self).__init__(*args, **kwargs)
for ft in FieldTypes:
self.setFieldsOfType(ft, [])
self.setFieldsOfType(FieldTypes.ONE, ['a','b'])
def setFieldsOfType(self,type,fields):
typeKey = type.getType()
if typeKey in self._fieldsByType:
self._fieldsByType[typeKey] += fields
else:
self._fieldsByType[typeKey] = fields
class ChildModel(ParentModel):
_fieldsByType = {} # not really sure why I have to repeat this attribute in the derived class
c = models.CharField()
d = models.CharField()
def __init__(self, *args, **kwargs):
super(ChildModel, self).__init__(*args, **kwargs)
self.setFieldsOfType(FieldTypes. ['c'])
self.setFieldsOfType(FieldTypes. ['d'])
I have a basic form:
class MyForm(forms.ModelForm):
class Meta:
model = ChildModel
And a custom filter to return all fields of a given type from a particular form (note, the accessing the model from the form via its Meta class):
#register.filter
def getFieldsOfType(form,type):
return form.Meta.model._fieldsByType[type.getType()]
And, finally, a template to pull it all together (the template takes MyForm and FieldTypes):
{% for type in types %}
<div id="{{type.getType}}">
{% with fieldsOfType=form|getFieldsOfType:type %}
{% for field in form %}
{% if field.name in fieldsOfType %}
<p>
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</p>
{% endif %}
{% endfor %}
{% endwith %}
</div>
{% endfor %}