Here are stripped down versions of the models I'm dealing with:
class Contact(models.Model):
contact_no = models.IntegerField(primary_key=True)
email_address = models.CharField(max_length=60, null=True)
class ContactRole(models.Model):
contact_no = models.ForeignKey(Contact, primary_key=True, db_column='contact_no')
role_code = models.CharField(primary_key=True, max_length=16)
role_scope_code = models.CharField(primary_key=True, max_length=16)
Contacts can and almost always do have many ContactRoles.
I want a list of Contacts where the role_scope_code of the related ContactRole is 'foo'. I know I can get this with:
Contact.objects.filter(contactrole__role_scope_code='foo')
What I also want, is for each Contact in the queryset to have a single .contactrole property. It would be the ContactRole with the role_scope_code of 'foo'. Instead I'm getting a set of all ContactRoles that match on contact_no, so that to get to properties of the ContactRole I have to do something like this:
contacts = Contact.objects.filter(contactrole__role_scope_code='foo')
for contact in contacts:
print contact.contactrole_set.filter(role_scope_code='foo')[0].role_code
I have to filter on role_scope_code twice! That doesn't seem DRY at all. What I'm looking for is a query that will allow me to have a set that works like this:
contacts = Contact.objects.filter(contactrole__role_scope_code='foo')
for contact in contacts:
print contact.contactrole.role_code
For the life of me I can't figure out how to tell Django to only return the related objects that match the filter I applied to the parent object.
A OneToOneField will solve this provided that a contact only have one contactrole. A OneToOneField gives you the api you are looking for. So instead of using a ForeignKey use a OneToOneField
Related
I've got two models that are related to one another
class IndustryService(models.Model):
title = models.CharField(max_length=120)
pricingisarate = models.BooleanField(default=False)
class UserService(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.ForeignKey(IndustryService, on_delete=models.CASCADE, null=True, blank=True)
Within a view, I'm trying to develop a queryset of UserService instances that
a) belongs to a user
b) on the foreign key, has pricingisarate == True
I've tried the following query, but it doesn't work:
services = UserService.objects.filter(user=user, industryservice__pricingisarate__is=True)
Thanks for your help!!!
Got it!
services = UserService.objects.filter(user=user, title__pricingisarate=True)
You can filtering Foreign-Keys fields by using double underline between foreign-key defined name and sub field name that you want filtering by this, for your case it is similar below:
title__pricingisarate
And your query must change as below:
services = UserService.objects.filter(user=user, title__pricingisarate=True)
Some formal examples of Django about this article is available...
services = UserService.objects.filter(user=user, title__pricingisarate=True)
Because UserService is related to IndustryService model using lookup title.
Please refer to this link - https://docs.djangoproject.com/en/2.1/topics/db/queries/#lookups-that-span-relationships
The Django docs are not plainly stating this and its getting annoying.
Observe the following models:
# models
class Venue(models.Model):
name = models.CharField(max_length=150, blank=False)
description = models.CharField(max_length=1000)
image = models.ImageField(upload_to=imgUnique('venueMedia/venueImages'))
streetAddress= models.CharField(max_length=100)
city = models.CharField(max_length=100, blank=False)
state = models.CharField(max_length=100, blank=False)
class Room(models.Model):
name = models.CharField(max_length=150, blank=False)
venue = models.ForeignKey(Venue, related_name='rooms', on_delete=models.CASCADE)
description = models.CharField(max_length=1000)
standingCapacity = models.IntegerField
seatedCapacity = models.IntegerField
image = models.ImageField(upload_to=imgUnique('venueMedia/venueImages'))
I have a foreign key relationship into Venue in the Room model in the rooms venue property.
Do I need to join those tables? Or will a Venue.objects.all() do that for me automatically? If not I have the raw sql as follows will this work:
venueList = Venue.objects.raw('''SELECT *
FROM venue_Venue
INNER JOIN venue_Room
ON venue_Venue_id=venue_Room_id''')
if not that then I understand there is a select_related() method and my understanding is that I would do the following:
Venue.objects.all().select_related('Room')
Can we get this clear? As you can see I have done alot of research but am still confused
select_related
select_related can be used when you want to select the objects connected by ForeignKey or OneToOne field, more precisely you can use this only when you want to select a single connected object. select_related takes a param can use it to lookup for related_field. The param related_name='rooms' specified in your model attributes is responsible for setting related names. select_related method searches for these related names and if it finds a match then it returns you the related objects. This line
Venue.objects.all().select_related('Room')
would result in a lookup error as you don't have any related names 'Room' but you have room so you also need to watch out for case sensitivity. Below one would work.
Venue.objects.all().select_related('room')
As mentioned above select_related can only be used for selecting single related objects. If you wish to fetch multiple related objects like for ManyToManyField then use prefetch_related
In my Django application, I've got two models, one Users and one Friendships. There is a Many to Many relationship between the two, as Users can have many Friends, and Friends can have many other Friends that are Users.
How can I return all friends (first and last name) whom are NOT friends with the user with the first_name='Daniel'?
Models.py:
class Friendships(models.Model):
user = models.ForeignKey('Users', models.DO_NOTHING, related_name="usersfriend")
friend = models.ForeignKey('Users', models.DO_NOTHING, related_name ="friendsfriend")
created_at = models.DateTimeField(blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'friendships'
class Users(models.Model):
first_name = models.CharField(max_length=45, blank=True, null=True)
last_name = models.CharField(max_length=45, blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'users'
So far, here's what I've tried in my controller (views.py) -- please note, I understand controllers should be skinny but still learning so apologies. What I tried in the snippet below (after many failed attempts at a cleaner method) was to try and first grab friends of daniels (populating them into a list and then removing any duplicate ids), and then filter them out by their id.
# show first and last name of all friends who daniel is not friends with:
def index(req):
friends_of_daniel = Friendships.objects.filter(user__first_name='Daniel')
daniels_friends = []
for friend_of_daniel in friends_of_daniel:
daniels_friends.append(friend_of_daniel.friend.id)
daniels_friends = list(set(daniels_friends))
not_daniels_friends = Friendships.objects.exclude(id__in=daniels_friends)
context = {
'not_daniels_friends':not_daniels_friends,
}
return render(req, "friendapp/index.html",context)
However, when I try the following in my views (templates) file, I still see individuals whom are friends of Daniels. Any idea what I'm doing wrong?
<ul>
{% for not_daniel_friend in not_daniels_friends %}
<li>{{ not_daniel_friend.user.first_name }} {{ not_daniel_friend.user.last_name }}</li>
{% endfor %}
</ul>
I guess something like this will do. Just then take the list users, and get the first and last name of those users.
daniels = Users.objects.filter(first_name="Daniel") # There may be more than one Daniel
users = Friendships.objects.exclude(friend__in=daniels)
Note here, while Friendships.friend is a foreignkey of type Users you can pass Users instances (i.e daniels list) in friend__in to exclude those users.
Try this,In the place of friend_of_daniel.friend.id , You should exclude the results from User model.
Something like this :
def index(req):
friends_of_daniel = Friendships.objects.filter(user__first_name='Daniel')
daniels_friends = []
for friend_of_daniel in friends_of_daniel:
daniels_friends.append(friend_of_daniel.friend.id)
daniels_friends = list(set(daniels_friends))
not_daniels_friends = Users.objects.exclude(id__in=daniels_friends)
context = {
'not_daniels_friends':not_daniels_friends,
}
return render(req, "friendapp/index.html",context)
Thanks.
Firstly as a general comment: a cleaner way of populating a list of ids is using the .value_list() method from django (part of the .values() method in previous versions of Django). It has a "flat" flag that creates the list you want.
So, instead of:
friends_of_daniel = Friendships.objects.filter(user__first_name='Daniel')
daniels_friends = []
for friend_of_daniel in friends_of_daniel:
daniels_friends.append(friend_of_daniel.friend.id)
daniels_friends = list(set(daniels_friends))
You could do, in one line:
daniels_friends = Friendships.objects \
.filter(user__first_name='Daniel') \
.distinct('friend') \
.values_list('friend', flat=True)
distinct makes the same as your list() - set() cast (it makes sure that your list has no repeated elements) and values_list with flat=True can be customizable to any field in the related "user" table: .values_list('friend__id', flat=True) or .values_list('friend__first_name', flat=True) to get a list of first_names of Daniel's friends.
Coming back to your general question, you can do the whole query directly in one line using your related_names, as I am not really sure of what you want (an user instance, a Friendship instance or just a list of firsts and last names) I will give you many options:
If you want a Friendship instance (what you are trying in your sample
code):
friendships_not_friends_with_daniel = Friendships.objects\
.exclude(friend__first_name="Daniel")
This is equivalent to what #Rafael proposes in his answer:
daniels = Users.objects.filter(first_name="Daniel") # There may be
more than one Daniel users =
Friendships.objects.exclude(friend__in=daniels)
Here I am embedding his first query in the exclude by referencing the
field in the related table with double underscore (which is an very
powerful standard in Django).
If you want an User instance:
users_with_no_friendship_with_daniel = Users.objects\
.exclude(usersfriend__friend__first_name="Daniel")
Here you are using the related name of your model to access from the
users table to the friendships table, and then check if the friend of this user is called Daniel. This way of querying is a bit complex to understand but as soon as you get used to it becomes really powerful because it is very similar to the spoken language: you want all users, but excluding the ones that have a friendship, whose friend's first name is Daniel. Depending on how many friends an user hat or how many users are called Daniel, you might to add some distinct() methods or split the query in two.
As an advice, maybe you could improve the related name in your model, because it is what you would use if you have an user instance and want to get the related friendships: user_instance.friendships instead of user_instance.usersfriend and user_instance.friendsfriendships instead of user_instance.friendsfriend.... Do not know, it is always difficult to me to choose good related names...
If you want a list of tuples of users first and last names:
names_of_users_with_no_friendship_with_daniel = Users.objects\
.exclude(usersfriend__friend__first_name="Daniel")\
.values_list('first_name', 'last_name')
I am sorry if something is not clear, please ask and I try to explain better. (I am quite new in stackoverflow)
Using the django-rest-framework is it possible to retrieve content from a related field. So for example I want to create a genre list which contains all projects within it. This is what I have but I keep on getting the error:
'Genre' object has no attribute 'project_set'
models.py
class Genre(models.Model):
name = models.CharField(max_length=100, db_index=True)
class Project(models.Model):
title = models.CharField(max_length=100, unique=True)
genres = models.ManyToManyField(Genre, related_name='genres')
serializers.py
class GenreSerializer(serializers.ModelSerializer):
project_set = serializers.ManyRelatedField()
class Meta:
model = Genre
fields = ('name', 'project_set')
The related name you're using on the Project class is badly named. That related name is how you access the set of projects related to a given genre instance. So you should be using something like related_name='projects'. (As it is you've got it the wrong way around.)
Then make sure that your serializer class matches up with the related name you're using, so in both places project_set should then instead be projects.
(Alternatively you could just remove the related_name='genres' entirely and everything will work as you were expecting, as the default related_name will be 'project_set'.)
I have the following models
class Person(models.Model):
name = models.CharField(max_length=100)
class Employee(Person):
job = model.Charfield(max_length=200)
class PhoneNumber(models.Model):
person = models.ForeignKey(Person)
How do I access the PhoneNumbers associated with an employee if I have the employee id?
Currently I am using
phones = PhoneNumbers.objects.filter(person__id=employee.id)
and it works only because I know that the employee.id and person.id are the same value, but I am sure this is the incorrect way to do it.
Thanks
Andrew
You can (and should) filter without knowing the foreign key field:
PhoneNumber.objects.filter(employee=your_employee).all()
You could do:
employees = Employee.objects.filter(id=your_id).select_related()
if employees.count() == 1:
phone_numbers = employees[0].phonenumber_set.all()
That should get you all your phone numbers in one db query.
By default you can access models related through a foreignkey on the "opposite" side by using "model name in all lower case" followed by "_set". You can change the name of that accessor by setting the related name property of the foreignkey.