Does not work to use _set on foreignkey - python

I've read in the docs and stackoverflow but i cant figure out why my foreign key following isnt working, django just says that there is no such attribute as vote_story_set.all().
I have a table named story and one named vote_story and the story table has several foreign keys like a foreign to vote_story to get the number of votes for a news story.
According to the docs and the detailed answer here: *_set attributes on Django Models i created an object like this:
all_stories = Story.objects.all()
votes = all_stories.vote_story_set.all()
but this doesnt work since django says that there is no such attribute as "vote_story_set". The database table for story has the 'votes' attribute as a foreign key to the table Votes_story. From the examples ive seen this should be working so i dont understand why it doesnt. There is no foreign keys in the Votes_story table, just a primay key and the attribute 'votes' containing the number of votes.
Update:
Models and template is shown below for Story and Vote_story as well as the relevant view.
Models:
class Story(models.Model):
story_id = models.IntegerField(primary_key=True)
date_added = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
title = models.CharField(max_length=150)
description = models.CharField(blank=True, null=True, max_length=2000)
story_text = models.TextField()
picture = models.ImageField(blank=True, null=True, upload_to="story/images")
storyAccessLevelID = models.ForeignKey(StoryAccessLevel)
categoryID = models.ForeignKey(Category)
votes = models.ForeignKey(Vote_story, blank=True, null=True)
comments = models.ForeignKey(Comment, blank=True, null=True)
def __unicode__(self):
return self.title
class Vote_story(models.Model):
votes = models.IntegerField()
def __unicode__(self):
return self.votes
In the file Vote_story is defined above Story so it can find it.
In Vote_story i let django create the primary key automatically unlike Story.
There is currently one vote for one of the stories added.
Template code (the relevant portion):
<table class="table table-hover">
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Date added</th>
<th>Votes</th>
<th>Comments</th>
</tr>
</thead>
<tbody>
{% for story in all_stories %}
<tr>
<td>{{ story.title }}</td>
<td>{{ story.description }}</td>
<td>{{ story.date_added }}</td>
<td>{{ story.votes }}</td>
<td>{{ story.comments }}</td>
</tr>
{% endfor %}
</tbody>
</table>
The view is like this:
def list_all_stories(request):
""" Show all stories """
all_stories = Story.objects.all()
return render(request, "base/story/all_stories.html", {'all_stories': all_stories})

all_stories is a queryset, not an instance. vote_story_set is an attribute on each instance within the queryset, not the queryset itself.
Plus, you seem to be confused about the direction of relations. If your ForeignKey goes from Vote to VoteStory, you don't need the reverse relation, you need the forward one, which is just whatever you called the field. Again, though, this is an attribute of each instance, not the queryset.

Related

Django - Join two table in view and use it in template

I'm using python 3.4.5 with Diango 2.0.2
I have 2 tables with a foreign key. For some reasons, I need to use 2 tables to store these data.
I want to generate a table which contain the following information.
program.name , program.program_id , program.filter(user=request.user.id).status(if exist, if not exist, keep it NULL)
models.py
class Program(models.Model):
name = models.CharField(max_length=128, null=True, blank=True, default=None)
program_id = models.AutoField(primary_key=True)
class ProgramInfo(models.Model):
pid = models.ForeignKey(Program, on_delete=models.CASCADE, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
status = models.CharField(max_length=10, default=1003)
views.py
def program_list(request):
program = Program.objects.all()
return render(request, 'panel/program_list.html', {'program': program})
template
{% for prog in program %}
<tr>
<td> {{ prog.name }} </td>
<td> {{ prog.program_id }} </td>
<td> {{ "program's status of existing user" }} </td>
</tr>
{% endfor %}
How to generate a table which contain the following information?
program.name , program.program_id , program.filter(user=request.user.id).status
I have 2 solutions,
(1) generate a table and pass it to template
(2) send the program table to template, and use something like program.filter(user=request.user.id).status in "td"
But I don't know how to do it for these 2 solutions.
The table should be like this
NameA, 123, yes
NameB, 234,
NameC, 345, no
NameD, 456,
NameE, 567, no
ProgramInfo has a ForeignKey field pointing to a Program instance
pid = models.ForeignKey(Program, on_delete=models.CASCADE, null=True)
Thus, you can have multiple ProgramInfo instances linked to a single Program instance. To retrieve that list, simply use:
program_instance = Program.objects.get(id=123)
# gives you the list of ProgramInfo instances:
programinfo_list = program_instance.programinfo_set
You can find more information about Many to One relationships and Related objects in Django doc. FYI, instance.programinfo_set is a RelatedManager (a child class of the more generic Manager). Thus, it provides an interface to lookup instances contained in this relationship.
If you need to have only 1 to 1 relationship between your models, use a OneToOne field instead:
pid = models.OneToOneField(Program, on_delete=models.CASCADE, null=True)

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")

Ordering queryset client-side, Django + TwitterBoostrap

I have the following:
models.py
class Person(models.Model):
full_name = models.CharField(...)
address = models.CharField(...)
views.py
def all_persons(request):
persons = Person.objects.all()
return render(
request,
'my_template.html',
{
'persons':persons
}
)
my_template.html
<table class="table table-striped">
<thead>
<tr>
<th>Person's Fullname</th>
</tr>
</thead>
<tbody>
{% for person in persons %}
<td>{{ person.full_name }}</td>
<td>{{ person.address }}</td>
Ok, so the code as it is will display an unordered list of persons in the template, of course I can manage to order by Person's name or address, I want to give the user the possibility to order by any field, I want to accomplish this with a button on each column of the table in the template. For example a button on top of full_name and a button on top of address, if any of those is pressed, then the list should be ordered depending on the pressed button.
Thank you very much for reading, hope you can help me.
Edit: I use Twitter Boostrap 2, so tablesorter and DataTables JS wont work.
Check out this JS plugin for HTML tables: Datatables. Comes with sorting, filtering, pagination, lots of plugins. Easy to get started, but you might consider it a bit bloated.
Another way is to have the view take an extra argument (an integer representing a field, for example), sort the queryset by this field and render the template.

How do I get a cascading drop down selection box with my foreign keys in Django?

So I have a model called Car with a foreign key of a Manufacturer model. I also have a CarCharacterisitcs model with a foreign key of Car.
This is what the code looks like:
class Car(models.Model):
idcar = models.AutoField(primary_key=True)
manufacturer = models.ForeignKey(Manufacturer, null=True, blank=True, on_delete=models.SET_NULL)
name = models.CharField(max_length=765)
class Manufacturer(models.Model):
idmanufacturer = models.AutoField(primary_key=True)
name = models.CharField(max_length=765)
class CarCharacteristics(models.Model):
idcar_characteristics = models.AutoField(primary_key=True)
car = models.ForeignKey(Car, on_delete=models.PROTECT)
I am making a page that lets you add CarCharacteristics.
This is my form and view:
class CarCharacteristicsForm(ModelForm):
class Meta:
model = CarCharacterisitics
def add_car_characteristics(request):
if request.method == 'POST':
car_char_form = CarCharacterisiticsForm(request.POST)
if car_char.is_valid(:
car_char_form.save()
return HttpResponseRedirect('/cars/car-characterisitcs')
else:
car_char_form = CarCharacterisiticsForm()
return render(request, 'car_characteristics/add_car_characteristics.html',{'car_char_form': car_char_form,})
In my html page, I have this:
<h1>Car Characteristics</h1>
<form action="." method="POST">
{% csrf_token %}
<table id="characteristics_table">
<tr>
<td>
<table id="car_characterisitcs_table">
<td>{{ char_char_form.as_table }}</td>
</table>
</td>
</tr>
</table>
<p><input type="submit" value="Submit"></p>
</form>
When this form is displayed, I have a drop down select field with all my possible Car models. What I want is to have two select fields. I want the first to let you be able to select the Manufacturer and then the second to display all the possible Cars that have that manufacturer. Then when you submit the form, it assigns the Car you selected to the Foreign Key of the CarCharacterisitcs model.
If you go look at Advance Auto Parts, when you click "Your Vehicle", that is what I want to have. How do I do that?
You need to use AJAX to fetch the data for a selected manufacturer and populate the car field accordingly. This needs to be done inside the view code and return the car fetched data in to the template and use the template language to populate the field.

Showing the Foreign Key value in Django template

Here is my issue. I am new to python/django (about 2 months in). I have 2 tables, Project and Status. I have a foreign key pointing from status to project, and I am looking to try to display the value of the foreign key (status) on my project template, instead of the address of the foreign key.
Here is my models.py
from django.db import models
from clients.models import Clients
from django.contrib.auth.models import User
from settings import STATUS_CHOICES
from django.db import models
from clients.models import Clients
from django.contrib.auth.models import User
from settings import STATUS_CHOICES
class Project(models.Model):
client = models.ForeignKey(Clients, related_name='projects')
created_by = models.ForeignKey(User, related_name='created_by')
#general information
proj_name = models.CharField(max_length=255, verbose_name='Project Name')
pre_quote = models.CharField(max_length=3,default='10-')
quote = models.IntegerField(max_length=10, verbose_name='Quote #', unique=True)
desc = models.TextField(verbose_name='Description')
starts_on = models.DateField(verbose_name='Start Date')
completed_on = models.DateField(verbose_name='Finished On')
def __unicode__(self):
return u'%s' % (self.proj_name)
class Status(models.Model):
project = models.ForeignKey(Project, related_name='status')
value = models.CharField(max_length=20, choices=STATUS_CHOICES, verbose_name='Status')
date_created= models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.value
class Meta:
verbose_name = ('Status')
verbose_name_plural = ("Status")
My views.py
#login_required
def addProject(request):
if request.method == 'POST':
form = AddSingleProjectForm(request.POST)
if form.is_valid():
project = form.save(commit=False)
project.created_by = request.user
project.save()
project.status.create(
value = form.cleaned_data.get('status', None)
)
return HttpResponseRedirect('/project/')
else:
form = AddSingleProjectForm()
return render_to_response('project/addProject.html', {
'form': form, 'user':request.user}, context_instance=RequestContext(request))
And finally my template:
{% if project_list %}
<table id="plist">
<tr id="plist">
<th>Quote #</th>
<th>Customer</th>
<th>Date</th>
<th>Project Name</th>
<th>Status</th>
<th>Contact</th>
</tr id="plist">
{% for p in project_list %}
<tr id="plist">
<td>{{ p.pre_quote }}{{ p.quote }}</td>
<td>{{ p.client }}</td>
<td>{{ p.starts_on }}</td>
<td>{{ p.proj_name }}</td>
<td>{{ p.status_set.select_related }}</td>
<td>{{ p.created_by }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<p>No projects available.</p>
{% endif %}
Any help would be much appreciated. Thank you!
I'm guessing you mean here:
<td>{{ p.status_set.select_related }}</td>
This doesn't do anything. select_related is an optimisation feature, it has nothing to do with actually getting or displaying the related content. If you want to do that, you will have to iterate through the result of p.status_set.all.
In your model, you have defined the related name for this ForeignKey as "status." Thus, you can now use "status" as this name instead of the "_set" business.
Since this is a ForeignKey (ManyToOne) field, you can't simply display the field as if there were only one value. Instead you need .all, which will return a queryset of all the statuses that point to the object in question. You can then iterate through these.
If instead, you know that each project will have only one status, you can use a OneToOne field instead of a ForeignKey.

Categories

Resources