Django accessing Model with property decorator - python

How do I access the data in the method with the property decorator. I can access it in the shell, and it is what I need, but it comes up blank in my site.
Models.py:
class Project(models.Model):
date_published = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
area = models.ForeignKey(Area, on_delete=models.PROTECT)
title = models.CharField(max_length=128, unique=True)
summary = models.CharField(max_length=256)
others = models.CharField(max_length=128, blank=True)
deadline = models.DateField(null=True, blank=True)
priority = models.ForeignKey(Priority, on_delete=models.PROTECT)
closed = models.DateTimeField(null=True, blank=True)
#property
def updates(self):
updates = []
categories = set(self.update_set.all().values_list(
'category__id', flat=True))
for cat_id in categories:
updates.append(Update.objects.filter(
project=self, category__id=cat_id).order_by('added').last())
return updates
def __str__(self):
return self.title
Views.py:
class ProjectView(ListView):
template_name = 'project_portal/home.html'
queryset = Project.objects.all()
And I am trying to use the following in my template:
<div class="box5">
{% for item in object_list %}
<table>
<tr>
<td>{{ item.updates }}</td>
</tr>
</table>
{% endfor %}
</div>
So far the box is blank. However, I have managed to get this data in the Django shell with the following:
p = Project.objects.all()[0]
p.updates
This returns the correct data in the right order for the first project. What do I need to do to get it to appear in the site?

It looks like your problem is that you are using .values() in your queryset in your view. This will give you a list. The reason why it works in the shell is because you are working with model objects, and hence you are able to get the updates property. So remove .values() from queryset = Project.objects.all().values()
Read up more about .values() here.

Are you sure the template has a var named project? Try changing project for object_list or override the get_context_data method to pass project to the template.
Edit: After reading your edited question I think you want to do something like this.
<div class="box5">
{% for project in object_list %}
<table>
<tr>
{% for category_update in project.updates %}
<td>{{ category_update }}</td>
{% endfor %}
</tr>
</table>
{% endfor %}
</div>
If you keep watching the category update in a wrong way please share with us your Category Model.

So this is now solved. The Models.py and Views.py in the original post are correct now, what was causing the rest of the issue was the Template, here is a short version of what was needed:
{% block home %}
{% if object_list %}
{% for project in project_list %}
<div class="box1">
<h4>{{ project.title }}</h4>
<p>Project Status</p>
<div class="square"></div>
</div>
<div class="box5">
{% for item in project.updates %}
<table>
<tr>
<td>{{ item }}</td>
</tr>
</table>
{% endfor %}
</div>
{% endfor %}
{% endif %}
The point I am making here is that there are two loops, one loops through projects in the object_list, and the second loop loops through items within the project.updates, which is fed by the method with the property decorator. I hope this is clear if anyone finds this looking for help.

Related

Is there a way to isolate a foreign key from its related model using django?

I am building a django website that displays the prices of items in a shop. Each item belongs to a category so I mande category a foreign key that can have one or more items.
`class Category(models.Model):
category = models.CharField(max_length=64)
def __str__(self):
return self.category
class ShopItem(models.Model):
itemName = models.CharField(max_length=64)
price = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.itemName`
However, in the html, I am unable to obtain both the category and the shop item
I tried the following...
`---snip---
{% used_category = [] %}
{% for item in shopitem_list %}
{% if item.category not in used_category %}
<tr>
<td>item.category</td>
<td></td>
<td></td>
</tr>
<tr>
{% for rel_item in shopitem_list %}
{% if rel_item.category == item.category %}
<td></td>
<td>rel_item.itemName</td>
<td>rel_item.category</td>
{% endif %}
{% enfor %}
</tr>
{% endif %}
{% endfor %}`
I was hoping this would help me create a table where all the items are sorted below their respective categories but I got an error instead:
TemplateSyntaxError at /
Invalid block tag on line 18: 'used_category', expected 'endblock'. Did you forget to register or load this tag?
You need to create a view function which will pass the required Models information to the Template, thus completing the Model - View - Template (MVT) method which django runs on.
You can't create and update a list within the template, but you can prepare the data in the view function, then iterate over your items differently.
Start by making sure the categories are in your template context. To avoid n+1 queries, prefetch the related items also. Let's say your view looks like this (updated):
from django.shortcuts import render
from .models import Category
def items_list(request):
context = {
"categories": Category.objects.exclude(shop_items=None).prefetch_related(
"shop_items"
)
}
return render(request, "my_template.html", context)
Then in the template iterate over the categories:
{% for category in categories %}
<tr>
<td colspan="3">{{ category.category }}</td>
</tr>
<tr>
{% for item in category.shop_items %}
<td></td>
<td>{{ item.itemName }}</td>
<td>{{ item.price }}</td>
{% enfor %}
</tr>
{% endfor %}
NB The related name shop_items may not work here. To make sure it does, you can manually set the related_name on your foreign key. At the same time I would suggest changing itemName to just name:
class ShopItem(models.Model):
name = models.CharField(max_length=64)
price = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="shop_items")
def __str__(self):
return self.name

Why the ListView not showing data in the template?

I'm working on my first Django project (the final project for codecademy's Django class) and I'm making webpages to show the inventory and menu that a restaurant has. I made the model, view, template, etc. for inventory and it displays the ListView perfectly. I did the same thing for my menu and it doesn't work. The page loads but the table that's supposed to output data is empty.
Any insight on what might be going wrong?
PS I'm new to programming and this is my first stackoverflow post so forgive any formatting errors or other faux pas
## views.py
from django.http import HttpResponse
from django.shortcuts import render
from .models import Inventory, MenuItem, RecipeRequirement, Purchase
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.views.generic import ListView
# Create your views here.
def index(request):
return render(request, "index.html")
class InventoryList(ListView):
template_name = "inventory.html"
model = Inventory
class MenuList(ListView):
template_name = "menu.html"
model = MenuItem
Inventory (below) works fine! :)
{% extends './base.html' %}
{% block content %}
<h2>Inventory</h2>
<table id="inventory">
<tr>
<th>Ingredient</th>
<th>Price</th>
<th>Units Available</th>
</tr>
{% for ingredient in inventory_list %}
<tr>
<tr>
<td>{{ ingredient.ingredient_name }}</td>
<td>{{ ingredient.price }}</td>
<td>{{ ingredient.units_avail }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
This one (Menu) is the problematic one :(
{% extends './base.html' %}
{% block content %}
<h2>Menu</h2>
<table id="menu">
<tr>
<th>Item</th>
<th>Price</th>
<th>In Stock?</th>
</tr>
{% for food in menu_list %}
<tr>
<tr>
<td>{{ food.menu_item_name }}</td>
<td>{{ food.price }}</td>
<td>{{ food.available }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Models below
from django.db import models
from django.forms import DateTimeField
# Create your models here.
class Inventory(models.Model):
ingredient_name = models.CharField(max_length=30)
price = models.DecimalField(max_digits=5, decimal_places=2)
units_avail = models.IntegerField()
def __str__(self):
return self.ingredient_name + " avail: " + str(self.units_avail)
class MenuItem(models.Model):
menu_item_name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=5, decimal_places=2)
def __str__(self):
return self.menu_item_name + " with Price: " + str(self.price)
def available(self):
return all(recipe_req.enough() for recipe_req in self.reciperequirement_set.all())
class RecipeRequirement(models.Model):
ingredient = models.ForeignKey(Inventory, on_delete=models.CASCADE)
menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
quantity = models.IntegerField()
def __str__(self):
return self.ingredient.ingredient_name + " in " + self.menu_item.menu_item_name
def enough(self):
return self.quantity <= self.ingredient.units_avail
class Purchase(models.Model):
menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
timestamp = models.DateTimeField()
def __str__(self):
return self.menu_item.menu_item_name + " at " + self.timestamp
URLs below
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("inventory", views.InventoryList.as_view(), name="inventory_list"),
path("menu", views.MenuList.as_view(), name="menu_list"),
]
I could see one problem, you have one extra <tr> tag open in both the templates just below the loop.
Also think some problem with available method you manually defined in MenuItem model, so I removed it, try without it.
Also I'd recommend you to use context_object_name which is by default object_list so:
views.py
class InventoryList(ListView):
template_name = "inventory.html"
model = Inventory
context_object_name="inventories"
class MenuList(ListView):
template_name = "menu.html"
model = MenuItem
context_object_name="menu_list"
inventory.html template
{% extends './base.html' %}
{% block content %}
<h2>Inventory</h2>
<table id="inventory">
<tr>
<th>Ingredient</th>
<th>Price</th>
<th>Units Available</th>
</tr>
{% for ingredient in inventories %}
<tr>
<td>{{ ingredient.ingredient_name }}</td>
<td>{{ ingredient.price }}</td>
<td>{{ ingredient.units_avail }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
menu.html template
{% extends './base.html' %}
{% block content %}
<h2>Menu</h2>
<table id="menu">
<tr>
<th>Item</th>
<th>Price</th>
<th>In Stock?</th>
</tr>
{% for food in menu_list %}
<tr>
<td>{{ food.menu_item_name }}</td>
<td>{{ food.price }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Note: It's recommended to give / at the end of every route so your urls.py should be:
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("inventory/", views.InventoryList.as_view(), name="inventory_list"),
path("menu/", views.MenuList.as_view(), name="menu_list"),
]
Note: class based views in Django requires their name to be written as model name as prefix and actual view name as suffix, so you may change it to InventoryListView and MenuItemListView from InventoryList and MenuList respectively.
I notice that the RecipeRequirement model Class has the method available. This method calls the method enough from the RecipeRequirement model Class.
One troubleshooting strategy is to disable (comment out) the methods enough and available as they are introducing logic that would prevent display of menu items. In other words, your code appears to read: If we don't have enough ingredients there is no point returning menu items for recipes that we cannot make.
An alternative troubleshooting strategy is to populate the RecipeRequirement table with sufficient ingredients and it should work. Caveat: This answer is untested.
Here's a tip for a newcomer to coding that has served me well in my brief journey into software development. Errors have been my greatest teacher. When there are errors try to reduce as many elements as you can. When you hit a wall like this consider what is essential and comment out the rest. I suggested commenting out the two methods first because they are not required for the tables to display (or for what you report as being central to your question). Once you get the menu items displaying then you can re-introduce those methods one at time.

Get the value in the intermediate model in a Through Relationship

As I loop through a Prize.objects.all() set, I am trying to get the quantity value from PrizeItem, but it seems to reference only the GameItem model, not giving access to the intermediate PrizeItem attributes.
How do I get access to the Through model's attributes?
MODELS:
class GameItem(models.Model):
'...'
class Prize(models.Model):
'...'
item = models.ManyToManyField(GameItem, through='PrizeItem')
class PrizeItem(models.Model):
#relations
game_item = models.ForeignKey(GameItem, on_delete=models.CASCADE)
prize = models.ForeignKey(Prize, on_delete=models.CASCADE)
#Item details
quantity = models.IntegerField(null=True, blank=True, default=1)
VIEWS:
def gameprizes(request):
prizes=Prize.objects.all()
context={'prizes':prizes}
return render(request, "the-app/game-prizes.html", context)
TEMPLATE:
{% if prize.item.all %}
<table class="table table-condensed">
<tbody>
{% for prize_item in prize.item.all %}
<tr>
<td>{{ prize_item.type }}</td><td>{{ prize_item.name }} {{ prize_item.quantity }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
It gets a little ugly when it comes to intermediate models in querysets. One way you can address this would be:
class Prize(models.Model):
'...'
item = models.ManyToManyField(GameItem, through='PrizeItem')
def quantity(self):
return self.item.quantity
Note that this does lead to additional queries, and you can use prefetch_related in your query to optimize
Another option would be to use the template
For game item, you would be doing something like:
class GameItem(models.Model):
'...'
def quantity(self):
return self.prizeitem_set.first()
What the hell with the models?
~> Use the #karthikr answer
Just prepare in views.py (overwrite query_set)
Use (_set ~ join) in html
Have a nice day, don't make it hard, give the hardest part for the strongest (server - database)

Django View check second model for value

I'm somewhat new to Django so please bear with me here. I have a three models in my app, Company, Articles, and Transcripts. Both Articles and Transcripts have a foreignkey() field that links to the Company model. In my main view I list all of the companies, but I would like to indicate if there is an article or transcript for each of those companies.
Here are the models:
class Company(models.Model):
stock_symbol = models.CharField(max_length=5, unique=False)
company_name = models.CharField(max_length=200)
address = models.CharField(max_length=300, blank=True)
website = models.URLField(max_length=200, blank=True)
following = models.BooleanField()
class Articles(models.Model):
company = models.ForeignKey('Company', related_name='articles')
headline = models.CharField(max_length=200)
url = models.URLField(max_length=300, blank=True)
class Transcripts(models.Model):
company = models.ForeignKey('Company', related_name='transcripts')
headline = models.CharField(max_length=200)
url = models.URLField(max_length=300, blank=True)
Below is a view for a single company, however since I'm returning all companies I obviously can't use a primarykey. How would I go about getting this info for each of the companies? Am I setting up my models the right way to do this?
class CompanyDetails(generic.DetailView):
model = Company
template_name = 'company_details.html'
def get_context_data(self, **kwargs):
pk = self.kwargs.get('pk')
context = super(CompanyDetails, self).get_context_data(**kwargs)
context['company'] = Company.objects.filter(id=pk)
context['articles'] = Articles.objects.filter(company_id=pk).order_by('-date')
context['transcripts'] = Transcripts.objects.filter(company_id=pk).order_by('-date')
return context
UPDATE
How do I update the template if I want to show in a column that there is at least one article tied to each company? When I tried the for loop in the answer it created a column for every article, so I tried changing it to the below but it every records shows up as "Blank".
<tbody>
{% for company in all_companies %}
<tr>
<td nowrap="true">{{ company.company_name }}</td>
<td nowrap="true">{{ company.stock_symbol }}</td>
{% if company.following %}
<td align="center"><span class="glyphicon glyphicon-star"></span></td>
{% else %}
<td></td>
{% endif %}
<!--HAS ARTICLE(S) FLAG -->
{% if article in company.articles.all %}
<td align="center"><span class="glyphicon glyphicon-list-alt"></span></td>
{% else %}
<td>Blank</td>
{% endif %}
<td nowrap="true">{{ company.website }}</td>
<td nowrap="true">{{ company.address }}</td>
</tr>
{% endfor %}
</tbody>
In the template, you can follow the foreign key backwards from a company to its articles or transcripts.
{{ company }}
{% for company in company.articles.all %}
{{ article }}
{% endfor %}
If you use this in your detail view, you don't have to set articles or transcripts in the get_context_data method. You shouldn't set context['company'] at all - the detail view does this for you.
In the list view, you can loop through the companies and access the articles and transcripts in the same way.
{% for company in company_list %}
{{ company }}
{% for company in company.articles.all %}
{{ article }}
{% endfor %}
{% endfor %}
This will cause a query for every single company in the queryset. You can prevent this by using prefetch_related.
Company.objects.prefetch_related('articles', 'transcripts')
In your list view, you can use prefetch_related by setting queryset:
class CompanyList(generic.ListView):
# No need to set model now that you have set queryset
queryset = Company.objects.prefetch_related('articles', 'transcripts')
If you just want to know whether or not there are associated articles or transactions, then you don't have to prefetch them. You can annotate the queryset instead:
from django.db.models import Count
# in your view
queryset = Company.objects.annotate(num_articles=Count('article')
Then in your template, you can access company.num_articles.
{% if company.num_articles %}
Company has articles
{% endif %}

Related Objects Reference Django

Given the following code:
Models.py
class Advertisement(models.Model):
title = models.CharField(null=True, blank=True, max_length=30)
created_by = models.ForeignKey(User)
class Gallery(models.Model):
advertisement = models.ForeignKey(Advertisement, related_name='images')
image = models.ImageField(upload_to=image_directory_path, help_text="Your ad image (Recommended size: 1024x768)")
creation_date = models.DateTimeField(editable=False, default=timezone.now)
Views.py
def do_foo(request):
search_result = Advertisement.objects.all().order_by('-creation_date')
return render(request, 'content_search.html',
{
'search_result': search_result
})
page.html
{% for ad in search_result %}
{% for ad_image in ad.gallery_set %}
<img src="{{ ad_image.image.url }}">
{% endfor %}
{% endfor %}
How do I access the backwards relation between Advertisement and Gallery? I tried ad.gallery_set and ad.images_set but I could not get the images.
I tried to follow what they say here Django Relation Objects Reference and in this topic.
Your code has related_name="images". So ad.images it is.
Edit: as shredding notes correctly, you can't use that directly if you want to loop over it, and need to add .all to get a queryset object:
{% for ad_image in ad.images.all %}
<img src="{{ ad_image.image.url }}">
{% endfor %}
Maybe related name "galleries" would be a bit more clear.

Categories