Django models for school timetable? - django

I am building a school timetable app for each room? There are my models. Is there anything i am missing?
class Building(models.Model):
bid = models.CharField(max_length=10, primary_key=True)
name = models.CharField(max_length=255, unique=True)
class Meta:
ordering = ['bid']
def __str__(self):
return f'{self.bid}'
class Room(models.Model):
building = models.ForeignKey(Building, on_delete=models.CASCADE,
related_name='rooms')
number = models.PositiveIntegerField()
availability = models.BooleanField(default=False)
power = models.BooleanField(default=False)
class Meta:
ordering = ['building', 'number']
unique_together = ['building', 'number']
def __str__(self):
return f'{self.building.bid}/{self.number}'
class Occurrence(models.Model):
date = models.DateField('Date')
start_period = models.ForeignKey(Period, on_delete=models.CASCADE, related_name='start_at')
end_period = models.ForeignKey(Period, on_delete=models.CASCADE, related_name='end_at')
class Meta:
abstract = True
class Period(models.Model):
start = models.TimeField()
end = models.TimeField()
objects = PeriodManager()
class Meta:
ordering = ['start']
def __str__(self):
return f'{self.start}-{self.end}'
def check_time(self):
return True if self.start < self.end else False
def check_overlap(self):
pass
class TimetableModel(models.Model):
class Meta:
abstract =True
There will be a model name Booking which extends from Occurrence to allow students register to use a room in periods. I would like to make a Timetable model link to a Room Model for providing to context for rendering different weeks timetable for a room and Period models which are resemble school periods. Any advises?

I think you may need to re-think your actual models and what you're trying to achieve.
Having a look at your models you seemed to create models for different aspects of what you're trying to do but there doesn't seems to be coherence between the models.
For instance one way to look at it would be as follows:
Building
- Name
Room
- Number
- Building (FK)
Booking
- Room
- Period (you could just have statically defined list assume these are fixed)
- Duration (number of periods, this removes the need to specify end as you can calculate it.
To make a booking you need to know the following:
- The room you want to book
- The period you want to use it
- How long you'd like to book it for (ie. 2 periods)
From a data perspective that's probably the simplest models you can build (simple is always better). In terms of showing what is available you can do that at the point where you're making a booking (booking form).
I hope that gives you some idea of how to approach this, I didn't create models in this answer as I think your question wasn't really a technical how to but more of a point in the right direction.

Related

Django Many To Many Ordering

I have two tables Subjectlist and Day. Subject list is m2m in Day. So my problem is I'm creating school timetable. So for each days different subjects to be shown, when i add subjects on each days the order of subject is same.
#Models.py
class SubjectList(models.Model):
subject_name = models.CharField(max_length=25)
def __str__(self):
return self.subject_name
class Day(models.Model):
day_name = models.CharField(max_length=15)
subject_name = models.ManyToManyField(SubjectList)
class_number = models.ForeignKey(AddClass, on_delete=models.CASCADE, null=True, blank=True)
start_time = models.TimeField(null=True, blank=True)
end_time = models.TimeField(null=True, blank=True)
def __str__(self):
return self.class_number.class_number
#Views.py
class TimeTableView(APIView):
def get(self, request, id):
class_number = AddClass.objects.get(id=id)
day = Day.objects.filter(class_number=class_number.id)
print(day)
serializer = DaySerializer(day, many=True)
return Response(serializer.data)
I want to do like this
Monday - English, maths, science, Social Science
Tuesady - Maths, Social Science, Englih, Math's
but i get like this
Monday - English, maths, science, Social Science
Tuesday- English, maths, science, Social Science
both are in same order even if add subjects in different order.
You can add more fields to the M2M table by declaring a model and assigning it to the M2M relation with the through parameter
class DaySubjectList(models.Model):
day = models.ForeignKey(Day, on_delete=models.CASCADE, related_name="day_subject_lists")
subject_list = models.ForeignKey(SubjectList, on_delete=models.CASCADE, related_name="day_subject_lists")
order = models.IntegerField(default=0)
class Day(models.Model):
day_name = models.CharField(max_length=15)
subject_name = models.ManyToManyField(SubjectList, through=DaySubjectList)
class_number = models.ForeignKey(AddClass, on_delete=models.CASCADE, null=True, blank=True)
start_time = models.TimeField(null=True, blank=True)
end_time = models.TimeField(null=True, blank=True)
def __str__(self):
return self.class_number.class_number
You can set the order field using the through_defaults parameter of the related manager
my_day.subject_name.add(my_subject, through_defaults={"position": my_position})
You can order using related_name
Day.objects.filter(class_number=class_number.id).prefetch_related(Prefetch("day_subject_lists", queryset=DaySubjectList.objects.order_by("position")))
Records will only be ordered if you access my_day.day_subject_lists.all() not my_day.subject_name.all()
You can store those records under another name if thats more convenient
Day.objects.filter(class_number=class_number.id).prefetch_related(Prefetch("day_subject_lists", queryset=DaySubjectList.objects.order_by("position"), to_attr="my_prefered_name"))
be aware that my_day.my_prefered_name is a list not a queryet so don't use .all() to access it.
Use you have any issue with your models referencing each other you can use string synthax
class DaySubjectList(models.Model):
day = models.ForeignKey("myapp.Day", on_delete=models.CASCADE, related_name="day_subject_lists")
subject_list = models.ForeignKey("myapp.SubjectList", on_delete=models.CASCADE, related_name="day_subject_lists")
order = models.IntegerField(default=0)
You need to specify an order for the m2m relation [Django-doc]:
class Day(models.Model):
# ...
subject_name = models.ManyToManyField(
SubjectList,
ordered=True
)
# ...
Now if you query a Day object, the related SubjectList objects will be ordered in the order you added these.
You can further alter the order with .move(...), or .reorder(...).
Note: You should rename the subject_name field to subjects, since it relates to multiple SubjectList objects. A ForeignKey or OneToOneField indeed often uses the _name suffix, since these relate to at most one other object, but a ManyToManyField does not have this limitation.
Note: You should not use ForeignKey(.., null=True)s. A foreign key represents a relation, and a relation can not be "NULL". You should make these fields not nullable, and make the related model have a nullable parent relation (with null=True), or make the field optional with blank=True.

Many to Many in two directions

I have a friend that requested something that I was thinking would be simple and quick. It never turns out that way. Quick disclaimer, model design is a krux of mine. I often spend too long perfecting it only to have to rework it several times. Anyway, here is the current state of my model. For everything, it works, except when creating 'raids'.
from django.db import models
# Create your models here.
class PlayerRole(models.Model):
"""
PlayerRole Model
"""
role = models.CharField(max_length=20)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.role
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['role']
class PlayerClass(models.Model):
"""
PlayerClass Model
"""
name = models.CharField(max_length=100)
color = models.CharField(max_length=6)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['name']
class Player(models.Model):
"""
Player Model
"""
name = models.CharField(max_length=100)
playerclass = models.ForeignKey(PlayerClass, on_delete=models.CASCADE, blank=True, null=True)
playerrole = models.ForeignKey(PlayerRole, on_delete=models.CASCADE, blank=True, null=True)
value = models.IntegerField(default=0)
reliability = models.IntegerField(default=0)
last_drop = models.DateField(auto_now=False, blank=True, null=True)
last_raid_attended = models.DateField(auto_now=False, blank=True, null=True)
last_boss_attended = models.DateField(auto_now=False, blank=True, null=True)
drop_received = models.BooleanField(default=False)
note = models.TextField(null=True, blank=True)
core_raider = models.BooleanField(default=False)
enabled = models.BooleanField(default=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['name']
class WoWInstance(models.Model):
"""
Instance Model
"""
name = models.CharField(max_length=100)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['name']
class Boss(models.Model):
"""
Boss Model
"""
name = models.CharField(max_length=100)
instance = models.ForeignKey(WoWInstance, on_delete=models.CASCADE)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['name']
class Raid(models.Model):
"""
Raid Model
"""
date = models.DateTimeField(auto_now_add=True)
boss = models.ForeignKey(Boss, on_delete=models.CASCADE, null=True, blank=True)
success = models.BooleanField()
attendees = models.ManyToManyField(Player)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return str(self.date) + ' + ' + self.boss.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['boss']
The idea here is simply to track attendance on each attempt at fighting something in a game. In the most general and simple sense - Many Raids can contain many of the same Bosses. Many Raids can contain many of the same Players.
Now, I thought that because Django automatically creates ID fields, this method wouldn't be an issue. But, it seems not to be the case. If I create a raid with the same Boss more than once, I get...
Raid with this Boss already exists.
What am I missing here? Since all Raid and Boss are unique, shouldn't the two never overlap?
If I create a raid with the same Boss more than once, I get...
Raid with this Boss already exists.
Well that is due to the unique_together constraint:
class Raid(models.Model):
# ...
class Meta:
unique_together = ['boss']
If you write unique_together, it means you enforce a uniqness constraint on a combination of fields. But since you here have mentioned only one field ('boss'), you thus add a unique=True constraint on that specific field.
It thus means that no two Raids can exist with the same boss field. You probably want to remove that.
Since all Raid and Boss are unique, shouldn't the two never overlap?
The Raids and Bosses are already unique. An object does not belong to two models at once (unless one is the subclass of another, but let us ignore that case). You even made the name unique as well (although you better do that by adding a unique=True parameter to the name fields). So there is no need at all to make the 'boss' unique here, since you clearly do not want that.

django models - Avoid possible circular reference in database design

I have a database design and relationships problem and I am concerned with possible circular references.
To give an example, Jack has on his stock Medicines A, B and C. Medicines A and B have an active_ingredient AI1 and medicine C has an active_ingredient AI2.
Jack goes to the doctor, who prescribes him AI1. For the Prescription object, it is indifferent if he takes Medicine A or B.
Here is an example code:
class ActiveIngredient(models.Model):
...
class Medicine(models.Model):
quantity = models.IntegerField()
active_ingredient = models.ForeignKey("ActiveIngredient", on_delete=models.CASCADE)
class Person(models.Model):
...
class PersonStock(models.Model):
customer = models.ForeignKey("Person", on_delete=models.CASCADE)
medicine = models.ForeignKey("Medicine", on_delete=models.CASCADE)
expiration_date = models.DateField()
class Prescription(models.Model):
...
quantity = models.IntegerField()
What is the best solution to model this relationship?
Changing Prescription to this:
class Prescription(models.Model):
...
customer = models.ForeignKey("Person", on_delete=models.CASCADE)
active_ingredient = models.ForeignKey("ActiveIngredient", on_delete=models.CASCADE)
quantity = models.IntegerField()
Seems wrong to me given that there is the PersonStock class already connecting Person and Medicine.
You're right to be concerned about duplicated information; a major concern of database design (specifically database normalization) is avoiding that so as to eliminate the possibility of inconsistent data.
In this case, however, I think it makes more sense to keep prescriptions and their filling separate. These are two separate things, and in the real world it's very possible for mistakes to be made and the wrong medicine to be delivered. While one should endeavor to prevent such mistakes, that's very different from making it impossible to represent a mistake in your data model.
So my recommendation would be to validate the data at the application layer rather than building constraints into the data model itself. Something like:
class ActiveIngredient(models.Model):
...
class Medicine(models.Model):
quantity = models.IntegerField()
active_ingredient = models.ForeignKey("ActiveIngredient", on_delete=models.CASCADE)
class Person(models.Model):
...
class Prescription(models.Model):
...
customer = models.ForeignKey("Person", on_delete=models.CASCADE)
active_ingredient = models.ForeignKey("ActiveIngredient", on_delete=models.CASCADE)
quantity = models.IntegerField()
class PersonStock(models.Model):
prescription = models.ForeignKey("Prescription", on_delete=models.CASCADE)
medicine = models.ForeignKey("Medicine", on_delete=models.CASCADE)
expiration_date = models.DateField()
# Make sure the supplied medicine is correct.
def clean(self):
if self.medicine.active_ingredient != self.prescription.active_ingredient:
raise ValidationError("Wrong medicine!")
Alternatively you could do the check only when a PersonStock is being created.

Which approach is better in Django Jsonfield or ForeignKey?

Such a question, I want to create a price comparison site, there are two ideas how to implement a list of prices from different stores.
First via ForeignKey
class Price(models.Model):
price = models.DecimalField()
shop = models.CharField()
class Product(models.Model):
name = models.CharField(max_length=255)
prices = models.ForeignKey()
JSONfield second method
class Product(models.Model):
name = models.CharField(max_length=255)
data = JSONField()
""" Product.objects.create(name='product', data={'price': 999,
'shop': 'Amazon.com'}
def __str__(self):
return self.name
If anyone has experience, what works faster and more reliable if a large number of goods? Thanks for the early ones.

Advice Needed for the Structure of Many-To-Many Fields in Django

I am building a REST-API that will be consumed by an Angular application - this is for my guitar company’s website. There is an Artist Profile page that display an artist’s name, a short bio and a list of the projects(bands) they’re associated with and the date-ranges they were active with them. Here is where things get complicated.
Any given project can be associated with more than one artist - i.e. I could have two guitar players from the same band. I was able to solve that association by creating a many-to-many field and it worked great…until I realized that I have artists who have been in the same band at different times.
I have tried many approaches so far. I wish I could list them, but I kinda lost track. But, the code below is the where I am at right now. I can indeed associate a band with multiple artists, but I can’t associate different date ranges to different artists in the same bands. Any guidance is much appreciated.
class projectDate(models.Model):
begin = models.DateField()
end = models.DateField()
def __str__(self):
string_date_range = self.begin.strftime("%d/%m/%y") + "-" + self.end.strftime("%d/%m/%y")
return string_date_range
class artistProfiles(models.Model):
artist_name = models.CharField(max_length=20)
artist_image = models.URLField()
description = models.TextField(max_length=500)
band_website = models.URLField()
def __str__(self):
return self.artist_name
class artistProjects(models.Model):
project_name = models.CharField(max_length=20)
dates = models.ManyToManyField(projectDate, related_name='date_span')
artists = models.ManyToManyField(artistProfiles, related_name='projects')
def __str__(self):
return self.project_name
class artistSocialMedia(models.Model):
facebook = models.URLField()
twitter = models.URLField()
instagram = models.URLField()
artist = models.ForeignKey(artistProfiles, related_name='social_media', on_delete=models.CASCADE)
def __str__(self):
return self.artist.artist_name
artistProjects and projectDate should not be a many-to-many relationship, since a projectDate is specific to a project and unlikely to be shared by many. You can instead make artistProjects a foreign key in projectDate so that a artistProjects can have many projectDates but not vice versa:
class projectDate(models.Model):
begin = models.DateField()
end = models.DateField()
project = models.ForeignKey(artistProjects, related_name='dates')
Note that your artistProjects represents just one project, so you should avoid giving it a plural name. Naming it artistProject will make your code more readable.
Not sure whether i can solve your problems or not. I am going to describe it in simple way so you can just adjust it with your models.
These is my advice. Hope it solve your problems.
Artist Profile
id (PK)
artist_name
artist_image
description
band_website
Artist Social Media
id (PK)
artist_profile_id (FK)(Artist Profile)
facebook
twitter
instagram
Artist Project
id (PK)
artist_band_project_id (FK)(Artis Band Project)
Artist Band Project
id (PK)
begin
end
Artist Band Project Member
id (PK)
artis_band_project_id (FK)(Artist Band Project)
artis_profile_id (FK)(Artist Profile)
Regards,
Meikelwis Wijaya
#blhsing Ended up being the closest of all answers, but it took a little more massaging to get the relationships and JSON structure I was looking for.
Here is what worked for the models:
from django.db import models
class artistProfile(models.Model):
artist_name = models.CharField(max_length=20)
artist_image = models.URLField()
description = models.TextField(max_length=500)
band_website = models.URLField()
def __str__(self):
return self.artist_name
class artistProject(models.Model):
project_name = models.CharField(max_length=20)
def __str__(self):
return self.project_name
class projectTenure(models.Model):
begin = models.DateField()
# blank and null are allowed here in case an artists is still with a given project
end = models.DateField(blank=True, null=True)
project = models.ForeignKey(artistProject, on_delete=models.CASCADE)
artist = models.ForeignKey(artistProfile, on_delete=models.CASCADE,
related_name='projects')
def __str__(self):
# TODO: find a way to return the related project and artist names
string_date_range = self.begin.strftime("%d/%m/%y") + "-"
return string_date_range
class artistSocialMedia(models.Model):
facebook = models.URLField()
twitter = models.URLField()
instagram = models.URLField()
artist = models.ForeignKey(artistProfile, related_name='social_media',
on_delete=models.CASCADE)
def __str__(self):
return self.artist.artist_name
And here is how I serialized it:
from rest_framework import serializers
from .models import (artistProfile, artistProject, projectTenure, artistSocialMedia)
class artistSocialMediaSerializer(serializers.ModelSerializer):
class Meta:
model = artistSocialMedia
fields = ('facebook', 'twitter', 'instagram')
class artistProjectSerializer(serializers.ModelSerializer):
class Meta:
model = artistProject
fields = ('project_name',)
class projectTenureSerializer(serializers.ModelSerializer):
project_name = serializers.CharField(source='project.project_name')
class Meta:
model = projectTenure
fields = ('project_name', 'begin', 'end')
class artistProfileSerializer(serializers.ModelSerializer):
projects = projectTenureSerializer(many=True, read_only=True)
social_media = artistSocialMediaSerializer(many=True, read_only=True)
class Meta:
model = artistProfile
fields = ('artist_name', 'artist_image', 'description',
'band_website', 'projects', 'social_media')