I am new to Django and I am trying to create a model for a website I am developing. So far I know how to implement everything I want to, but there is one thing that I cannot properly understand. So my idea is the following, in the models:
A class "Class" that can have as many "Teacher" and "Student" as wanted by the admin. I've been playing for a while with foreign key, but I can only manage to associate one student and teacher to a class, and whenever I want to display the "Class" info there is no Student or Teacher to display.
from django.db import models
from datetime import datetime
# Create your models here.
LANGUAGE_CHOICES = (
('english','ENGLISH'),
('spanish', 'SPANISH'),
('german','GERMAN'),
('french','FRENCH'),
('portuguese','PORTUGUESE'),
)
class Clase(models.Model):
name = models.CharField(max_length=300)
language = models.CharField(max_length=10, choices=LANGUAGE_CHOICES, default='english')
def __str__(self):
return self.name
class Student(models.Model):
name = models.CharField(max_length=300)
clase = models.ForeignKey(Clase, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Teacher(models.Model):
name = models.CharField(max_length=300)
total_earnings = models.IntegerField()
month_earnings = models.IntegerField()
clase = models.ForeignKey(Clase, on_delete=models.CASCADE)
def __str__(self):
return self.name
Since a Clase can have 'as many "Teacher" and "Student" as wanted' it may make sense to define the relationships with ManyToManyFields. I assume a Teacher and a Student can have more than 1 Clase?
class Clase(models.Model):
name = models.CharField(max_length=300)
language = models.CharField(max_length=10, choices=LANGUAGE_CHOICES, default='english')
students = models.ManyToManyField(Student)
teachers = models.ManyToManyField(Teacher)
def __str__(self):
return self.name
Then you can remove the foreign keys on the Student and Teacher models. Now when you edit a Clase in the admin you should be able to add as many students and teachers as you want
Related
I am getting errors while I am building the following database. Idea is that you create a Team object. Create Student Objects and link them to Teams. And then give the Students points through PointEntry objects. I want to relate the team given to each Student object in each PointEntry Object. But for some reason, Django gives the error:
score_board.Student: (fields.E336) The model is used as an intermediate model by 'score_board.PointEntry.team', but it does not have a foreign key to 'PointEntry' or 'Team'.
Modeladmin Class
class StudentAdmin(admin.ModelAdmin):
list_display = ['name', 'team']
list_filter = ['team']
class PointEntryAdmin(admin.ModelAdmin):
list_display = ['student', 'points']
list_filter = ['student']
Below are the models
class Team(models.Model):
# Team/group model
name = models.CharField(max_length=64)
class Student(models.Model):
# Student model
name = models.CharField(max_length=64)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
def __str__(self):
return f"{self.name}"
class PointEntry(models.Model):
# Point entry's made by users appointed to a student in a group
student = models.OneToOneField(Student, on_delete=models.CASCADE)
points = models.IntegerField()
team = models.ManyToManyField(Team, through='Student')
Your through model needs a ForeignKey to both models. Since the model that defines the ManyToManyField is defined lower, you can not reference to the class. But in Django, you can also use a string literal:
class Student(models.Model):
name = models.CharField(max_length=64)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
pointentry = models.ForeignKey('PointEntry', on_delete=models.CASCADE)
def __str__(self):
return f'{self.name}'
class PointEntry(models.Model):
# Point entry's made by users appointed to a student in a group
student = models.OneToOneField(Student, on_delete=models.CASCADE)
points = models.IntegerField()
team = models.ManyToManyField(Team, through='Student')
Django will then replace the string with a reference to the class.
class Team(models.Model):
# Team/group model
name = models.CharField(max_length=64)
student = models. models.ManyToManyField(Student,through='PointEntry ')
class Student(models.Model):
# Student model
name = models.CharField(max_length=64)
team = models. models.ManyToManyField(Team,through='PointEntry')
def __str__(self):
return f"{self.name}"
class PointEntry(models.Model):
# Point entry's made by users appointed to a student in a group
student = models. ForeignKey(Student,on_delete=models.CASCADE)
team = models. ForeignKey(Team, on_delete=models.CASCADE)
points = models.IntegerField()
I think a model like this might work for what you’re trying to achieve.
I have these two models :
class Country(models.Model):
Name = models.CharField(max_length=100)
Population = models.CharField(max_length=100)
Language = models.IntegerField()
Total_persons= models.IntegerField()
class Person(models.Model):
Country = models.ForeignKey(Country, on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=100)
contact = models.IntegerField()
In Country I have USA, CANADA, MEXICO, SPAIN, FRANCE......
In Person I have more than 1000 persons.
How can I make the Total_persons increase as the Total number of Persons increase in that country.
Instead of making Total_persons a separate field, you can drop the field and simply get the number of Persons belonging to a Country instance country with:
country.person_set.count()
Or if you prefer a friendlier name, you can give the Country foreign key in Person a related_name:
class Person(models.Model):
Country = models.ForeignKey(Country, related_name='persons', on_delete=models.CASCADE, null=True)
so that you can get the same count with:
country.persons.count()
Or if you'd still prefer to make Total_persons a separate field, you can override the Person.save() method to sync its country when a Person instance is created (when its pk is None), and override the Person.delete() method to sync when Person instance is deleted:
class Person(models.Model):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.pk:
self.Country.Total_persons = self.Country.person_set.count()
self.Country.save()
def delete(self):
country = self.Country
super().delete()
country.Total_persons = country.person_set.count()
country.save()
Your model field names should be lower case.
See Coding Style-Model for more information. Also your population and language fields don't seem to have the right data type??
Another way to achieve what you need is by using model properties.
class Country(models.Model):
name = models.CharField(max_length=100)
population = models.CharField(max_length=100)
language = models.IntegerField()
#property
def total_people(self):
return self.people.count()
class Person(models.Model):
Country = models.ForeignKey(Country, on_delete=models.CASCADE, null=True,
related_name='people')
name = models.CharField(max_length=100)
contact = models.IntegerField()
You have several ways to have the number of people in countries without a DB field:
With annotations:
qs = Country.objects.annotate(total_people=models.Count('person_set'))
for country in qs:
print(country.total_people)
Else if you really want to have a DB field, you can use post/pre hooks.
There are:
Post save
#receiver(post_save, sender=Person)
def increment_total_people(sender, instance, created, **kwargs):
instance.Country.Total_persons += 1
instance.Country.save()
Post delete
#receiver(post_delete, sender=Person)
def decrement_total_people(sender, instance, **kwargs):
instance.Country.Total_persons -= 1
instance.Country.save()
But you'll have to design things for all use cases when this number could change.
I am trying to create the proper Django model that could fit the following reqs:
Person Class has 1 to many relations with the Address Class
Person Class has many to many relations with the Group Class
Book Class contains the collections of the Persons and the Groups
This is my code:
class Person(models.Model):
first_name = models.CharField(max_length=15)
last_name = models.CharField(max_length=20)
def __str__(self):
return self.first_name+ ' - ' + self.last_name
class Address(models.Model):
person = models.ForeignKey(Person)
address_line = models.CharField(max_length=200)
def __str__(self):
return self.address_line
class Group(models.Model):
group_name = models.CharField(max_length=12)
persons = models.ManyToManyField(Person)
def __str__(self):
return self.group_name
class Book(models.Model):
record_name = models.CharField(max_length=12)
person = models.ForeignKey(Person )
group = models.ForeignKey(Group )
def __str__(self):
return self.record_name
However it's not correct:
1) A Group can now contain multiple Persons but the Persons do not contain any Group.
I am not sure if I should add to the Person class the following code:
groups = models.ManyToManyField(Group)
2) The Book class now contains only 1 record of Person & Group per Book record.
3) When I added the Foreign Keys to the models, I removed
on_delete tag:
person = models.ForeignKey(Person, on_delete=models.CASCADE())
because it does not compile it, asking for some params.
I know how to make all this for C#, but I am a kinda stucked with this simple task in Python/Django.
1) The ManyToMany field should appear only in one of the models, and by looks of things you probably want it in the Person model.
Its important to understand that the data about the ManyToMany field is saved in a differant table. Django only allows this field to be visable through buth models (so basiclly, choose where it is move convinient).
2)By the look of your structure I will suggest you use a ManyToMany field through a different table. here is an example:
class Activity(models.Model):
name = models.CharField(max_length=140)
description = models.TextField(blank=True, null=True)
class Route(models.Model):
title = models.CharField(max_length=140)
description = models.TextField()
activities_meta = models.ManyToManyField(Activity, through = 'RouteOrdering')
class RouteOrdering(models.Model):
route = models.ForeignKey(Route, on_delete=models.CASCADE)
activity = models.ForeignKey(Activity, on_delete=models.CASCADE, related_name='activita')
day = models.IntegerField()
order = models.IntegerField(default=0)
that way the data is binded to the ManyToMany field
I'm a newbee to django and I'm trying to implement django models in django-admin.
Here is my models.py:
class Author(models.Model):
salutation = models.CharField(max_length=10)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
def __str__(self):
return self.first_name
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=100)
website = models.URLField()
def __str__(self):
return ' %s ----> %s' % (self.name, self.address)
class Book(models.Model):
title = models.CharField(max_length=30)
publication_date = models.DateField()
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
def __str__(self):
return self.title
In this when I create a new book through the admin site it should allow me to select one or more authors, but instead all the authors are selected automatically and there is no option to remove any. There is a plus sign to add more authors but none to remove the authors already related.
I want to have the particular authors that I select for any particular book.
I believe that you have misunderstood how the widget works.
Here is an example of the use of Django's default widget for a ManyToManyField
Example: a teacher model is related to a language model:
teaching_languages = models.ManyToManyField('Language')
In the following case, English and Spanish are selected as values related to the teacher model, whilst French and German are not selected.
The green + sign allows you to add another language to the teaching languages list.
I am trying to get a queryset but it is not displaying anything. Basically, I want to get the Asset objects that are assigned via foreign key to an employee, which is a foreign key of the signed in user.
views.py
def get_queryset(self):
assetlist = Asset.objects.filter(organisation__employee__user=self.request.user)
print(assetlist)
return assetlist
models.py
class Employee(models.Model):
name = models.CharField("Employee Name", max_length=50, blank=False)
email = models.CharField("Employee Email", max_length=50, blank=True)
user = models.ForeignKey(User)
clientID = models.ForeignKey(Organisation)
def save(self):
self.name = self.user.get_full_name()
self.email = self.user.email
super(Employee, self).save()
def __str__(self):
return self.name
class Asset(models.Model):
name = models.CharField("Asset Name", max_length=30, primary_key=True)
organisation = models.ForeignKey(Organisation)
def __str__(self):
return self.name
class Organisation(models.Model):
name = models.CharField("Organisation Name", max_length=50, blank=False)
location = models.TextField("Organisation Address", max_length=200, blank=True)
tel = models.CharField("Telephone Number", max_length=20)
def __str__(self):
return self.name
There is no employee field inside organisation. It's an reversed relation, there are many employees attached so you can't query it like that.
But there is something called related_name in django foreign keys, with use of that, your query should look like that:
assetlist = Asset.objects.filter(organisation__employee_set__user=self.request.user)
or if you specify your own related_name into employee -> organisation relation:
clientID = models.ForeignKey(Organisation, related_name="employees")
it will look like this:
assetlist = Asset.objects.filter(organisation__employees__user=self.request.user)
The answer was to approach from another model, as seen below:
assetlist = Sensor.objects.filter(asset__organisation__employee__user=self.request.user)
You have written wrong code. You want an Asset object by using:
assetlist = Asset.objects.filter(organisation__employee__user=self.request.user)
But you clearly can see in your models.py that there is no relationship between Organisation and Employee, then how can you get a result using organisation__employee...?
You should first create a ForeignKey field to relate with Employee model then your code will work fine.