Django model with multiple foreign keys - python

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

Related

Show list of related objects in a template Django

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.

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 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 %}

Django models, set unqiue attribute for M2M field per object

Currently, have a database with an Item table and a Stock table. There is a many to many relationship between the two. A single item object can have many sizes. The next step is to assign an 'inStock' options to the item per size.
Any thoughts on acheiving this?
Current models.py
class Stock(models.Model):
size = models.CharField(max_length=30)
stock = models.BooleanField(default=False)
def __str__(self):
return self.size
class Item(models.Model):
title = models.CharField(max_length=250, null=True, unique=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
aw_product_id = models.CharField(max_length=11, null=True) # Removed because multiple products has similar identifer
url = models.URLField(max_length=250) # Removed 'unique=True'as the aw_prod_id will throw an integrity error
image = models.CharField(max_length=1000)
retailer = models.CharField(max_length=250)
category = models.CharField(max_length=100)
featured = models.CharField(max_length=10, default='NO')
gender = models.CharField(max_length=100, null=True)
sizes = models.ManyToManyField(Stock)
uniq_id = models.CharField(max_length=11, null=True, unique=True) # Removed because multiple products has similar identifer
def __str__(self):
return self.title
You can use the through argument to ManyToManyField to specify another model to use for the relationship, with additional fields instead of the autogenerated model that django creates by default.

Creating SubCategories with Django Models

I am trying to expand the relationships within my Django Models. I have a system where elements are stored within Categories. How do I structure my models.py so that each category is related to a subcategory?
Here is what my category model looks like:
class Category(models.Model):
site = models.ForeignKey(Site)
template_prefix = models.CharField(max_length=200, blank=True)
name = models.CharField(max_length=200)
slug = models.SlugField()
description = models.TextField(default='')
sortby_fields = models.CharField(max_length=200,
help_text=_(u'A comma separated list of field names that should show up as sorting options.'),
blank=True)
sort_order = models.PositiveIntegerField(default=0)
def __unicode__(self):
return self.name + u' Category'
class Meta:
verbose_name_plural = u'categories'
Thanks for any suggestions.
You can create a foreign key to itself:
class Category(models.Model):
...
parent_category = models.ForeignKey('self', null=True, blank=True)
Then you can assigning any existing Category instance as the parent_category of that instance. Then, if you wanted to find all of the subcategories of a given Category instance you would do something like:
subcategories = Category.objects.filter(
parent_category__id=target_category.id)
You can also try django-mptt. It has some additional features.

Categories