Ordering Users in a Django ManyToManyField - python

I have a model that has a ManyToManyField of Users:
models.py
class Excuse(models.Model):
students = models.ManyToManyField(User)
reason = models.CharField(max_length=50)
views.py
def excuse_list(request, block_id=None):
queryset = Excuse.objects.all().prefetch_related('students')
context = {
"object_list": queryset,
}
return render(request, "excuses/excuse_list.html", context)
Template (excuse_list.html)
...
{% for excuse in object_list %}
<tr>
<td>{{ excuse.reason }}</td>
<td>
{% for student in excuse.students.all %}
{{student.get_full_name}};
{% endfor %}
</td>
</tr>
{% endfor %}
...
How can I sort the User set (excuse.students.all) alphabetically by a User field, for example: user.last_name?

you can do this just changing your views.py with this:
queryset = Excuse.objects.all().prefetch_related(
Prefetch('students', queryset=User.objects.order_by('lastname')))
remember to import User model!

You can create a method in your Excuse model, that will return a sorted students set:
def get_sorted_students(self):
return self.students.all().order_by('last_name')
And then in your template use excuse.get_sorted_students instead of excuse.students.all
Alternatively, you could use a custom template tag to render the list of students.

Related

Database contents not showing up after render

So, I try to display a list of items from database, but after calling return render(...) it behaves as if there were no objects in the database. I am new to django and after a second day of trial and error I have no idea what to do with it
Affected view:
class DropDownList(ListView):
context_object_name = 'drop_down'
template_name="browse.html"
model=ServiceList
asd = ''
def post(self, request):
name = request.POST.get('selected')
obj = ServiceList.objects.get(sourceFile=name)
print(obj)
print(request.POST) #some prints for debugging
context = {'asd': obj}
return render(request, self.template_name, context)
Models:
class ServiceList(models.Model):
version = models.IntegerField()
location = models.CharField(max_length=500)
services = models.ManyToManyField(Service)
drms = models.ManyToManyField(Drm)
sourceFile = models.CharField(max_length=500)
class Service(models.Model):
uid = models.CharField(max_length=255, unique=True)
locations = models.ManyToManyField(Location)
names = models.ManyToManyField(Name)
category = models.CharField(max_length=500)
drm = models.CharField(max_length=500)
description = models.CharField(max_length=500)
html fragment:
<p>
<br/>
<form action="/browse/" id="tableform" method="POST">{% csrf_token %}
<select class="form-select" aria-label="Default select example" form="tableform" name="selected">
<option selected>-----------------</option>
<option >One</option>
<option >Two</option>
<option >Three</option>
{% for obj in drop_down %}
<option name={{obj.sourceFile}}>{{obj.sourceFile}}</option>
{% endfor %}
</select>
</form>
<button id="add-list" type="submit" class="btn btn-success pull-left" class="button1" form="tableform">+</button>
</p>
<table class="table table-bordered table-dark">
<tr>
<th>UID</th>
<th>Category</th>
</tr>
{% for obj in drop_down %}
adasdasd
{% if obj.sourceFile == asd %}
<br/>fghfghfgh
{% for service in obj.services.all %}
<tr>
<td>{{service.uid}}</td>
<td>{{service.category}}</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
</table>
There are a few things wrong in your code, I'm going to list them, so it is easier to understand.
1 - context_object_name = 'drop_down' is not being used, 'drop_down' is not being declared in your class.
2 - You don't need to assign 'asd' as an empty variable, since you are only using as a namespace for the obj variable in your context set.
3 - Your return function is wrong, you should only be returning the context there, since you are using a class based view.
4 - You are using a ListView to get only one object
I don't know if this is going to work(I don't have a lot of experience in class based views) but try to make your code look like this:
class DropDownList(DetailView):
template_name="browse.html"
model=ServiceList
def post(self):
name = request.POST.get('selected')
obj = ServiceList.objects.get(sourceFile=name)
context = {'asd': obj}
return context
you can delete the context_object_name line as I mentioned, so if you want to render your ServiceList object version field in and HTML page for example, you should use {{ asd.version }}
Also, try to look for function based views, they are better to use, im assuming that you are using this view to get an object from your database, if you were using a function based view here, your code would be much simpler, it would look like this
def get_object(request, object_id):
template = 'browse.html'
obj = ServiceList.objects.get(pk=object_id)
context = { 'youcandecideanamehere': obj }
return render(request, template, context)
but you would need to pass the object_id parameter in your url(I'm going to create some here, since you didn't give yours)
urlpatterns = [
...
path('get_object/<object_id>/', get_objects, name='get_objects')
...
]
And the a tag for that page, should be like this:
Text
Hope I've helped, but if you still have some problems, feel free to contact me.

Accessing fields in a Queryset from a ManytoManyFIeld

Pretty new to Django and models and I am trying to make a wishlist where people can add to the wishlist an then view what is in their wishlist by clicking on the link to wishlist. I created a separate model for the wishlist that has a foreignfield of the user and then a many to many field for the items they want to add to the wish list. For right now i am trying to view the wishlist that i created for the user using the admin view in Django.
The problem that i am having is that when I try to print their wishlist to the template the below comes up on the page.
<QuerySet [<listings: item: Nimbus, description:this is the nimbus something at a price of 300 and url optional(https://www.google.com/ with a category of )>, <listings: item: broom, description:this is a broom stick at a price of 223 and url optional(www.youtube.com with a category of broom)>, <listings: item: wand, description:this is a wand at a price of 3020 and url optional(www.twitter.com with a category of sales)>]>
What i ideally want is the query set to be split such that the two listings and their information would be on seperate lines on the the html page when i iterate through then the items based on the user. I know it must be from the string representation that i have set up on the model itself but don't know how to manage to accomplish this. I trie .all,.values,.values_list,.prefetch_related, and i still get the same outcome when i go to page and or not iterable
if anything what would be nice is to have access to the the item,description, and price and print that onto the page for every item in the wishlist.
is this possible to do or is my approach wrong and should the wishlist be added to one of the other forms but i think this should work somehow. Don't know if i am close or there is something i am missing in one of my files or need to create a new separate view.
code below:
models.py
class User(AbstractUser):
pass
class listings(models.Model):
item = models.CharField(max_length=100)
description = models.CharField(max_length=100)
price = models.IntegerField()
url = models.CharField(max_length=100,blank=True)
category = models.CharField(max_length=100,blank=True)
def __str__(self):
return f"item: {self.item}, description:{self.description} at a price of {self.price} and url optional({self.url} with a category of {self.category})"
class bids(models.Model):
desired = models.ForeignKey(listings,on_delete=models.CASCADE,related_name="desired",null=True)
bid = models.IntegerField()
def __str__(self):
return f"{self.desired} Bid: {self.bid}"
class comments(models.Model):
information = models.ForeignKey(listings,on_delete=models.CASCADE, related_name ="Review",null=True)
title = models.CharField(max_length=100)
comment = models.CharField(max_length=100)
def __str__(self):
return f"title {self.title} Comment: {self.comment}"
class wl(models.Model):
user = models.ForeignKey(User ,on_delete=models.CASCADE, related_name="users")
product = models.ManyToManyField(listings, related_name="item_wished")
def __str__(self):
return f"{self.product.all()}"
views.py
def watch_list(request):
user = wl.objects.filter(user_id = request.user.id).prefetch_related('product')
return render(request, 'auctions/wishlist.html',{
'wishes': user
})
html template
{% extends "auctions/layout.html" %}
{% block body %}
<h2>Active Listings</h2>
{% for i in wishes.all %}
<li> {{ i }} <li>
{% endfor %}
{% endblock %}
Your code seems fine. Try tweaking your template a bit with structure, maybe an HTML table:
{% extends "auctions/layout.html" %}
{% block body %}
<h2>Active Listings</h2>
<table>
{% for w in wishes %}
<tr>
<td>{{ w.pk }}</td>
<td>{{ w.product.item }}</td>
<td>{{ w.product.description }}</td>
<td>{{ w.product.price }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
The query would return all wl objects. With:
{% for w in wishes %}
You iterate over all wl objects. Each "w" has "product" field which is a Many to many relation - it contains multiple objects and needs to be iterated again:
{% for each_wl in wishes %}
{% for each_product in each_wl %}
{{ each_product.item }}
Also, name your variables/Classes to something more verbose.

Retrieve data from database without using extra page

In my 'django' project, I need to retrieve / insert data from multiple tables of my (mysql)database. However because of the code I used, I had to use an extra .html page for each table operation. I want to be able to access multiple tables on a single html page. In order to better explain the situation, I write the relevant codes of the project below.
Project/views.py
def home_view(request, *args, **kwargs):
print(args, kwargs)
print(request.user)
return render(request, "home.html", {})
#login_required()
def admin_view(request, *args, **kwargs):
print(args, kwargs)
print(request.user)
return render(request, "adminPage.html", {})
#login_required()
def doctor_view(request):
return render(request, 'doctorPage.html', {'doctors': doctor_view()})
appointments/views.py
from django.views.generic import DetailView, ListView
class list_of_appointments(ListView):
model = Appointment
template_name = 'appointments/appointment_list.html'
class list_of_patients(ListView):
model = Patient
template_name = 'appointments/patient_list.html'
appointments/urls.py
urlpatterns=[
url(r'^appointment_list/$', list_of_appointments.as_view(), name='list1'),
url(r'^patient_list/$', list_of_patients.as_view(), name='list2')
]
So, in order to access the operations related to the tables, I have to use the following url code.
<a href={% url 'appointments:list2' %}>
Therefore, I can create a second html file and extract the data I want to extract from the database with this method.
{% for appointment in object_list %}
<tr>
<td>{{ appointment.patient.name }}</td>
<td>{{ appointment.doctor.name }}</td>
<td>{{ appointment.Date }}</td>
<td>{{ appointment.time }}</td>
<td>{{ appointment.province }}</td>
<td><a href="#">
<button type="button" class="btn btn-default"><span class="glyphicon glyphicon-pencil"
aria-hidden="true"></span>Edit
</button>
</a></td>
</tr>
{% endfor %}
But I want to do this database interaction on an existing html link (eg adminPage) without going to another link. Nowhere could I find out how to do this, can you help me? Thank you all!
If you want pass more than one model to ListView, you can override get_context_data method of MultipleObjectTemplateResponseMixin. But be carefull, it should be a QuerySet object. The example look like this:
views.py
from django.views.generic.list import ListView
from .models import FirstModel, SecondModel, ThirdModel
class MyListView(ListView):
template_name = 'my_app/what-ever.html'
model = Article
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_first_model'] = FirstModel.objects.all()
context['my_second_model'] = SecondModel.objects.all()
context['my_third_model'] = ThirdModel.objects.all()
return context
what-ever.html
{% for object in my_first_model %}
<h1>{{ object.title }}</h1>
<p>{{ object.pk }}</p>
{% endfor %}
You can find more information here.

How to reduce SQL queries in Django using prefetch_related?

I am trying to optimize a Django project (vers. 1.8.6) in which each page shows 100 companies and their data at once. I noticed that an unnecessary amount of SQL queries (especially with contact.get_order_count) are performed within the index.html snippet below:
index.html:
{% for company in company_list %}
<tr>
<td>{{ company.name }}</td>
<td>{{ company.get_order_count }}</td>
<td>{{ company.get_order_sum|floatformat:2 }}</td>
<td><input type="checkbox" name="select{{company.pk}}" id=""></td>
</tr>
{% for contact in company.contacts.all %}
<tr>
<td> </td>
<td>{{ contact.first_name }} {{ contact.last_name }}</td>
<td>Orders: {{ contact.get_order_count }}</td>
<td></td>
</tr>
{% endfor %}
{% endfor %}
The problem seems to lie in constant SQL queries to other tables using foreign keys. I looked up how to solve this and found out that prefetch_related() seems to be the solution. However, I keep getting a TemplateSyntaxError about being unable the parse the prefetch, no matter what parameter I use. What is the proper prefetch syntax, or is there any other way to optimize this that I missed?
I've included relevant snippets of model.py below in case it's relevant. I got prefetch_related to work in the defined methods, but it doesn't change the performance or query amount.
model.py:
class Company(models.Model):
name = models.CharField(max_length=150)
def get_order_count(self):
return self.orders.count()
def get_order_sum(self):
return self.orders.aggregate(Sum('total'))['total__sum']
class Contact(models.Model):
company = models.ForeignKey(
Company, related_name="contacts", on_delete=models.PROTECT)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150, blank=True)
def get_order_count(self):
return self.orders.count()
class Order(models.Model):
company = models.ForeignKey(Company, related_name="orders")
contact = models.ForeignKey(Contact, related_name="orders")
total = models.DecimalField(max_digits=18, decimal_places=9)
def __str__(self):
return "%s" % self.order_number
EDIT:
The view is a ListView and defines the company_list as model = Company. I altered the view based on given suggestions:
class IndexView(ListView):
template_name = "mailer/index.html"
model = Company
contacts = Contact.objects.annotate(order_count=Count('orders'))
contact_list = Company.objects.all().prefetch_related(Prefetch('contacts', queryset=contacts))
paginate_by = 100
Calling the get_order_count and get_order_sum methods causes one query every time the method is called. You can avoid this by annotating the queryset.
from django.db.models import Count, Sum
contacts = Contact.objects.annotate(order_count=Count('orders'), order_sum=Sum('orders'))
You then need to use a Prefetch object to tell Django to use your annotated queryset.
contact_list = Company.objects.all().prefetch_related(Prefetch("contacts", queryset=contacts)
Note that you need to add the prefetch_related to your queryset in the view, it is not possible to call it in the template.
Since you are using ListView, you should be overriding the get_queryset method, and calling prefetch_related() there:
class IndexView(ListView):
template_name = "mailer/index.html"
model = Company
paginate_by = 100
def get_queryset(self):
# Annotate the contacts with the order counts and sums
contacts = Contact.objects.annotate(order_count=Count('orders')
queryset = super(IndexView, self).get_queryset()
# Annotate the companies with order_count and order_sum
queryset = queryset.annotate(order_count=Count('orders'), order_sum=Sum('orders'))
# Prefetch the related contacts. Use the annotated queryset from before
queryset = queryset.prefetch_related(Prefetch('contacts', queryset=contacts))
return queryset
Then in your template, you should use {{ contact.order_count }} instead of {{ contact.get_order_count }}, and {{ company.order_count }} instead of {{ company.get_order_count }}.
Try this in views.py
company_list = Company.objects.all().prefetch_related("order", "contacts")

Django form field issue with model formset

I'm having an issue with rendering individual form fields in a template. I have a model formset that I'm re-ordering after creation to make displaying a little easier on the template. Nothing too complicated, but rendering the form fields isn't working. You can see in the template where I try and render {{ form.train }}, but nothing shows up in the output. However, the form is definitely there because {{ form.instance.user.name }} works correctly.
I opened up PDB and inspected the form variable that I'm adding into the dictionary, and it says <django.forms.widgets.ScheduleForm object at 0x10c58bc50>. I'm not sure if that helps or not, but I wanted to provide as much info as possible.
The Model Form
class ScheduleForm(ModelForm):
class Meta:
model = models.Schedule
fields = [
'train',
'semi',
'tri_axle',
'flow_boy',
'misc',
'material',
'notes'
]
views.py
formset_fields = ('train','semi','tri_axle','flow_boy','misc','material','notes')
ScheduleFormSet = modelformset_factory(models.Schedule, fields=formset_fields, extra=0)
formset = ScheduleFormSet(queryset=queryset)
# Getting form in the right format
ordered_forms = {}
for form in formset:
# Make sure the job exists on the object
if not form.instance.job.number in ordered_forms:
ordered_forms[form.instance.job.number] = {}
# Make sure the user exists on the object
if not form.instance.user.name in ordered_forms[form.instance.job.number]:
ordered_forms[form.instance.job.number][form.instance.user.name] = []
# Append to correct place.
ordered_forms[form.instance.job.number][form.instance.user.name].append(form)
# Dict will look like
# { 'jobID' : { 'user' : [form1,form2,form3] } }
Template
{% for job, users in ordered_forms.items %}
<h2>{{ job }}</h2>
{% for user, forms in users %}
<table class='table striped'>
<thead>
<tr>
<th>{{ user }}</th>
<th>Train</th>
<th>Semi</th>
<th>Tri-Axle</th>
<th>Flow Boy</th>
<th>Misc</th>
<th>Material</th>
<th>Notes</th>
<th></th>
</tr>
</thead>
<tbody>
{% for form in forms %}
<tr>
<td>{{ form.instance.broker.name }}</td>
<td>{{ form.train }}</td>
<td>Semi</td>
<td>Tri-Axle</td>
<td>Flow Boy</td>
<td>Misc</td>
<td>Material</td>
<td>Notes</td>
<td></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}
{% endfor %}
Turns out I forgot to add .items to {% for user, forms in users %}.
{% for job, users in ordered_forms.items %}
<h2>{{ job }}</h2>
{% for user, forms in users.items %}
<table class='table striped'>
....
{% endfor %}
{% endfor %}

Categories

Resources