I have a simple Person class and a Club class that should contain lots of Person instances:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=200)
class Club(models.Model):
name = models.CharField(max_length=200)
persons = models.ForeignKey(Person, on_delete=models.CASCADE)
How do I add more than one Person to the persons attribute? I want to build a database and then save it as follows:
club = Club(name='some_club')
club.person.add(Person(name='Alex'))
club.person.add(Person(name='John'))
club.save()
# etc. ...
Your foreign key is the wrong way round. To allow multiple people to be members of each club, the FK needs to be on Person pointing to Club.
class Person(models.Model):
name = models.CharField(max_length=200)
club = models.ForeignKey("Club", on_delete=models.CASCADE)
class Club(models.Model):
name = models.CharField(max_length=200)
...
some_club = Club(name='some_club')
some_club.save()
some_club.person_set.add(Person(name='Alex'))
some_club.person_set.add(Person(name='John'))
# Or:
Person.objects.create(name="Alex", club=some_club)
class Club(models.Model):
name = models.CharField(max_length=200)
class Person(models.Model):
name = models.CharField(max_length=200)
club = models.ForeignKey(Club, on_delete=models.CASCADE)
club1 = Club(name='some_club')
club1.save()
person1 = Person(name='Alex', club=club1)
person1.save()
person2 = Person(name='John', club=club1)
person2.save()
Edit: changed istance name to club1
Related
I have 3 models
class Person(models.Model):
name = models.CharField(max_length=128)
class Company(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField (Person, through = 'Membership', related_name = 'companies')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
is_admin = models.BooleanField()
I can then call person.companies.all() to get the list of companies associated with person.
How do I create a manager to have the list of companies associated with person, but whose person is admin (is_admin = True)?
You can create a manager like the following:
managers.py:
from django.db import models
class AdminCompaniesManager(models.Manager):
def get_queryset(self):
return super().get_queryset().companies.filter(membership__is_admin=True)
and then in your Person model (please remind the objects manager):
class Person(models.Model):
name = models.CharField(max_length=128)
objects = models.Manager()
administered_companies = AdminCompaniesManager()
Now you can easily call the following (e.g. in your views):
my_person.administered_companies.all()
PS: a very efficient option (e.g. if you are in a view and you need the list of company ids by a given person) is to query the membership model directly, so you can optimize the query for data retrieval from DB avoiding the joins:
Membership.objects.filter(is_admin=True, person=person).values_list('company_id')
You can filter with:
person.companies.filter(membership__is_admin=True)
This will filter the junction table Membership, such that it will only retrieve Companys for which the Membership has is_admin set to True.
Another option is to retrieve this with:
Company.objects.filter(membership__is_admin=True, members=person)
You can attach this to the Person model with:
class Person(models.Model):
name = models.CharField(max_length=128)
#property
def admin_companies(self):
return self.companies.filter(membership__is_admin=True)
I have 3 models using Django Framework:
class Student(models.Model):
name = models.CharField()
surname = models.CharField()
class Group(models.Model):
groupId = models.AutoField()
name = models.CharField()
students = models.ForeignKey(Student)
class Faculty(models.Model):
facultyId = models.AutoField()
students = models.ForeignKey(Student)
I need to get the list of all students and for each one to have the student's group and faculty.
Well, first you need to modify your model relationships. With your current models, each Faculty and Group will have a single student.
You can modify the model to this:
class Group(models.Model):
groupId = models.AutoField()
name = models.CharField()
class Faculty(models.Model):
facultyId = models.AutoField()
class Student(models.Model):
name = models.CharField()
surname = models.CharField()
group = models.ForeignKey(Group)
faculty = models.ForeighKey(Faculty)
Then to get the Group and faculty of each student you can use select_related.
Now your query will look like this:
Students.objects.all().select_related('group', 'faculty')
class Student(models.Model):
name = models.CharField()
surname = models.CharField()
class Group(models.Model):
groupId = models.AutoField()
name = models.CharField()
students = models.ForeignKey(Student, related_name="group")
class Faculty(models.Model):
facultyId = models.AutoField()
students = models.ForeignKey(Student, "related_name"="faculty")
you can get this data Student.objects.filter(group__isnull=False, faculty__isnull=False )
It will return the student who have faculty and group.
for Json data:
class Student(serializer.ModelSerializer):
class Meta:
model = Student
fields = ('name', 'surname' , 'group', 'faculty')
Consider the following database model:
class User:
id = models.BigAutoField(primary_key=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
class Restaurant:
id = models.BigAutoField(primary_key=True)
title = models.CharField(max_length=50)
class Rating:
id = models.BigAutoField(primary_key=True)
by_user = models.ForeignKey(to='User',
on_delete=models.PROTECT,
related_name='written_ratings')
for_restaurant = models.ForeignKey(to='Restaurant',
on_delete=models.PROTECT,
related_name='received_ratings')
score = models.SmallIntegerField()
# make sure only one vote per user per restaurant
class Meta:
unique_together = ('by_user', 'for_restaurant')
For a given User, we can obtain a list of Restaurant that we have not yet rated by performing the following query (that I have learned from my last post)
eligible_restaurants = Restaurant.objects.exclude(rating__by_user_id=my_id)
But what happens when the Ratings don't point directly at the Restaurants - but rather at an intermediate Profile object?
class User:
id = models.BigAutoField(primary_key=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
class Restaurant:
id = models.BigAutoField(primary_key=True)
title = models.CharField(max_length=50)
current_profile = models.OneToOneField(to='Profile',
on_delete=models.PROTECT,
related_name='+')
# the `+` means to not generate a related name
class Profile:
# this is here acting as an intermediate between
# `Restaurant` and `Rating` so that we can keep track
# of all reviews - deleting/remaking would simply be
# creating a new `Profile` and setting the `Restaurant`
# to point to it instead - the old one will act as a
# historical record
id = models.BigAutoField(primary_key=True)
by_restaurant = models.ForeignKey(to='Restaurant',
on_delete=models.PROTECT,
related_name='written_profiles')
picture_url = models.CharField(max_length=500)
picture_desc = models.CharField(max_length=500)
class Rating:
id = models.BigAutoField(primary_key=True)
by_user = models.ForeignKey(to='User',
on_delete=models.PROTECT,
related_name='written_ratings')
for_profile = models.ForeignKey(to='Profile',
on_delete=models.PROTECT,
related_name='received_ratings')
score = models.SmallIntegerField()
# make sure only one vote per user per restaurant
class Meta:
unique_together = ('by_user', 'for_profile')
How would I query for eligible restaurants now?
You could filter them starting with restaurants
restaurant_ids = Rating.objects.filter(by_user=user).values_list('for_profile__by_restaurant', flat=True).distinct()
eligible_restaurants = Restaurant.objects.exclude(id__in=restaurant_ids)
Note: this will generate only one query because django's querysets are lazy.
Is there any way I can set through_field to an intermediatory models's nested field,
Just for an example:
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('assignee__group', 'person'),
)
class GroupLeader(models.Model)
identity = models.ForeignKey(Person)
group = models.ForeignKey(Group)
#more fields
class Membership(models.Model):
assignee = models.ForeignKey(GroupLeader)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
# more fields
I tried doing it but I am getting an error:
The intermediary model 'Membership' has no field 'assignee__group'
NOTE: The above is just an example, just in case such a situation is encountered.
Your group leader should NOT be part of the M2M relation, this is just a metadata. So just add group into the Membership class.
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
)
class GroupLeaders(models.Model)
identity = models.ForeignKey(Person)
group = models.ForeignKey(Group)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
assignee = models.ForeignKey(GroupLeaders)
or you can even completely strip out the GroupLeaders class
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
leader = models.ForeignKey(Person, on_delete=models.CASCADE,
related_name="leading_memberships",
)
Sorry for title, I do not know how else to express myself.
For example, I have this three models:
class Person(models.Model):
name = models.CharField()
class Teacher(models.Model):
person = models.ForeignKey(Person)
subject = models.CharField()
class Student(models.Model):
person = models.ForeignKey(Person)
grade = models.CharField()
How can I select all Person models that are Teachers?
Person.objects.filter(teacher__isnull=False)
# return Person who has a teacher pointing to it