Difference between 'related_name' and 'related_query_name' attributes in Django? - python

Can you explain the difference between related_name and related_query_name attributes for the Field object in Django ? When I use them, How to use them? Thanks!

related_name will be the attribute of the related object that allows you to go 'backwards' to the model with the foreign key on it. For example, if ModelA has a field like: model_b = ForeignKeyField(ModelB, related_name='model_as'), this would enable you to access the ModelA instances that are related to your ModelB instance by going model_b_instance.model_as.all(). Note that this is generally written with a plural for a Foreign Key, because a foreign key is a one to many relationship, and the many side of that equation is the model with the Foreign Key field declared on it.
The further explanation linked to in the docs is helpful. https://docs.djangoproject.com/en/dev/topics/db/queries/#backwards-related-objects
related_query_name is for use in Django querysets. It allows you to filter on the reverse relationship of a foreign key related field. To continue our example - having a field on Model A like:
model_b = ForeignKeyField(ModelB, related_query_name='model_a') would enable you to use model_a as a lookup parameter in a queryset, like: ModelB.objects.filter(model_a=whatever). It is more common to use a singular form for the related_query_name. As the docs say, it isn't necessary to specify both (or either of) related_name and related_query_name. Django has sensible defaults.

class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
Here foreign key forward relation is Album to musician and backward relation is musician to album. This means one album instance can have relation with only one musician instance(forward relation), and one musician instance can relate to multiple album instance(backward).
Forward query will be like this
Album_instance.artist
note here forward query done by Album_instance followed by artist(field name). and backward would be
Musician_instance.album_set.all()
here for backward query modelname_set is used .
now if you specifies the related_name like
artist = models.ForeignKey(Musician, on_delete=models.CASCADE, related_name='back')
then backward query syntax will be change modelname_set(artist.set) will be replace by back.
now backward query
Musician_instance.back.all()
If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'.
and related_query_name to use for the reverse filter name from the target model

Related

Reverse accessor clasher with Django [duplicate]

D:\zjm_code\basic_project>python manage.py syncdb
Error: One or more models did not validate:
topics.topic: Accessor for field 'content_type' clashes with related field 'Cont
entType.topic_set'. Add a related_name argument to the definition for 'content_t
ype'.
topics.topic: Accessor for field 'creator' clashes with related field 'User.crea
ted_topics'. Add a related_name argument to the definition for 'creator'.
topics.topic: Reverse query name for field 'creator' clashes with related field
'User.created_topics'. Add a related_name argument to the definition for 'creato
r'.
topicsMap.topic: Accessor for field 'content_type' clashes with related field 'C
ontentType.topic_set'. Add a related_name argument to the definition for 'conten
t_type'.
topicsMap.topic: Accessor for field 'creator' clashes with related field 'User.c
reated_topics'. Add a related_name argument to the definition for 'creator'.
topicsMap.topic: Reverse query name for field 'creator' clashes with related fie
ld 'User.created_topics'. Add a related_name argument to the definition for 'cre
ator'.
You have a number of foreign keys which django is unable to generate unique names for.
You can help out by adding "related_name" arguments to the foreignkey field definitions in your models.
Eg:
content_type = ForeignKey(Topic, related_name='topic_content_type')
See here for more.
http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
Example:
class Article(models.Model):
author = models.ForeignKey('accounts.User')
editor = models.ForeignKey('accounts.User')
This will cause the error, because Django tries to automatically create a backwards relation for instances of accounts.User for each foreign key relation to user like user.article_set. This default method is ambiguous. Would user.article_set.all() refer to the user's articles related by the author field, or by the editor field?
Solution:
class Article(models.Model):
author = models.ForeignKey('accounts.User', related_name='author_article_set')
editor = models.ForeignKey('accounts.User', related_name='editor_article_set')
Now, for an instance of user user, there are two different manager methods:
user.author_article_set — user.author_article_set.all() will return a Queryset of all Article objects that have author == user
user.editor_article_set — user.editor_article_set.all() will return a Queryset of all Article objects that have editor == user
Note:
This is an old example — on_delete is now another required argument to models.ForeignKey. Details at What does on_delete do on Django models?
"If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source model name, lowercased."
But if you have more than one foreign key in a model, django is unable to generate unique names for foreign-key manager.
You can help out by adding "related_name" arguments to the foreignkey field definitions in your models.
See here:
https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward
If your models are inheriting from the same parent model, you should set a unique related_name in the parent's ForeignKey. For example:
author = models.ForeignKey('accounts.User', related_name='%(app_label)s_%(class)s_related')
It's better explained in th
If your models are inheriting from the same parent model, you should set a unique related_name in the parent's ForeignKey. For example:
author = models.ForeignKey('accounts.User', related_name='%(app_label)s_%(class)s_related')
It's better explained in the docs
I had a similar problem when I was trying to code a solution for a table that would pull names of football teams from the same table.
My table looked like this:
hometeamID = models.ForeignKey(Team, null=False, on_delete=models.CASCADE)
awayteamID = models.ForeignKey(Team, null=False, on_delete=models.CASCADE)
making the below changes solved my issue:
hometeamID = models.ForeignKey(Team, null=False, on_delete=models.CASCADE,related_name='home_team')
awayteamID = models.ForeignKey(Team, null=False, on_delete=models.CASCADE,related_name='away_team')
But in my case i am create a separate app for some functionality with same model name and field ( copy/paste ;) ) that's because of this type of error occurs i am just deleted the old model and code will work fine
May be help full for beginners like me :)
This isn't an ultimate answer for the question, however for someone it may solve the problem.
I got the same error in my project after checking out a really old commit (going to detached head state) and then getting the code base back up to date. Solution was to delete all *.pyc files in the project.
Do as the error message instructs you to:
Add a related_name argument to the
definition for 'creator'.

Django only one default row

I have a ManyToMany relationship that indicates a Doctor can have many specialties, but only one of them is the PRIMARY one.
I've designed a custom M2M class as follows:
class Doctor(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE)
specialty = models.ManyToManyField(Specialty, through='DoctorSpecialty')
.....
class Specialty(models.Model):
title = models.CharField(max_length=45)
.....
class DoctorSpecialty(models.Model):
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
specialty = models.ForeignKey(Specialty, on_delete=models.CASCADE)
default = models.BooleanField(default=True)
The doctor can have many specialties, but only one of them can be the default one. He or she can have many specialties with the default field set as False, but cannot have more than one with the default field set as True
I wanted to do something like this:
class Meta:
constraints = [
models.UniqueConstraint(fields=['doctor', 'specialty', 'default'], name='unique specialty')
]
But this will mean that the doctor can have only one specialty as a default one, and only one other as a non default one.
How can we achieve this with the minimum of code?
PS: I could leave it without constraints and try to validate adding new entries by checking if another default specialty exists, but this will add a lot of overhead and exception raising.
I think there is no way we can achieve this with built-in functions. So I came up with this solution (since no one else answered):
I created another ForeignKey for the Primary Specialty, and ditched the DoctorSpecialty custom M2M class and left the M2M relationship with Specialty. One doctor can have only one primary specialty, and can also choose additional specialties as secondary. Later on in the views, I can put in place an algorithm to remove the primary specialty from the list of specialties when entering additional ones in case there is an existing primary specialty.

How to make a username as a foreign key of another model in django

I am very new to Django. I want to link a model which has 2 field 'username' and 'password'. I want to make 'username' field as as Foreign in another model. But as per Django we can only pass the whole Model Object, who is referring to as it's foreign key.
am I wrong somewhere? please give me any solution regarding this basic problem.
No you can link to any unique field of a Django model. So if your models look like:
class Target(models.Model):
name = models.CharField(max_length=128, unique=True)
class SourceModel(models.Model):
target = models.ForeignKey(Target, to_field='name', on_delete=models.CASCADE)
You can assign the value of the target column to the target_id then. So for example:
Target.objects.create(name='target1')
SourceModel.objects.create(target_id='target1')
So you do not need to pass a Target object itself. You can use the …_id "twin" field to use the target column value. The database will normally enforce referential integrity, and thus will prevent passing a non-existing value to the foreign key column.

Django: list all reverse relations of a model

I would like my django application to serve a list of any model's fields (this will help the GUI build itself).
Imagine the classes (ignore the fact that all field of Steps could be in Item, I have my reasons :-) )
class Item(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
class Steps(models.Model):
item = models.OneToOneField('Item', related_name='steps')
design = models.BooleanField(default=False)
prototype = models.BooleanField(default=False)
production = models.BooleanField(default=False)
Now, when I want to list a model's fields:
def get_fields(model):
return model._meta.fields + model._meta.many_to_many
But I would also like to get the list of "related" one-to-one foreign keys to my models. In my case Item.steps would not be in that list.
I have found that model._meta.get_all_field_names does include all the related fields.
But when I call Item._meta.get_field_by_name('steps') it returns a tuple holding a RelatedObject, which does not tell me instantly whether this is a single relation or a one-to-many (I want to list only reversed one-to-one relations).
Also, I can use this bit of code:
from django.db.models.fields.related import SingleRelatedObjectDescriptor
reversed_f_keys = [attr for attr in Item.__dict__.values() \
if isinstance(attr, SingleRelatedObjectDescriptor)]
But I'm not very satisfied with this.
Any help, idea, tips are welcome!
Cheers
This was changed (in 1.8 I think) and Olivier's answer doesn't work anymore. According to the docs, the new way is
[f for f in Item._meta.get_fields()
if f.auto_created and not f.concrete]
This includes one-to-one, many-to-one, and many-to-many.
I've found out that there are methods of Model._meta that can give me what I want.
my_model = get_model('app_name','model_name')
# Reverse foreign key relations
reverse_fks = my_model._meta.get_all_related_objects()
# Reverse M2M relations
reverse_m2ms = my_model._meta.get_all_related_many_to_many_objects()
By parsing the content of the relations, I can guess whether the "direct" field was a OneToOneField or whatever.
I was looking into this answer as a starting point to identify reversed relationships for a model instance.
So, I noticed that when you get all the fields using instance._meta.get_fields(), those that are direct relationships, which are 3 types (ForeignKey, ManyToMany, OneTone), their parent class (field.__class__.__bases__) is django.db.models.fields.related.ForeignKey.
However, those that are reverse relationships inherit from django.db.models.fields.reverse_related.ForeignObjectRel. And if you take a look at this class, it has:
auto_created = True
concrete = False
So you could identify those by the attributes mentioned in the top-rated answer or by asking isinstance(field, ForeignObjectRel.
Another thing I could notice is that those reverse relationships have a field attribute which points to the direct relationship generating that reverse relationship.
Additionally, in order to exclude the fields instantiating the through table, those have through and through_fields attributes
And what about this :
oneToOneFieldNames = [
field_name
for field_name in Item._meta.get_all_field_names()
if isinstance(
getattr(
Item._meta.get_field_by_name(field_name)[0],
'field',
None
),
models.OneToOneField
)
]
RelatedObject may have a Field attribute for relations. You just have to check if this is a OneToOne field and you can retrieve only what you want
if you are using Django Rest Framework, you could use something like that for your obj:
from rest_framework.utils import model_meta
info = model_meta.get_field_info(obj)
for field in obj.__class__.__dict__.keys():
if field in info.relations and info.relations[field].to_many and info.relations[field].reverse:
#print all reverse relations
print(field)

How do you force only one relationship in django when multiple are possible?

I am creating a web application to manage robotics teams for our area. In the application I have a django model that looks like this:
class TeamFormNote(models.Model):
team = models.ForeignKey(Team, blank=True, null=True)
member = models.ForeignKey(TeamMember, blank=True, null=True)
notes = models.TextField()
def __unicode__(self):
if self.team:
return "Team Form Record: " + unicode(self.team)
if self.member:
return "Member Form Record: " + unicode(self.member)
Essentially, I want it to have a relationship with team or a relationship with member, but not both. Is there a way to enforce this?
I can only see two viable solutions. First is actually the same as #mariodev suggested in the comment which is to use Genetic foreign key. That will look something like:
# make sure to change the app name
ALLOWED_RELATIONSHIPS = models.Q(app_label = 'app_name', model = 'team') | models.Q(app_label = 'app_name', model = 'teammember')
class TeamFormNote(models.Model):
content_type = models.ForeignKey(ContentType, limit_choices_to=ALLOWED_RELATIONSHIPS)
relation_id = models.PositiveIntegerField()
relation = generic.GenericForeignKey('content_type', 'relation_id')
What that does is it sets up a generic foreign key which will allow you to link to any other model within your project. Since it can link to any other model, to restrict it to only the models you need, I use the limit_choices_to parameter of the ForeignKey. This will solve your problem since there is only one generic foreign key hence there is no way multiple relationships will be created. The disadvantage is that you cannot easily apply joins to generic foreign keys so you will not be able to do things like:
Team.objects.filter(teamformnote_set__notes__contains='foo')
The second approach is to leave the model as it and manually go into the database backend and add a db constaint. For example in postgres:
ALTER TABLE foo ADD CONSTRAINT bar CHECK ...;
This will work however it will not be transparent to your code.
This sounds like a malformed object model under the hood...
How about an abstract class which defines all common elements and two dreived classes, one for team and one for member?
If you are running into trouble with this because you want to have both referenced in the same table, you can use Generic Relations.

Categories