Django What is reverse relationship? - python

Can someone tell me what is reverse relationship means?
I have started using Django and in lot of places in the documentation I see 'reverse relationship, being mentioned. What is it exactly mean? why is it useful? What does it got to do with related_name in reference to this post ?

Here is the documentation on related_name
Lets say you have 2 models
class Group(models.Model):
#some attributes
class Profile(models.Model):
group = models.ForeignKey(Group)
#more attributes
Now, from a profile object, you can do profile.group. But if you want the profile objects given the group object, How would you do that? Thats' where related name or the reverse relationship comes in.
Django, by defaults gives you a default related_name which is the ModelName (in lowercase) followed by _set - In this case, It would be profile_set, so group.profile_set.
However, you can override it by specifying a related_name in the ForeignKey field.
class Profile(models.Model):
group = models.ForeignKey(Group, related_name='profiles')
#more attributes
Now, you can access the foreign key as follows:
group.profiles.all()

For a clearer picture you can assume that when we use reverse relationship, it adds an extra field in the referenced model:
For example:
class Employee(models.Model):
name = models.CharField()
email = models.EmailField()
class Salary(models.Model):
amount = models.IntegerField()
employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='salary')
After using related_name in Salary model, now you can assume the Employee model will have one more field: salary.
For example, the available fields would now be:
name, email, and salary
To find an employee, we can simply query in this way:
e = Employee.objects.filter(some filter).first()
To check their salary, we can check it by writing
e.salary (now we can use salary an attribute or field in employee model). This will give you the salary instance of that employee, and you can find the amount by writing e.salary.amount. This will give you the salary of that employee.
In case of many to many relationship we can use .all() and then iterate over that.

In Django 2.0 you would define a ForeignKey as follows
mainclient = models.ForeignKey( MainClient, on_delete=model.CASCADE, related_name='+')
the related_name='+' would cancel the default reverse relationship that Django sets up, so in the previous example, you would not be able to query the profiles using group.profiles.all().

Related

Django: constrain foreign key to only rows with the same id

For a Django project, I got two models:
class User(AbstractUser):
child = models.ForeignKey('children.Child', null=True, on_delete=models.SET_NULL)
And the following:
class Child(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
What I want is that User.child can only be set to an entity that has child.user_id = user.id, with a constraint preferably. Is this possible? If it matters, I am using PostgreSQL
Aren't you looking for a one-to-one relationship?
user in Child can only be set to one User.
child in User can also only be set to one Child.
You want user in Child and child in User to point to each other.
This is exactly what a one-to-one relationship is. The following code is even simpler than your current one and it enforces the constraint at the database level:
class User(AbstractUser):
# The relationship only needs to be defined in one of the models
pass
class Child(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
If there are finer points you need to cover (eg. if there are situations in which you would need the relationship to be defined in one direction but not in the other), then please clarify the use case.

Django Generic Foreign Key Sorting

So I have a model that has a generic foreign key relationship with with three other models
class Status(BaseRequestStatus):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Request(models.Model):
name = models.CharField(max_length=50, db_index=True)
...some fields
statuses = GenericRelation(Status, related_query_name='request')
class AnotherRequest(models.Model):
name = models.CharField(max_length=50, db_index=True)
...some fields
statuses = GenericRelation(Status, related_query_name='another_request')
class ThirdRequest(models.Model):
name = models.CharField(max_length=50, db_index=True)
...some fields
statuses = GenericRelation(Status, related_query_name='third_request')
So currently I am displaying the statuses with the name in a table. I want to be able to sort by the name. Currently I am sorting it like this.
Status.objects.all().order_by('request__name', 'another_request__name', 'third_request__name')
however with this method all of the 'request' is sorted in a chunk, then 'another_request', then 'third_request'
is there anyway to sort all of these together? I was able to do it by converting it to a list.. but in this case I need to use a Queryset.
Thanks in advance for any help.
If you want them treated the same way (deducing this from the fact that you want them sorted by their common name field) then this indicates that these models have the same base.
Use model inheritance in that case with a base model that is abstract and defines the name field and the generic fkey relation.
class RequestBase(models.Model):
name = models.CharField(max_length=50, db_index=True)
statuses = GenericRelation(Status, related_query_name='request')
class Meta:
abstract = True
class Request(RequestBase):
# ... more fields
Status.objects.all().order_by('request__name')
A more general explanation: to be able to sort objects by one attribute, you have to make clear that they all share that same attribute. Either by fetching the values in your own routines and creating lists that are sortable (this would be the qs approach you mentioned) or by creating a common interface - which is this Model inheritance approach.
As long as you are not using table inheritance, though, you will not be able to use the database for sorting (meaning: you cannot use the Django ORM for sorting).
There are certainly other approaches, including those that are outside of Django models: for example generating an additional lookup structure like a search index which will contain your transformed data as required (e.g. a haystack index).

Proper way of using Models with foriegnkey in Django

I'm new to Django and I know little bit about databases.
Let me explain my problem.
For example I have two models- Person and Car (A person can have multiple cars.)
class Person(models.Model):
name
username
password
#some more user attributes.
class Car(models.Model):
user = models.ForeignKey(Person)
car_name
car_price
#other car atrributes
Currently I'm working with models like this but somebody told me that you should use like below -
class Car(models.Model):
car_name
car_price
#other car atrributes
class Person(models.Model):
name
username
password
car = models.ForeignKey(Car)
#some more user attributes.
Noticed reverse relationship? from person to Car or Car to person which one should I use.
I know I can use related_name value in ForeignKey field for reverse lookup. I'm just curious about efficiency,standard and other advantages of one over other.
PS: although first one I'm using currently and its working perfectly.
Suppose a car can have only one owner.
Then, I would write it this way:
class Car(models.Model):
owner = models.ForeignKey(Person, related_name='cars")
class Person(models.Model):
# ... fields unrelated to cars

Use of meta class in django

Can someone please explain why is meta class used in the following example.
Ex:
Class Employee (models.Model):
name = models.ForeignKey(name)
Gender = models.IntegerField()
class Meta:
ordering = ["Gender"]
Thanks.
Django models use the Meta class to contain extra information about the model that would not necessarily be appropriate to contain within the model class itself. Note that this is not the same as Python's metaclass; that is a completely different topic.
In this case it ordering or sorting the queries to this model by field "Gender"
Because author/programmer wants to sort results by value of Gender field.
In this case it defines the default field for ordering if you don't provide ORDER_BY in your query.
It is explained in Django Documentation for Models
https://docs.djangoproject.com/en/dev/topics/db/models/
Give your model metadata by using an inner class Meta, like:
Class Employee (models.Model):
....
class Meta:
ordering = ["attribute-X"]
Another useful option can be used in class Meta is verbose_name.

ManyToOneField in Django

I'm trying to define a many-to-one field in the class that is the "Many". For example, imagine a situation where a user can only be a member of one group but a group can have many users:
class User(models.Model):
name = models.CharField()
class Group(models.Model):
name = models.CharField()
# This is what I want to do -> users = models.ManyToOneField(User)
Django docs will tell to define a group field in the User model as a ForeignKey, but I need to define the relationship in the Group model. As far as I know, there isn't a ManyToOneField and I would rather not have to use a ManyToManyField.
A ManyToOne field, as you've guessed, is called ForeignKey in Django. You will have to define it on your User class for the logic to work properly, but Django will make a reverse property available on the Groups model automatically:
class Group(models.Model):
name = models.CharField(max_length=64)
class User(models.Model):
name = models.CharField(max_length=64)
group = models.ForeignKey(Group)
g = Group.objects.get(id=1)
print g.user_set.all() # prints list of all users in the group
Remember that Django's models sit on top of a relational database... there's no way to define a single FK field in a table that points to more than one foreign key (without a M2M, that is), so putting the ManyToOne relationship on the Groups object doesn't map to the underlying data store. If you were writing raw SQL, you'd model this relationship with a foreign key from the user table to the group table in any event, if it helps to think of it that way. The syntax and logic of using a ManyToOne property that is defined on a Group instance, if such a concept existed, would be much less straightforward than the ForeignKey defined on User.
Assuming that the Users construct is the built-in authentication system... I would recommend creating a Profile model of some sort and attaching the OneToMany field to it instead. You can then hook the Profile model to the user model.
You should probably be looking at simply using built in reverse lookups:
group = Group.objects.get(id=1)
users_in_group = group.user_set.all()
Reverse lookup sets are automatically created for any foreign keys or many-to-many relationships to the model in question.
If you want to refer to this with a friendlier name, or provide additional filtering before returning, you can always wrap the call in a method:
class Group(models.Model):
name = models.CharField(max_length=64)
def users(self):
return self.user_set.all()
Either can be called from the templates:
{{ group.user_set.all }}
{{ group.users }}

Categories