django query accessing foreign key data - python

Apology if this is a duplicate but I could not find any satisfying answer for this case. Maybe I am doing something fundamentally wrong, like need to define models in a different way.I am also very new to django.So suggestions will be really helpful.
Suppose I have my models like this:
from django.contrib.auth.models import Group
class Team(Group):
description = models.CharField(max_length=30)
class Players(models.Model):
name = models.CharField(max_length=60)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
Now if I for example, want a queryset of all players by the Team model what can I do?
I have the foreign key in the Players model and not in Team model. So something like Team.objects.filter(logic) is needed to get all players. But exactly what logic will work here?
Any help will be much appreciated.

Django ORM also can filter by relation. these kind of filtering is valid for all kind of relations.
players = Players.objects.filter(team__id = 1)
in case you have the team object.
players = Players.objects.filter(team = team)

Django allows you get access to related objects in reverse way. It calling reverse relation. And related manager objects using for it. You can find more details here and here. In your specifc case you can get players using players_set team's attribute:
team = Team.objects.get(pk=1)
players = team.players_set.all()

Related

Django many to many relation, include all IDs in queryset in both directions

I have 2 models connected via M2M relation
class Paper(models.Model):
title = models.CharField(max_length=70)
authors = models.ManyToManyField(B, related_name='papers')
class Author():
name = models.CharField(max_length=70)
Is there a way to include authors as all related authors' IDs (and maybe name somehow)?
Is there a way to include papers IDs as reverse relation (and maybe title as well)?
Author.objects.all().annotate(related_papers=F('papers'))
this only adds id of one paper, first one it finds I think.
Furthermore, changing related_papers to papers gives an error:
ValueError: The annotation ‘papers’ conflicts with a field on the
model.
From what I understand in your comments, you're using DRF. I will give you 2 answers.
1) If you're talking about model serializer, you can use PrimaryKeyRelatedField :
class AuthorSerializer(serializers.ModelSerializer):
papers=serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Author
fields = ['name', 'papers']
class PaperSerializer(serializers.ModelSerializer):
class Meta:
model = Paper
fields = '__all__'
This will return the IDs for the other side of the relationship whether you're on Paper or Author side. That will return the primary keys, not a representation of the object itself.
2) Now you're also talking about performance (e.g. database hit at each iteration).
Django (not DRF-specific) has a queryset method to handle preloading related objects. It's called prefetch_related.
For example, if you know you're going to need the relation object attributes and want to avoid re-querying the database, do as follow:
Author.objects.all().prefetch_related('papers')
# papers will be already loaded, thus won't need another database hit if you iterate over them.
Actually, it has already been implemented for you. You should include a Many-to-Many relationship to author in your Paper model like this:
class Paper(models.Model):
title = models.CharField(max_length=70)
authors = models.ManyToManyField(Author, related_name='papers')
That gives you the opportunity to add Author objects to a related set using
p.authors.add(u), assuming that p is the object of Paper model, and a is an object of Author model.
You can access all related authors of a Paper instance using p.authors.all().
You can access all related papers of an Author instance using u.papers.all().
This will return an instance of QuerySet that you can operate on.
See this documentation page to learn more.

How to access data across M2M tables in Django?

What is the 'best practice' way of accessing data across a 1 (or more) many-to-many tables?
This is incredibly difficult for me as I am not sure what I shuld be googling/looking up.
I have attached a diagram of my data model. I am able to query data for 'C' related ot a user, by utilizing serializers.
there has to be a simpler way of doing this (I'm hoping).
Doing it with serializers seems incredibly limiting. I'd like to access a user's 'B' and 'C' and transform the object to only have a custom structure and possible unique values.
Any direction is much appreciated. Pretty new to Django, so I apologize for this newb type of question.
Here is an example of M2M relation using Django:
class User(models.Model):
name = models.CharField(...)
class Song(models.Model)
title = models.CharField(...)
users_that_like_me = models.ManyToManyField('User', ..., related_name='songs_that_i_like')
So a User can like many Songs and a Song can be liked by many Users.
To see all the songs a user liked, we can do:
user = User.objects.get(id='<the-user-id>')
liked_songs = user.songs_that_i_like.all()
And to see all the users who like a particular song we can similarly do:
song = Song.objects.get(id='<the-song-id>')
users_that_like_this_song = song.users_that_like_me.all()
Both liked_songs and users_that_like_this_song are actually querysets, meaning we can do some Django magic on them.
For example, to find all users named Jon that liked this song we can do:
users_that_like_this_song.filter(name='Jon')
We can also add some property shortcuts to our Models to help with some common tasks, for example:
class User(models.Model):
...
#property
def number_of_liked_songs(self):
return self.songs_that_i_like.count()
Then we can do:
user = User.objects.get(id='<the-user-id>')
number_of_songs_i_like = user.number_of_liked_songs
There's much more we can do with Django - if you're looking for something specific let us know.

Django ORM - Filter Related Objects

I apologize if this is a duplicate, but I was unable to find any other SO posts that address this matter. I have models like so:
class Person(models.Model):
pass
class Interest(models.Model):
person = models.ForeignKey(Person, related_name='interests')
is_cool = models.BooleanField()
I know that I can find all people who have cool interests like so:
Person.objects.filter(interests__is_cool=True)
However, what I really want is to get only their cool interests when I get the Person object. I know that I could always pluck the related queryset out and operate on it, like so:
interests = person.interests.filter(is_cool=True)
but I cannot assign it back to the person instance since the relationship is reversed. To summarize, the goal is to use the ORM directly to filter the Interest objects being returned in the person.interests queryset.
One possibility is to define a method or property on the model:
def cool_interests(self):
return self.interests.filter(is_cool=True)

How to perform queries in Django following double-join relationships (or: How to get around Django's restrictions on ManyToMany "through" models?)

There must be a way to do this query through the ORM, but I'm not seeing it.
The Setup
Here's what I'm modelling: one Tenant can occupy multiple rooms and one User can own multiple rooms. So Rooms have an FK to Tenant and an FK to User. Rooms are also maintained by a (possibly distinct) User.
That is, I have these (simplified) models:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User)
maintainer = models.ForeignKey(User)
tenant = models.ForeignKey(Tenant)
The Problem
Given a Tenant, I want the Users owning a room which they occupy.
The relevant SQL query would be:
SELECT auth_user.id, ...
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id;
Getting any individual value off the related User objects can be done with, for example, my_tenant.rooms.values_list('owner__email', flat=True), but getting a full queryset of Users is tripping me up.
Normally one way to solve it would be to set up a ManyToMany field on my Tenant model pointing at User with TenantRoom as the 'through' model. That won't work in this case, though, because the TenantRoom model has a second (unrelated) ForeignKey to User(see "restictions"). Plus it seems like needless clutter on the Tenant model.
Doing my_tenant.rooms.values_list('user', flat=True) gets me close, but returns a ValuesListQuerySet of user IDs rather than a queryset of the actual User objects.
The Question
So: is there a way to get a queryset of the actual model instances, through the ORM, using just one query?
Edit
If there is, in fact, no way to do this directly in one query through the ORM, what is the best (some combination of most performant, most idiomatic, most readable, etc.) way to accomplish what I'm looking for? Here are the options I see:
Subselect
users = User.objects.filter(id__in=my_tenant.rooms.values_list('user'))
Subselect through Python (see Performance considerations for reasoning behind this)
user_ids = id__in=my_tenant.rooms.values_list('user')
users = User.objects.filter(id__in=list(user_ids))
Raw SQL:
User.objects.all("""SELECT auth_user.*
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id""")
Others...?
The proper way to do this is with related_name:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User, related_name='owns')
maintainer = models.ForeignKey(User, related_name='maintains')
tenant = models.ForeignKey(Tenant)
Then you can do this:
jrb = User.objects.create(username='jrb')
bill = User.objects.create(username='bill')
bob = models.Tenant.objects.create(name="Bob")
models.Room.objects.create(owner=jrb, maintainer=bill, tenant=bob)
User.objects.filter(owns__tenant=bob)

Creating "classes" with Django

I'm just learning Django so feel free to correct me in any of my assumptions. I probably just need my mindset adjusted.
What I'm trying to do is creating a "class" in an OOP style. For example, let's say we're designing a bunch of Rooms. Each Room has Furniture. And each piece of Furniture has a Type and a Color. What I can see so far is that I can have
class FurnitureType(models.Model):
name = models.CharField(max_length=200)
class FurnitureColor(models.Model):
name = models.CharField(max_length=50)
class FurniturePiece(models.Model):
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200)
class Room(models.Model):
name = models.CharField(max_length=200)
furnitures = models.ManyToManyField(FurniturePiece)
The problem is that each FurniturePiece has to have a unique name if I'm picking it out of the Django admin interface. If one person creates "Green Couch" then no one else can have a "Green Couch". What I'm wondering is if a) I need to learn more about Django UI and this is the right way to design this in Django or b) I have a bad design for this domain
The reason I want Furniture name to be unique is because 10 people could create a "Green Couch" each with a different sqft.
I don't get the problem with unique name. You can just specify it to be unique:
class FurniturePiece(models.Model):
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200, unique=True)
I don't know whether you have to learn about Django UI or not. I guess you have to learn how to define models. The admin interface is just a generated interface based on your models. You can change the interface in certain aspects without changing the models, but besides that, there is less to learn about the admin interface.
I suggest you follow a tutorial like the djangobook, to get a good start with Django.
I think, the problem that you have is not how to use Django but more that you don't know how to model your application in general.
First you have to think about which entities do yo have (like Room, Furniture, etc.).
Then think about what relations they have.
Afterwards you can model them in Django. Of course in order to do this you have to know how to model the relations. The syntax might be Django specific but the logical relations are not. E.g. a many-to-many relation is not something Django specific, this is a term used in databases to express a certain relationship.
Djangos models are just abstraction of the database design below.
E.g you specified a many-to-many relationship between Room and FurniturePiece.
Now the question: Is this what you want? It means that a piece of furniture can belong to more than one room. This sounds strange. So maybe you want to model it that a piece of furniture only belongs to one room. But a room should still have several pieces of furniture. We therefore define a relationship from FurniturePiece to Room.
In Django, we can express this with:
class FurniturePiece(models.Model):
room = models.ForeignKey(Room)
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200)
Maybe you should first learn about relational databases to get the basics before you model your application with Django.
It might be that this not necessary in order to create an application in Django. But it will definitely help you to understand whats going on, for every ORM not just Django's.
Why does each FurniturePiece need to have a unique name? It seems to me that if you remove that constraint everything just works.
(as an aside you seem to have accidentally dropped the models.Model base class for all but the Room model).
This is how I would do it:
class Room(models.Model):
name = models.CharField(max_length=255)
pieces = models.ManyToManyField('FurniturePiece')
class FurniturePiece(models.Model):
itemid = models.CharField(max_length=20, unique=True) # This is what I would require to be unique.
name = models.CharField(max_length=255)
type = models.ForeignKey('FurnitureType') # Note I put 'FurnitureType' in quotes because it hasn't been written yet (coming next).
color = models.ForeignKey('FurnitureColor') # Same here.
width_in_inches = models.PositiveIntegerField()
length_in_inches = models.PositiveIntegerField()
# Next is the property decorator which allows a method to be called without using ()
#property
def sqft(self):
return (self.length_in_inches * self.width_in_inches) / 144 # Obviously this is rough.
class FurnitureType(models.Model):
name = models.CharField(max_length=255)
class FurnitureColor(models.Model):
name = models.CharField(max_length=255)
Envision objects as real life objects, and you'll have a deeper understanding of the code as well. The reason for my sqft method is that data is best when normalized as much as possible. If you have a width and length, then when somebody asks, you have length, width, sqft, and if you add height, volume as well.

Categories

Resources