What exactly the reverse query name in its related model in Django? - django

I got pretty much the same problem as in Django - reverse query name clash.
for the following code:
class Category(models.Model):
title = models.CharField(max_length=255)
featured_product = models.ForeignKey(
'Product', on_delete=models.SET_NULL, null=True)
class Product(models.Model):
title = models.CharField(max_length=255)
price = models.DecimalField(max_digits=6, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
There will be an error saying:
store.Category.featured_product: (fields.E303) Reverse query name for 'store.Category.featured_product' clashes with field name 'store.Product.category'.
I know it the featured_productin Category should addrelated_name="+" to avoid this error. But since this is a many-to-one case, and I think the reverse query name for store.Category.featured_productinProductclass should be store.Product.category_set so it shouldn't have a conflict with store.Product.category.

Even if there weren't outright clashes (with the snippet you have, the generated reverse accessors should be Product.category_set and Category.product_set), I'd say Django is trying to be helpful so you don't make mistakes regarding the reverse accessors down the line.
As you noted, you can set related_name='+' to not create the reverse accessor at all, but I'd probably name the reverse accessor something like related_name='featured_product_for_categories'.
After all, your data model does make it possible for a product to be featured in multiple categories, and even categories it isn't part of.

Related

Get list of values from grandchildren records

I'm trying to access the grandchildren records in a list to avoid duplicate records. In this example, a tag can only be used once across articles for a given author. I will use the resulting list of grandchildren records in my clean function to return validation errors.
class Article(models.Model):
tag = models.ManyToManyField(Tag)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
class Tag(models.Model):
class Author(models.Model):
Right now I can do this:
print(author.articles.first().tag.first())
Travel
I'd like to be able to use something like author.articles.tags.all() to return the list and check the submitted form against it to raise a ValidationError message to the user.
How can this be done efficiently with the basic Many-to-Many setup without creating an intermediate table for the tag relationships? This is solely in the Admin interface, in case that matters at all.
i come from the link you posted on upwork,
the way i understand your question,
what you want to achieve seems to be impossible ,
what i think can work , is to fetch articles related to the author, with their corresponding tags,
after that they are retrieved you do filtering and remove duplicates.
otherwise the tag has nothing to connect it with the author,,
I'm so jealous that's a great answer #since9teen94
Be aware, I will not base my answer in the easiest solution but how we model reality (or how we should do it).
We put tags after things exists right?
In that order I will make your life horrible with something like this:
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=30, null=False)
class Article(models.Model):
name = models.CharField(max_length=30, null=False)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
class Tag(models.Model):
name = models.CharField(max_length=30, null=False, unique=True)
articles = models.ManyToManyField(Article)
but believe me you don't want to follow this approach it will make your work harder.
You can search for Articles based on Tags directly
Tag.objects.filter(name='Scify').first().articles.all() # name is unique
The real issue with this is that the reverse lookup is really complex (I mean.. get ready for debug)
Article.objects.filter(
id__in=list(
Tag.objects.filter(name='Scify').first().articles.values_list('id', flat=True)
)
)
I am sure this does not solve your problem and I don't like complex code for no reason but if you're open to suggestions I don't mind to think different and add some options
Edit:
About the author and clean repeated tags.. well you don't have to deal with that and if you want to find all Tag your author has you could loop the tags
for tag in Tag.objects.all():
if tag.articles.filter(author__name='StackoverflowContributor'):
print(tag.name)
# > Scify
I am just saying that there are options not that this is the best for you but don't be afraid of the terminal, it's really cool
The Django ORM is pretty cool when you get used to it, but regular SQL is pretty cool too
# created_at, updated_at, name and tag_val are variables I
# added due to my slight ocd lol
class Author(models.Model):
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Tag(models.Model):
tag_val = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Article(models.Model):
author = models.ForeignKey(Author,related_name='articles', on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, related_name='articles')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
I can write my query like this, assuming the variable 'author' has been assigned an Author object instance, and get a list of dictionaries [{'tags':1},{'tags':2}] where the value is the auto generated primary key id of the Tag object instance
author.articles.values('tags').distinct()

Django. Foreign key

I have 2 tables:
Side and AdditionalCost
now AdditionalCost has the following foreign key field:
side = models.ForeignKey(Side, on_delete=models.CASCADE, related_name='costs')
I want to have another foreign key field in AdditionalCosts:
number_of_additional_installations = models.ForeignKey(Side, on_delete=models.CASCADE, related_name="number_of_additional_installations")
The Side model has the following field:
number_of_additional_installations = models.IntegerField(null=True, blank=True,
db_column='number_of_additional_installations',
verbose_name='Количество доп монтажей')
But I get the following error:
ERRORS:
<class 'kinetics.apps.address_program.admin.AdditionalCostInline'>: (admin.E202) 'address_program.AdditionalCost' has more than one ForeignKey to 'address_program.Side'.
address_program.AdditionalCost.number_of_additional_installations: (fields.E302) Reverse accessor for 'AdditionalCost.number_of_additional_installations' clashes with field name 'Side.number_of_additional_installations'.
HINT: Rename field 'Side.number_of_additional_installations', or add/change a related_name argument to the definition for field 'AdditionalCost.number_of_additional_installations'.
address_program.AdditionalCost.number_of_additional_installations: (fields.E303) Reverse query name for 'AdditionalCost.number_of_additional_installations' clashes with field name 'Side.number_of_additional_installations'.
HINT: Rename field 'Side.number_of_additional_installations', or add/change a related_name argument to the definition for field 'AdditionalCost.number_of_additional_installations'.
I cannot figure out why this happened because I see that code has these lines:
buyer_org = models.ForeignKey("acl.Organization", on_delete=models.SET_NULL, null=True, blank=True,
related_name='buyer_costs')
client_org = models.ForeignKey("acl.Organization", on_delete=models.SET_NULL, null=True, blank=True,
related_name='client_costs')
that are obviously two foreign fields that relate to columns of one model.
If you need full code of the models let me know, it is quite large but I can add it if you need. Thank you
p.s. If I rename the related_name of number_of_additional_installations, i still get the following error:
ERRORS:
<class 'kinetics.apps.address_program.admin.AdditionalCostInline'>: (admin.E202) 'address_program.AdditionalCost' has more than one ForeignKey to 'address_program.Side'.
This is because you already have
number_of_additional_installations defined in Sides which clashes with the related name you set in AdditionalCosts. Either you rename the field in Sides or you change the related_name parameter so it wont confuse Django which field to resolve.
This code:
buyer_org = models.ForeignKey("acl.Organization", on_delete=models.SET_NULL, null=True, blank=True,
related_name='buyer_costs')
client_org = models.ForeignKey("acl.Organization", on_delete=models.SET_NULL, null=True, blank=True,
related_name='client_costs')
is not the same case. These fields do not cause conflict because they are referring to different relationships even if they are referring to the same model. model.buyer_org and model.client_org returns different relationships. model.buyer_org maps with organization.buyer_costs which is exclusive of model.client_org which, in turn, maps to organization.client_costs.
What's happening with the way you specify Sides and AdditionalCosts though creates conflict.
You can call side.number_of_additional_installations which returns an integer field but at the same time you are telling django that the additional_costs.number_of_additional_installations map to side.number_of_additional_installations.

Django: Annotate with field from another table (one-to-many)

Good day.
I wish to annotate my model with information from a different table.
class CompetitionTeam(models.Model):
competition_id = models.ForeignKey('Competition', on_delete=models.CASCADE, to_field='id', db_column='competition_id')
team_id = models.ForeignKey('Team', on_delete=models.CASCADE, to_field='id', null=True, db_column='team_id')
...
class Team(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
teamleader_id = models.ForeignKey('User', on_delete=models.CASCADE, to_field='id', db_column='teamleader_id')
...
class Competition(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
...
Looping through my competitions, I wish to retrieve the list of competitionteam objects to be displayed with the relevant team's name. I tried:
CompetitionTeam.objects.filter(competition_id=_competition.id).filter(team_id__in=joined_team_ids).annotate(name=...)
-where instead of the ellipses I put Subquery expressions in. However, I'm unsure of how to match the team_id variable. eg.
*.anotate(name=Subquery(Team.objects.filter(id=competitionteam.team_id)).values('name'))
Related is the question: Django annotate field value from another model but I am unsure of how to implement that in this case. In that case, in place of mymodel_id, I used team_id but it only had parameters from the Team object, not my competition team object. I didn't really understand OuterRef but here is my attempt that failed:
CompetitionTeam.objects.filter(competition_id=_competition.id).filter(team_id__in=joined_team_ids).annotate(name=Subquery(Team.objects.get(id=OuterRef('team_id'))))
"Error: This queryset contains a reference to an outer query and may only be used in a subquery."
The solution for my question was:
CompetitionTeam.objects.filter(
competition_id=_competition.id,
team_id__in=joined_team_ids
).annotate(
name=Subquery(
Team.objects.filter(
id=OuterRef('team_id')
).values('name')
))
Thanks.

Getting a queryset using a foreign key field in the "other side" of a foreign key relation

Forgive me if the question does not make sense, trying to teach myself django. I've been trying to search how to do this but i'm not sure if i'm using the right words in my search.
I have the following models.
class Category(models.Model):
code = models.CharField(max_length=10, unique=True)
description = models.CharField(max_length=50)
class UserGroupHeader(models.Model):
code = models.CharField(max_length=10, unique=True)
description = models.CharField(max_length=50)
class UserGroupDetail(models.Model):
usergroupheader = models.ForeignKey(UserGroupHeader, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
How do i get a query set from the Category model using the UserGroupHeader? so far what i've got is something like this UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all(), now from the result of this how do i get the Category model?
I'm not sure if I understood exactly what you are trying to do, but in general, while querying, you can follow relations using double underscores. Below are a couple of possible queries:
my_group_header = UserGroupHeader.objects.get(...)
Category.objects.filter(usergroupdetail__usergroupheader=my_group_header) # Gets Category objects related to my_group_header through UserGroupDetail model
Category.objects.filter(usergroupdetail__usergroupheader__code='abc') # Gets Category objects related to UserGroupHeader object with code 'abc' through UserGroupDetail model
UserGroupHeader.objects.filter(usergroupdetail__category__code='abc') # Gets UserGroupHeader objects related to Category object with code 'abc' through UserGroupDetail model
Your query UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all() would return a QuerySet of UserGroupDetail objects. In order to get the category of each UserGroupDetail, you can:
for user_group_detail in UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all():
category = user_group_detail.category
print(category. description)
Or something similar according to your needs

Getting all unique values stored in a many-to-many field

I have a many-to-many field called categories and I would like to get distinct values stored in that field.
The following is my model:
class Book (models.Model):
categories=models.ManyToManyField(Category, related_name = 'categories', blank = True, null=True)
Here is my Category model:
class Category (MPTTModel):
category = models.CharField(max_length=250)
parent = TreeForeignKey('self', blank=True, null=True, related_name='children')
I'd like to get every single one of the categories related to a book. How would I do this?
If you want to get categories related to one instance of book, do book_inst.category_set.all(). There will not be duplicates.
But I think, you want to get all Categories which are related to any Book, you can do:
Category.objects.filter(categories__in=[Book.objects.all()]).distinct()
Basically, you need a reverse lookup from the category side to check if there is book for that category if yes, add to the resultant query set. Since, the related_name argument in the Book is 'categories', your reverse lookup would look something like this.
Category.objects.filter(categories__in = Book.objects.all())