Django OneToOneField, ManyToManyField, Foreign Key - python

I've read many posts on what OneToOneField, ManyToManyField, and Foreign Key are but they aren't very clear. I am very new to Django and python programming, currently trying to develop models. Can someone explain to me in simple language, preferably with example, what they each are?

Imagine a database, which stores your book collection:
from django.db import models
class Place(models.Model):
address = models.CharField(max_length=50)
country = models.CharField(max_length=50)
class Publisher(models.Model):
name = models.CharField(max_length=30)
place = models.OneToOneField(Place, primary_key=True)
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
class Book(models.Model):
title = models.CharField(max_length=100)
publisher = models.ForeignKey(Publisher)
authors = models.ManyToManyField(Author)
One-to-many/Foreign Key
Every Book has one Publisher, but a Publisher might have published multiple books. Therefore they are in a one-to-many (book-to-publisher) relationship.
One-to-one
Every Publisher is located in one Place, and every Place can only hold one Publisher. Therefore they are in a one-to-one relationship. You could just have well have put the Place information (address and country) with the Publisher model in one table, but sometimes it is preferred to have seperate models. For example, if you do not know the Place for every Publisher, you don't need to take up a lot of space with empty rows.
Many-to-many
Every Book also has one or more Authors. However, an Author might have written multiple Books, so they are in a many-to-many relationship.
If you still need some guidance, I suggest taking a look at the model chapter of the Django Book.

Related

Django - Need helping writing annotated query

I'm trying to better understand how to write some more advanced queries. I'm used to the basic Model.objects.all() and .filter(), etc, but I'm having trouble writing out queries that are annotated.
Here is my model setup (I have removed any additional fields that are not necessary for this question):
from django.db import models
class Client(models.Model):
name = models.CharField(max_length=100)
class Project(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
class Campaign(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="campaigns")
class Advertisement(models.Model):
campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE, related_name='advertisements')
What I need is a query that shows how many Advertisements each client has, ordered by most to least.
I'm trying to follow the docs from here: https://docs.djangoproject.com/en/4.0/topics/db/aggregation/ but their annotation example isn't as nested, the publisher model example is a foreign key on the book object, where as with mine the client is a foreign key of a project, which is a foreign key of a campaign, which is a foreign key of advertisements.
Any advice on how to drill down through the layers of the models to get the info I need?
On Django ORM, the joins that navigate the relationships are done with __.
You can keep on nesting the relationship traversal.
This would make your query something like the following.
Client.objects.annotate(num_advertisements=Count('project__campaigns__advertisements'))
The names campaigns and advertisements have to be used because of the related_name you set.

Creating A Relationship Between Two User Models

Im building a website which will have teachers and students. Im using the default Django User models and originally ignored Teachers and treated all my Users as Students. Today I began trying to separate my users into Teachers and Students and am having a lot of difficulty. Im obviously missing some fundamental knowledge and have read a lot online but am going around in circles.
A teacher can have many students and a student can have one teacher.
First I thought Id need separate Student and Teacher models. So everyone now is a User, and I will attach either a Student or Teacher model to each (is this sloppy?).
Now for the relationship. I tried creating a relationship between teachers and students within their own models, but it didnt work so I figured id need a separate TeacherStudentRelationship class to hold the relationships.
This is my implementation so far:
class Student(models.Model):
student = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.student.username}'
class Teacher(models.Model):
teacher= models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.teacher.username}'
class TeacherStudentRelationship(models.Model):
student = models.ForeignKey(User, on_delete=models.CASCADE)
teacher = models.OneToOneField(User, on_delete=models.CASCADE)
This doesnt throw any errors, though in Django Admin I can create multiple instances of TeacherStudentRelationship and assign a student to a teacher in multiple instances (should only be able to assign a student to a teacher once). I can also only assign a single student to a single teacher within one instance even though the relationship is one-to-many.
I also have a problem with my implementation. I have a signal fire every time a user is created which generates a Student model and links it with the User. This is fine when I have students enrolling on my site, though it means my Admin also needs a Student model (or else it throws errors). I also planned on creating Teachers in the Django Admin panel, but this will cause issues there too as they will need Student models. How would you implement this?
Thank you.
Instead of having a separate relationship like this, you can have a ManyToMany Relation in your teacher model. Like this:
class Student(models.Model):
student = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.student.username}'
class Teacher(models.Model):
teacher= models.OneToOneField(User, on_delete=models.CASCADE)
students = models.ManyToManyField(Student)
In that way, it won't need to create new student when you are create a Teacher instance or vice versa.
Update
From comments, unfortunately you can't remove signals unless you use custom User model in django. You can try like this:
CHOICES = (('student', "Student"), ('teacher', "Teacher"))
class User(AbstractUser):
user_type = models.CharField(choices=CHOICES, max_length=255)
has_teacher = models.ForeignKey('self', null=True, default=None)
REQUIRED_FIELDS = ['user_type', 'email']
Then, you can define what kind of user it is during the User creating process, hence you do not need the signals. Also, you can store auth data of student and teacher in same model.

Defining a limit_choices_to for a OneToOneField which references the current model

I have two models, Company and Package, with a many-to-one relationship between them: each Company can have several Packages, but each Package has only one Company.
In addition, however, I'd like to define a default_package field for the Company model, which is a Package, and I'd like to limit the choices to the packages whose Company is the company under consideration.
class Company(models.Model):
default_package = models.OneToOneField(
'dashboard.Package',
on_delete=models.SET_NULL,
blank=True,
null=True,
related_name='default_for_%(class)s')
class Package(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
However, I'd like to add a limit_choices_to argument to the default_package field which would be something like
default_package = models.OneToOneField(..., limit_choices_to={'company': <this_company>})
where <this_company> is a reference to the current company. I'm not what the syntax for this would be, though; can anyone offer some help?
It would appear that this question was answered in How to get instance of entity in limit_choices_to (Django)?. In short, this is not possible.

Django - how do you decide which model to put foreign key?

Lets say I have two models that looks like this:
class Album(models.Model):
pub_date = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=250, blank=False, unique_for_date="pub_date")
class Track(models.Model):
title = models.CharField(max_length=250, blank=False, unique_for_date="pub_date")
album = models.ForeignKey(Album)
What is the difference between putting the ForeignKey (one-to-many) relationship on the Track model versus having a many-to-many on the Album model?
How does one decide such a thing?
Those would do completely different things.
Like you said, a ForeignKey is a one-to-many relationship. If you put it on Track, that means that many tracks belong to one album (or in other words, one album has many tracks). If you put it on Album, that means that many albums belong to one track, which is clearly not right.
The situation where there is a choice is when you're using a ManyToManyField. There, both sides are "many", so the choice of which side to put the field on is a purely semantic one.
Edit Again, these have different effects. A many-to-many would mean that both sides can have multiple items: an album can have many tracks and a track can belong to many albums.

How to save the data that has same model but with different data in Django

I'm working on a mini project which uses Django 1.8. The aim is to build a small billing app. So, I've created two models, Bill and Product, as follows.
#bill/models.py
class Bill(models.Model):
date_of_issue = models.DateField()
name = models.CharField(max_length=50, default="N/A", null=True)
address = models.CharField(max_length=150, default="N/A", null=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
def __str__(self):
return "{} {}".format(self.input_name, self.date_of_issue)
class Product(models.Model):
bill = models.ForeignKey(Bill, on_delete=models.CASCADE)
description = models.CharField(max_length=100, default="N/A")
quantity = models.IntegerField(default=0)
total = models.IntegerField(default=0)
By seeing the model, you can tell that my approach is to create Bill and Product tables and connect the Product to Bill via ForeignKey. Now, the thing is that a single bill will contain at least one product. If there are more than one product, how should I write my views in views.py. I'm a beginner in the Django development. If a Bill contains a single Product then I can write its view. But how can I store multiple Product which have different data and storing it in database.
For Example
The user will enter the name and address of the customer. Then the user will enter the details of products. Now I want to generate the Product modelform more than once (depends upon the number of products added by user). How can I accomplish this ?
Assuming
1) multiple products can be bought at once (and thus be part of one bill) and
2) one product can be bought multiple at any time (and thus be part of many bills)
a simple foreign key is the wrong modelling attempt. Instead of a m:1 relation you need a m:n relation - and thus a ManyToManyField. With a ManyToManyField you can add multiple products to one bill or have mutiple products added to multiple bills. In django-admin it's useful (and I recommend it) to put ManyToManyFields as filter_horizontal which eases the use of this field.
Plus, django will automatically resolve your m:n relation with an additional third database table, so you don't need to take care of this.

Categories