django models - Avoid possible circular reference in database design - django

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.

Related

django model to yeild specific HTML output without redundancy in the model

I have 3 models (supervisor, students, and allocation)
I am building an allocation system where multiple students can be allocated to one supervisor
Now I want my model to be able to yeld this output
Example of how i want the output to come out
Here are the structure of my model
class StudentProfile(models.Model):
stud_id = models.UUIDField(default=uuid.uuid4, primary_key=True, unique=True)
user_id = models.OneToOneField(User,blank=True, null=True, on_delete=models.CASCADE)
programme_id = models.ForeignKey(Programme, on_delete=models.CASCADE)
session_id = models.ForeignKey(Sessi`**enter code here**`on, on_delete=models.CASCADE)
type_id = models.ForeignKey(StudentType, on_delete=models.CASCADE)
dept_id = models.ForeignKey(Department, on_delete=models.CASCADE)
class SupervisorProfile(models.Model):
super_id = models.UUIDField(default=uuid.uuid4, primary_key=True, unique=True)
user_id = models.ForeignKey(User, on_delete=models.CASCADE)
dept_id = models.ForeignKey(Department, on_delete=models.CASCADE)
class Allocate(models.Model):
allocate_id = models.UUIDField(default=uuid.uuid4, primary_key=True, unique=True)
stud_id = models.ForeignKey(StudentProfile, on_delete=models.CASCADE)
super_id = models.ForeignKey(SupervisorProfile, on_delete=models.CASCADE)
now my main focus is the Allocate model where the allocation is made, and there is a lot of redundancy any suggestions on how to improve my model to remove redundancy in yielding the expected HTML output would be appreciated 🙏
As far as I understand, you need to assign several students to one Supervisor
For this, you only need to use ForeignKey in class StudentProfile
As below:
supervisor=models.ForeignKey(Supervisor,on_delete=models.DO_NOTHING)
But if you need to connect a student to several Supervisor, you should use ManyToManyField
ManyToManyField automatically creates a third table like class Allocate of yourself
For more information, refer to the hire.
It is also possible to reduce the redundancy by considering the department.
However
It doesn't seem that less redundancy can be found in sql database
I hope it was useful

Django model using hashmap

I have a question about how to properly model my data in Django (and later in graphene).
I have a model exam which consists of date, subject, participants, results where subject,participants, results are references to other objects. I could of course have two lists of participants and results however it would be practical to have a map of type:
pseudocode:
results= map(participant,result)
To be honest I do not know if this is even possible without introducing a additional model object participant_results
Any insight very welcome.
Benedict
I would model it this way:
class Exam(models.Model):
date = models.DateField()
subject = models.TextField()
class Participant(models.Model):
person = models.ForeignKey(Person, on_delete=models.PROTECT, related_name="participants")
result = models.FloatField(null=True, blank=True)
exam = models.ForeignKey(Exam, on_delete=models.PROTECT)
class Person(models.Model):
name = ...
You retrieve all participant from an exam using
exam.participants

Django intermediate table with a many-to-many field instead foreign key?

I have 2 models in my application:
User
Tower
My goal is to associate many towers to a user using an intermediate table because I need to add a period of time.
So I have something like this in my models:
class Tower(models.Model):
name = models.CharField(max_length=128)
class User(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField('Tower', through='Dates')
class Dates(models.Model):
user = models.ForeignKey('User', on_delete=models.CASCADE)
tower = models.ForeignKey('Tower', on_delete=models.CASCADE)
begin_date = models.DateTimeField()
end_date = models.DateTimeField()
But my goal was to have the field tower in class Dates to a many-to-many like this:
tower = models.ManyToManyField('Tower', blank=True)
So that i can associate many towers to a user in a certain period. But unfortunately django says that i need to use forgeignkey to Tower and User classes.
Have any solution for this? To apply directly the many-to-many field in the intermediate table? Or I must create a new class, sort of a GroupTower, that have a many-to-many field to the Tower class? Something like this:
class Tower(models.Model):
name = models.CharField(max_length=128)
class GroupTower(models.Model):
tower = models.ManyToManyField('Tower', blank=True)
class User(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField('Tower', through='Dates')
class Dates(models.Model):
user = models.ForeignKey('User', on_delete=models.CASCADE)
tower = models.ForeignKey('GroupTower', on_delete=models.CASCADE)
begin_date = models.DateTimeField()
end_date = models.DateTimeField()
Thanks in advance.
There is so many ways that you can design the database.
An example:
class Tower(models.Model):
name = models.CharField(max_length=128)
class User(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField('UserTower')
class UserTower(models.Model):
user = models.ForeignKey('User', on_delete=models.CASCADE)
tower = models.ManyToManyField('Tower')
begin_date = models.DateTimeField()
end_date = models.DateTimeField()
In this design you can add many UserTower instances to a user and each UserTower instance can have many towers.
Now if you query the members for a user:
User.objects.get(pk=1).members.all()
You will have members grouped by periods of time if you saved them this way but it requires you to write some codes to avoid duplicates for begin_date, begin_date and user.
And now if you need towers:
user_members = User.objects.get(pk=1).members.all().values_list('tower', flat=True)
towers = Tower.objects.filter(pk__in=user_members).distinct()
This design is okay only if you really don't want duplicates with same begin_date and begin_date which i can't find a reason for.
You can still have all the features if you add multiple instances with same begin_date, begin_date and user but different tower.

Django models for school timetable?

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.

Django: model has two ManyToMany relations through intermediate model

So I am new to Django, and want to describe the scenario: there are a bunch of Persons, and there are a bunch of Items, and a person passes Items to another Person.
I have the following model:
class Item(models.Model):
title = models.CharField(max_length=1024, blank=False)
def __unicode__(self):
return self.title
class Person(models.Model):
name = models.CharField(max_length=127, blank=False)
out_item = models.ManyToManyField(
Item,
through='Event',
through_fields=('from_user', 'item'),
related_name='giver'
)
in_item = models.ManyToManyField(
Item,
through='Event',
through_fields=('to_user', 'item'),
related_name='receiver'
)
def __unicode__(self):
return self.name
class Event(models.Model):
item = models.ForeignKey(Item)
from_user = models.ForeignKey(Person, related_name='event_as_giver')
to_user = models.ForeignKey(Person, related_name='event_as_receiver')
But makemigrations tells me app.Person: (models.E003) The model has two many-to-many relations through the intermediate model 'app.Event'.
I wonder what I did wrong? Or what is a clean way to achieve the scenario? Perhaps I can separate Event into GiveEvent and ReceiveEvent? But that just makes less sense intuitively, since there is actually only a single event when item is passed.
What you're describing sounds reasonable enough. There may be a technical reason why that's disallowed; one semantic reason is that each ManyToManyField implies the creation of a new table, and there can't be two tables with the same name (i.e. represented by the same class).
One alternative approach (shorter and more DRY) would be this:
class Person(models.Model):
name = models.CharField(max_length=127, blank=False)
to_users = models.ManyToManyField(
'self',
symmetrical=False,
related_name='from_users',
through='Event',
through_fields=('from_user', 'to_user'),
)
class Event(models.Model):
item = models.ForeignKey(Item, related_name='events')
from_user = models.ForeignKey(Person, related_name='events_as_giver')
to_user = models.ForeignKey(Person, related_name='events_as_receiver')
The table structure is the same but the descriptors are different. Accessing related people is a bit easier but accessing related items is a bit harder (for example, instead of person.out_items.all() you would say Item.objects.filter(events__from_user=person).distinct()).