Show list of related objects in a template Django - python

I have an issue with displaying list of related articles in my Q&A DetailView.
I have a field where user can connect an article to Q&A from admin site. What I want is to display these related articles.
models.py
class QA(models.Model):
id = models.AutoField(primary_key=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL) #settings INSTALLED_APPS
title = models.CharField(max_length=750)
category = models.ForeignKey(Category, on_delete = models.CASCADE, blank=True)
related_articles = models.ManyToManyField(Article, default=None, blank=True, related_name='related_article')
slug = models.SlugField(unique=True, blank=True)
class Article(models.Model):
id = models.AutoField(primary_key=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL) #settings INSTALLED_APPS
title = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete = models.CASCADE, blank=True)
slug = models.SlugField(unique=True, blank=True)
views.py
class QADetailView(LoginRequiredMixin, DetailView):
login_url = 'login'
redirect_field_name = 'login'
template_name = 'QADetailView.html'
model = QA
def get_context_data(self, **kwargs):
categories = Category.objects.all()
related_articles = Article.objects.filter(related_article=self.kwargs['id']) #No idea what to put in filter
#related_articles = Article.objects.filter(related_article='1')
context['related_article'] = related_articles
context['categories'] = categories
return context
QADetailView.html
{% for article in related_article %}
{{article.title}}
{% endfor %}

You don' t need to inject the related articles in the template context, you can simply write in your QADetailView.html template (without any necessary edit):
{% for article in object.related_articles.all %}
{{article.title}}
{% endfor %}

Check RedWheelBorrow's solution first. If that does not work. Try the following:
There might be a better way of structuring your classes. So in Django, it is possible to simulate a hierarchy. For instance, when creating an invoice representation it would look something like this.
from django.db import models
class Invoice(models.Model):
"""Representing a invoice"""
user = models.ForeignKey(to=User, on_delete=models.PROTECT, related_name="invoices", default=1)
title = models.CharField(max_length=200)
date = models.DateField()
start_time = models.TimeField(default=time(9))
duration = models.IntegerField(default=1)
invoice_number = models.CharField(max_length=500, default=increment_invoice_number) # increment_invoice_number this a function that I will leave out of this answer
class InvoiceLine(models.Model):
"""Representing invoice lines of an invoice"""
invoice = models.ForeignKey(to=Invoice, on_delete=models.CASCADE, related_name="lines")
description = models.CharField(_("Beschrijving"), max_length=512)
quantity = models.IntegerField(_("Aantal"), blank=True, default=1)
discount = models.IntegerField(_("Korting in %"), default=0)
Please note, the invoice representation as presented here lacks some attributes to be a fully functional class in production. It still needs tax references, etc. However, it holds the solution to your problem.
The Invoice class has an attribute user that holds a ForeignKey with 'invoices' being its related name. This means it the object must be linked to a User object.
A User can have multiple Invoices. An Invoice can only be linked to one User.
It is one-to-many relationship:
User -> List[Invoice,...]
When looking at the class InvoiceLine we see a similar pattern. The attribute invoice is a ForeignKey with a link to the Invoice class and holds 'lines' as related name. This is also an one-to-many relationship.
Invoice -> List[InvoiceLine, ...]
To obtain the linked objects we can use the following code:
# obtain user
user = User.objects.get(id=<USER_ID>)
# obtain all Invoice objects linked to user
invoices = user.invoices.all()
# print all string representations of Invoice objects
print(invoices)
# obtain all InvoiceLine objects linked to invoices
for invoice in invoices:
lines = invoice.lines.all()
print(lines)
In the above example the highest object is User. A User can hold multiple Invoice objects. A Invoice object can hold multiple InvoiceLine objects. We can use the same strategy to solve your problem.
We want the following representation:
User -> List[QA, ...]
QA -> List[Article, ...]
class QA(models.Model):
id = models.AutoField(primary_key=True)
# So in your case the author is the user.
# Here you define User -> List[QA, ...]
author = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL) #settings INSTALLED_APPS
title = models.CharField(max_length=750)
category = models.ForeignKey(Category, on_delete = models.CASCADE, blank=True)
slug = models.SlugField(unique=True, blank=True)
# related_articles is removed.
class Article(models.Model):
id = models.AutoField(primary_key=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL) #settings INSTALLED_APPS
title = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete = models.CASCADE, blank=True)
slug = models.SlugField(unique=True, blank=True)**
related_articles = models.ForeignKey(to=QA, on_delete=models.CASCADE, related_name="related_articles")
class QADetailView(LoginRequiredMixin, DetailView):
login_url = 'login'
redirect_field_name = 'login'
template_name = 'QADetailView.html'
model = QA
def get_context_data(self, **kwargs):
categories = Category.objects.all()
# obtain specific QA
qa = QA.objects.get(pk=id, author=self.request.user)) # check how you named the id variable in your url.
# obtain related articles
related_articles = qa.related_articles.all()
# Save in context for use in template.
context['related_articles'] = related_articles # Added an character 's' to key value for there is can be more than one related article.
context['categories'] = categories
return context
{% for article in related_articles %} # Also add character 's' here
{{article.title}}
{% endfor %}
This should do the trick. There might be some improvements on error handling though
I hope this works. If not, let me know.

Related

Django model with multiple foreign keys

I want to create a list of products that are categorized by area, need, and product category. I created 3 separate models that reply on each other so users will easily add categories and select from the dropdown list area and need.
For now, I have 3 areas and under each area I have needs. What I want is to render a list of items from Area model, under each item from Area model all items from Need model and under each item from Need model all items from Product model (only titles).
ProductCategory model is not used here but I need it somewhere else and it is a connector between products and areas/needs. It is much easier to define categories and just select them from the dropdown list when adding a new product.
I need to create a loop that will show me something like above. I know it will not work but I wanted to show the logic.
My question is what is the best approach to render something like this?
How can I get category_area and category_need in my class and in my html?
models.py
class Area(models.Model):
title = models.CharField(max_length=75, blank=False)
body = models.CharField(max_length=150, default='-', blank=False)
publish = models.DateTimeField('publish', default=timezone.now)
class Need(models.Model):
title = models.CharField(max_length=75, blank=False, null=False, help_text='max 75 characters')
body = models.CharField(max_length=150, default='-', blank=False)
publish = models.DateTimeField(default=timezone.now)
need_area = models.ForeignKey(Area, on_delete=models.CASCADE, related_name='need_area')
class ProductCategory(models.Model):
title = models.CharField(max_length=400, blank=False, null=False, help_text='max 400 characters')
body = models.TextField(default='-')
publish = models.DateTimeField('publish', default=timezone.now)
category_area = models.ForeignKey(Area, on_delete=models.CASCADE, related_name='category_area', null=True)
category_need = models.ForeignKey(Need, on_delete=models.CASCADE, related_name='category_need', null=True)
class Product(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=400, blank=False)
category = models.ForeignKey(ProductCategory, on_delete = models.CASCADE, blank=True, related_name='products')
def get_absolute_url(self):
return reverse("product", kwargs={'slug': self.slug})
views.py
class SearchProducts(LoginRequiredMixin, ListView):
login_url = 'login'
redirect_field_name = 'login'
template_name = 'hubble/search/manual_search.html'
model = Product
queryset = Product.objects.all()
def get_context_data(self, **kwargs):
context = super(SearchProducts, self).get_context_data(**kwargs)
product_results = ProductCategory.objects.prefetch_related("products").all()
context['product_results'] = product_results
return context
product_search.html
<div>
{% for area in product_results %}
<h6>{{area.title}}</h6>
{% endfor %}
</div>
You can prefetch_related all backwards relationship and make an iteration similar to this:
areas = Area.objects.prefetch_related('need_area__category_need__products')
for area in areas:
# iterate through each instance of Area
for need in area.need_area.all():
# iterate through each instance of Need for the given area
for product_category in need.category_need.all():
# iterate through each instance of ProductCategory for the given need
for product in product_category.products.all():
# iterate through each instance of product for the given product category

How to print data in template django of a diffrent table joined by foreign key?

Hello Everyone i have Two model first one is as following:
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.FloatField()
bargainprice = models.FloatField(default=0)
discount_price = models.FloatField(blank=True, null=True)
category = models.CharField(choices=CATEGORY_CHOICES, max_length=2)
label = models.CharField(choices=LABEL_CHOICES, max_length=1)
slug = models.SlugField()
description = models.TextField()
image = models.ImageField()
and i am getting this model data using the following view:
class ItemDetailView(DetailView):
model = Item
template_name = "product.html"
and in product.html i am accessing Item objects like this:
<span class="mr-1">
<del>₹ {{ object.price }}</del>
</span>
<span>₹ {{ object.discount_price }}</span>
{% else %}
<span> ₹ <span id="pp">{{ object.price }}</span></span>
and so on..
everything working fine up here. but problem arises when i created the following model:
class BargainModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
itemId = models.IntegerField()
bprice = models.FloatField()
i joined this with foreign key as mentioned.
**what i want to do is print the " bprice " in the product.html of the same user but i am not able to do it **
can anyone help me with this i am new to Django.
Thanks in advance
in this case you need to import User like
from django.contrib.auth.models import User
class BargainModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
itemId = models.ForeignKey(Item, on_delete=models.CASCADE)
bprice = models.FloatField()
in product.html you can call the model of BargainModel it also contains the Item with user
It is better to work with a ForeignKey since this will guarantee referential integrity. You thus shoudl define the BargainModel as:
from django.conf import settings
class Bargain(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
item = models.ForeignKey(
Item,
on_delete=models.CASCADE
)
bprice = models.FloatField()
class Meta:
constraints = [
models.UniqueConstraint(fields=['item', 'user'], name='unique_user_item')
]
In the DetailView, we can then look if there is a Bargain record for the given item and user with:
class ItemDetailView(DetailView):
model = Item
template_name = "product.html"
def get_bargain(self):
if self.request.user.is_authenticated():
return Bargain.objects.filter(item=self.object, user=request.user).first()
Then you can render this with:
{{ view.get_bargain.bprice }}
if there is a related Bargain, then it will show the corresponding bprice.
Note: Models normally have no Model suffix. Therefore it might be better to rename BargainModel to Bargain.

How to handle a legacy database in the Django framework

I'm working on an Django app that needs to access a very large (MySQL) database, the db has no foreign keys whatsoever. I need to do queries on multiple tables. The way I'm doing it is VERY inefficient and involves doing multiple loops:
{% for flower in especies_id %}
<td>{{flower.especies}}</td>
{% for family in family_id %}
{% if family.family_id == flower.family_id %}
<td><a class="nav-item active" href="/home">
{{family.family_name}}</td></a>
{% endif %}
{% endfor %}
{% endfor %}
Is there a way to handle this db with the Django shell maybe JavaScript? Or refactor the db entirely?
(edit):
the db has ~3000 entries
relationship between two tables are made using an additional one:
flower2estate which contains estate_id, flower_id
Models.py:
class Listflower(models.Model):
especies_id = models.AutoField(primary_key=True)
family_id = models.IntegerField(blank=True, null=True)
especies = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'listflower'
class Estate(models.Model):
estate_id = models.AutoField(primary_key=True)
estate_name = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = False
db_table = 'estates'
class Flower2Estate(models.Model):
estate_id = models.IntegerField(primary_key=True)
especies_id = models.IntegerField()
class Meta:
managed = False
db_table = 'flower2estate'
unique_together = (('estado_id', 'especie_id'),)
Views.py:
def flowers(request):
list_flower = ListFlower.objects.all().order_by('especies_id')
paginator = Paginator(list_flower, 3)
page = request.GET.get("page")
try:
flowers = paginator.page(page)
except PageNotAnInteger:
flowers = paginator.page(1)
except EmptyPage:
flowers= paginator.page(paginator.num_pages)
return render(request, 'accounts/flowers.html', {'page':page,"flowers"
:flowers,"especies_id":flowers})
I need to get the "flower" and the corresponding "estate" without the need to loop between 3000 entries.
You can add some methods to legacy models to retrieve related items a bit more efficiently:
class Listflower(models.Model):
especies_id = models.AutoField(primary_key=True)
family_id = models.IntegerField(blank=True, null=True)
especies = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'listflower'
def get_family(self):
return FamilyModel.objects.get(family_id=self.family_id)
class Estate(models.Model):
estate_id = models.AutoField(primary_key=True)
estate_name = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = False
db_table = 'estates'
def get_flowers(self):
flower_ids = Flower2Estate.objects.filter(estate_id=self.estate_id).values_list('especies_id', flat=True)
return Listflower.objects.filter(especies_id__in=flower_ids)
But if it is not the last time you work with this data probably the better way is to define regular django models with fks and write once a script to convert legacy data to a new model structure. The challenge shouldn't take more than an hour.
UPDATE
class FlowerFamily(models.Model):
# assuming you old family model has
# "family_id" and "family_name" fields
family_id = models.IntegerField(blank=True, null=True)
family_name = models.CharField(max_length=255, blank=True, null=True)
class Flower(models.Model):
# you might want preserve old model fields in the new model
# at least id fields
especies_id = models.IntegerField(blank=True, null=True)
family_id = models.IntegerField(blank=True, null=True)
especies = models.CharField(max_length=255, blank=True, null=True)
family = models.ForegnKey(FlowerFamily, related_name='flowers')
class NewEstate(models.Model):
estate_id = models.IntegerField(blank=True, null=True)
estate_name = models.CharField(max_length=100, blank=True, null=True)
flowers = models.ManyToManyField(Flower, related_name='estates')
# this is a slightly primitive example
# in real life you might want to use get_or_create instead of direct creation
# in case script fails and you'll need to run it again
# also objects.get() might better be used with try-except ObjectDoesNotExist
def convert_legacy():
# create new instances
for ff in YourOldFamilyModel.objects.all():
new_ff = FlowerFamily(family_id=ff.family_id, family_name=ff.family_name)
new_ff.save()
for fl in Listflower.objects.all():
new_fl = Flower(...)
family = FlowerFamily.objects.get(family_id=fl.family_id)
new_fl.family = family
new_fl.save()
# same thing for Estate
for ...:
new_estate ...
new_estate.save()
# and restore relations
flower_ids = Flower2Estate.objects.filter(estate_id=new_estate.estate_id).values_list('especies_id', flat=True)
for new_lf in Flower.objects.filter(especies_id__in=flower_ids):
new_estate.flowers.add(new_fl)

How do i create a category and subcategories

This is my Item Model, how do i create a Category and Subcategories like; Man-clothing-shirt and Woman-clothing-shirt.
class Item(models.Model):
title = models.CharField(max_length=250)
company_name = models.CharField(max_length=50, blank=True)
product_you_may_like = models.ForeignKey(ProductYouMayLike, null=True, blank=True,
on_delete=models.CASCADE)
image = models.ImageField(null=True)
price = models.IntegerField()
old_price = models.IntegerField(null=True, blank=True)
shipping_amount = models.IntegerField()
percentage_off = models.IntegerField(null=True, blank=True)
description = models.TextField(null=True, blank=True)
specification = models.TextField(null=True, blank=True)
product_type = models.CharField(max_length=20, choices=PRODUCT_TYPE, null=True, blank=True)
slug = AutoSlugField(populate_from='title', unique=True)
timestamp = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ['-timestamp']
db_table = 'items'
verbose_name_plural = 'Items'
def __str__(self):
return self.title
You may make use of django-mptt to create a tree of categories and then have a foreign-key to this model in the item model.
class Category(MPTTModel):
name=models.CharField(max_length=75,null=False,blank=False, unique=True)
parent=TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True, on_delete=models.CASCADE)
I don't know how you wrote your MenShirtCategory, but the name let me think that you mixed up class and instance.
Usually, you may have a Category model (or ItemCategory, if these caterogies are specific to Item) written this way:
class Category(models.Model):
name = models.CharField(max_length=..., ...) # men shirts will be the name
parent = models.ForeignKey('self', null=True, blank=True,
on_delete=...,
related_name='children')
And then connect it to your Item model:
class Item(models.Model):
category = models.ForeignKey(Category, null=True, blank=True,
on_delete=models.CASCADE)
By using 'self' in the parent field of the Category model, you can implement the concept of nested categories.
Of course, you may need to ensure manually that you don't create cycles (A child of B child of A), as the database (and then models) cannot do it for you. While doing this, you may also need to check that a category cannot be it's own parent.
I was able to solve the problem by adding a model Category and also A foreignkey to Item model. Example,
class Item(models.Model):
category = models.ForeignKey(Category, null=True,blank=True,
on_delete=models.CASCADE)
class Category(models.Model):
name = models.CharField(max_length=250)
In model add men_shirt in Category, then save.
In views.py:
def template(request):
category = Category.objects.filter(name="men_shirt")
context = { 'category':category }
return render(request, "template.html", context)
In template:
{% for category in category %}
{% for item in category.item.all %}
<h1>This will display all content in item by the name you filtered in views.py </h1>
{% endfor %}
{% endfor %}

How to build a form that contains items of a collection

I have 2 models: a wishlist model and a wish model. A wishlist is made of n wish (wish have a reference to his wishlist). I would like update a wishlist with a form. Here is the form I'd like to have:
input:text wishlist.name
for wish in wishlist:
input:text wish.name
input:number wish.price
I'm new to django and don't know how to build a form that handle two models.
# models.py
class WishList(models.Model):
name = models.CharField(max_length=200)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
created_date = models.DateTimeField('date of creation')
class Wish(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=24, decimal_places=2)
description = models.TextField(max_length=200)
wishlist = models.ForeignKey(WishList, on_delete=models.CASCADE)

Categories