display model fieldnames in Django listview - python

In my listview I want to display several fields from two models containing a many-to-many field.
I can display the correct values from one table, but then I cannot access the other table with the many-to-manyfield.
Models.py
class Books(models.Model):
title = models.CharField(max_length=100)
class Author(models.Model):
book = models.ManyToManyField(Books)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=200)
Views.py
class BooksListView(ListView):
context_object_name = 'booklist'
model = Author
template_name = 'Books/books_list.html'
Book_list.html
{% for books in booklist %}
<h5><li class="list-group-item list-group-item-light"">
{{books.book.all}}:{{books.first_name}} {{books.last_name}}</li></h5>
{% endfor %}
The first and lastname are displayed properly, but the books.book.all ()i know this is the wrong query) returns a queryset containing the title (which is what I need, but this is in format <QuerySet [<Books: Book Title>]>. But books.book.title doesnt seem to work. What I want to display is "booktitle - firstname lastname", and because I am using two tables, I need a many-to-many relationship. Any thoughts?

The many-to-many relationship goes both ways, so you don't need to base everything around authors.
class BooksListView(ListView):
context_object_name = 'booklist'
model = Book
template_name = 'Books/books_list.html'
And the template:
{% for books in booklist %}
<li class="list-group-item list-group-item-light">
<h5>{{ book.title }}</h5>
<p>
{% for author in book.author_set.all %}
{{ author.first_name }} {{ author.last_name }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
</li>
{% endfor %}
Notes:
By default, the related_name is {{ModelName}}_set (author_set in this case). If you want something more natural you can set related_name='authors'.
There will be a query made to the databse for every book in the list and this can be quite slow with a lot of data, so take a look at prefetch_related().

Related

How do I show navbar items based on a model field in Django?

I have a Blog model that looks like this:
class Blog(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blogs')
parent = models.CharField(max_length=50, choices=PARENT_TUTORIALS)
def get_absolute_url(self):
return reverse("blog_list", args=[str(self.parent), str(self.slug)])
I can succesfully display all the blogs on a table of contents via my table.html template:
{% for blog in blog_list %}
<li>{{blog.title}}</li>
{% endfor %}
However, I want to show only those blogs that have the same Blog.parent value as the current blog page. For example, the page example.com/biology/page1, has biology as a parent. When the user is on that page, the table of contents should show only the pages that have biology as a parent.
Why not just add an if statement like so?
template.html
{% for blog in blog_list %}
{% if blog.parent == current_blog.parent %}
<li>{{blog.title}}</li>
{% endif %}
{% endfor %}
Another option is to filter with js, something like (untested):
template.html
{% for blog in blog_list %}
<li class="blog-list-item {{blog.parent}}">
{{blog.title}}
</li>
{% endfor %}
script.js
$('.blog-list-item').filter(':not(.biology)').hide();

Django - Forms - assign custom attributes to forms in forms.py

The question title might be a little misleading, but I couldn't think of a better title. If you've got better title, please edit the title.
I have following set of models.py and forms.py`
# models.py
class BHA_Component(models.Model):
field_1 = models.CharField(max_length=100)
field_2 = models.CharField(max_length=100)
field_3 = models.CharField(max_length=100)
# forms.py
class BHA_Component_Form(forms.ModelForm):
class Meta():
fields = '__all__'
I want to create custom attributes for each field, so that I can identify what kind of field it is on the Front-End, and assign a class for each field type.
Something like this:
Some fields are just plain blank, some are grey, some are purple, and some have check boxes. These are done by manually giving each field a particular HTML class on the Front-End. However, I want to give each field some attributes on the Back-End, and have those attributes identified on the Front-End. So, something like this:
{% for field in bha_component_form %}
{% if field.custom_attribute == 'option_1' %}
{{ field|add_class:"has_checkbox"}}
{% else if field.custom_attribute == 'option_2' %}
{{ field|add_class:"blue_background"}}
{% else %}
{{ field|add_class:"plain"}}
{% endif %}
{% endfor %}
How can I do this?
To pass in attributes on the backend you can try something likes this:
email = forms.EmailField(widget=forms.EmailInput(attrs={'class': "form_control form-control-lg", 'placeholder': "Email"}), required=True, )
So, for your specific case:
models.py:
class BHA_Component(models.Model):
field_1 = models.CharField(max_length=100, widget=forms.TextInput(attrs={'custom_attribute': "option_1") })
field_2 = models.CharField(max_length=100, widget=forms.TextInput(attrs={'custom_attribute': "option_2") })
field_3 = models.CharField(max_length=100, widget=forms.TextInput() })
It should be a case of using something like this in your template:
{% for field in bha_component_form %}
{% if field.widget.attrs.custom_attribute == 'option_1' %}
{{ field|add_class:"has_checkbox"}}
{% else if field.widget.attrs.custom_attribute == 'option_2' %}
{{ field|add_class:"blue_background"}}
{% else %}
{{ field|add_class:"plain"}}
{% endif %}
{% endfor %}
It should just be a case of modifying what I have outlined above for your specific use case.
Hope that helps!

Look up field from a different Django model given another model field

I have two models in Django: Room for offices, and Person for employees. One office could have multiple employees. I'm trying to make a detail-view html page that shows a person's details, like their name and office number. I can get the details from the Person model fine, but I'm having trouble doing a reverse look-up to the Room model. How can I get a person's office given the following code?
#models.py
class Room(models.Model):
number = models.CharField('Room number', unique=True)
persons = models.ManyToManyField('Person', blank=True)
#...
class Person(models.Model):
name = models.CharField('Full name', max_length=200)
#...
#views.py
from django.views import generic
class PersonDetailView(generic.DetailView):
model = Person
#person_detail.html
{% extends "base_generic.html" %}
{% block content %}
<h1>Name: {{ person }}</h1>
<p>Room: {{ Room.number }}</p>
{% endblock %}
All that currently does is return a name, but "Room" is left blank.
You could overload the get_context_data method
class PersonDetailView(DetailView):
model = Person
context_object_name = 'person'
template_name = 'person_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# woops, typo
# context['room'] = Room.objects.filter(person=context['object']).first()
context['room'] = Room.objects.filter(persons=context['object']).first()
return context
And the template:
#person_detail.html
{% extends "base_generic.html" %}
{% block content %}
<h1>Name: {{ person }}</h1>
<p>Room: {{ room.number }}</p>
{% endblock content %}
Edit:
But you might be better off with Person having a models.ForeignKey pointing to a Room, so a Person can only have one Room, but many Person can have the same Room.
You could then keep your orginal view and change your template:
#person_detail.html
{% extends "base_generic.html" %}
{% block content %}
<h1>Name: {{ person }}</h1>
<p>Room: {{ person.room.number }}</p>
{% endblock content %}

Listing ForeignKey associated instances within template (queryset within a queryset)

I have a site which catalogs local hikes, and users can log that they have been on the hike. I have a search page which contains the hikes, and one of the fields I'm trying to display is a list of all the people who have been on the hike. I've got this figured out within the individual detail page of the hike, but can't figure out how to create a new queryset within the queryset which is printing the hikes, in order to display this info on a search page.
Here's some code:
models.py:
class Hike(models.Model):
name = models.CharField(max_length=255, unique=True)
slug = models.SlugField(unique=True)
...
class UserLog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
hike = models.ForeignKey(Hike, on_delete=models.CASCADE)
views.py:
def hike_list(request):
qs = Hike.objects.all()
... some other filters here
?-->users = UserLog.objects.filter(id=qs.id)
template:
{% for qs in qs %}
{{ hike.name }}{{ hike.other_details_and_stuff }}
?----> {% for users in hikes %}{{ user.name }}{% endfor %}
{% endfor %}
Here's the working code within the individual hike's detail page:
views.py:
def hike_detail (request, slug)
users = UserLog.objects.filter(hike__slug=slug)
How do I call on the slug from each individual item in the queryset, then run a queryset on that?
The easiest is to add a ManyToManyField to Hike:
class Hike(models.Model):
...
users = models.ManyToManyField(User, through='app.UserLog')
If you have no extra fields in UserLog, you can even remove the UserLog model and the through parameter alltogether. In the template you can do:
{% for hike in qs %}
{{ hike.name }}{{ hike.other_details_and_stuff }}
{% for user in hike.users.all %}{{ user.name }}{% endfor %}
{% endfor %}
In order avoid too many queries, you should prefetch the users in the Hike query in the view:
qs = Hike.objects.all().prefetch_related('users')
Without the ManyToManyField, you could add a property and user the same template, but the prefetch clause could not be used that easily:
class Hike(models.Model):
...
#property
def users(self):
return User.objects.filter(userlog__hike=self)

Access Many-to-Many relationship data in template

I am new to django and trying to model a OOP concept.
I have model for actor and movies. Movie have many-to-many relationship with actor model.
url.py
from movies import views
url(r'^recent/$', views.recentlyadded, {'movieid': 1}),
models.py
from django.db import models
class Actor(models.Model):
actorid = models.AutoField(primary_key=True)
name = models.CharField(max_length=250)
sex = models.CharField(max_length=1)
class Movie(models.Model):
movieid = models.AutoField(primary_key=True)
title = models.CharField(max_length=250)
actor = models.ManyToManyField(Actor)
view.py
from movies.models import *
def recentlyadded(request, movieid):
m = get_object_or_404(Movie, pk = movieid)
return render_to_response('search_results.html', {'movies': m})
search_results.html
{% if movies %}
{{movies.title}}
<ul>{{ movies.actor_set.all.count }}
{% for actor in movies.actor_set.all %}
<li>{{actor.name}}</li>
{%endfor%}
</ul>
{% endif %}
Output
The Dark Knight
I am not getting the actor list at all.
Should I use a intermediary model? If yes, what will happen if I have to associate directors to the movie model? How many intermediary model should be created?
Please help with the code. Thanks in advance.
Try to access to movies.actor directly
{% if movies %}
{{movies.title}}
<ul>{{ movies.actor.all.count }}
{% for actor in movies.actor.all %}
<li>{{actor.name}}</li>
{%endfor%}
</ul>
{% endif %}

Categories