Stack Inline Multiple Django Models in Admin with indirect relationship - django

I have the following three models
class Participant(models.Model):
participant_id = models.AutoField(primary_key=True)
pin = models.CharField(unique=True, max_length=4,
validators=[RegexValidator(r'^\d{1,10}$')])
class Teacher(models.Model):
teacher_id = models.AutoField(primary_key=True)
participant_id = models.OneToOneField(Participant, on_delete=models.CASCADE)
email = models.EmailField()
compensation_id = models.OneToOneField(Compensation, on_delete=models.CASCADE)
class Compensation(models.Model):
compensation_id = models.AutoField(primary_key=True)
wage = models.DecimalField(max_digits=6, decimal_places=2)
To summarize,
Participant has different types, one of them is Teacher (OneToOne
relation).
Teacher is one type of employee that is compensated and it's related (OneToOne relation) with Compensation Model
I want to create an admin form by stacking
Participant
Teacher
Compensation
The problem:
I am using Participant as a primary model and stacking teacher to it. However, when I try to stack compensation to it, it apparently doesn't work due to missing foreign key relation between Participant and Compensation models.
An easy way out would be to have the fields of the Compensation model in the Teacher Model. However, it would create redundancy when I want to associate other types of Participants to it.
What could potentially be a solution to this? I am open to altering the model if it makes thing any easier or less complicated.
P.S. There are also other types of participants (Child, Parent) that I have not mentioned.
P.S.S. There are other fields in the above-mentioned models that I have not mentioned for the sake of simplicity.

Related

Public Transportation Webapp in DJANGO : models (many-to-many relationship) for transportation systems and routes

This webapp would allow users to create alerts about controlers in public transportation in a big city. It would include mostly trains and buses.
I'm quite stuck with how to create the model. I've already created the Alert that has a station attribute with a one-to-one relationship, and a line attribute also.
I have two models : a model Stations, and a model Line.
Now each transportation mode (bus/train) has a line, stations and a schedule.Each line has several stations (or bus stops), and each stations receives many lines. I used a many-to-many fields in the Line model, but I don't know how to order the stations, since a bus will go through each of its stations in an orderly fashion, neither how to link that to a schedule. I thought about making another model "Line_Station" with each instance having the attributes Line, Station and Order, but that doesn't seem optimal for routes that have many stations, since a Route would be each instance of Line_Station for the same attribute Line. I'm new at Django and haven't really had the chance to manipulate the database relationships, but I feel like this problem could be solved with a many-to-many relationship.
(Transportations/models.py)
class Station(models.Model):
Station_name = models.CharField(max_length=200)
Station_adress = models.CharField(max_length=300)
Station_vehicule = models.ForeignKey(Vehicule, on_delete=models.CASCADE)
class Line(models.Model):
Line_number = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(100)])
Line_name = models.CharField(max_length=200, blank=True, null=True)
Line_vehicule = models.ForeignKey(Vehicule, on_delete=models.CASCADE)
Line_stations = models.ManyToManyField(Station)
class Line_station(models.Model):
line = models.ForeignKey(Line, on_delete=models.CASCADE)
station = models.ForeignKey(Station, on_delete=models.CASCADE)
order = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(60)])
Is there a more optimal way to resolve this ? The point is that I would like to create lines in my django admin and from there, add stations and select the order. If I were to think that this app would attract many users, I feel like creating two more models (routes and schedules) might create too many queries.
Here's the Alert model if it is of any interest :
(Alerts/models.py)
class Alert(models.Model):
alert_whistleblower = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
alert_timestamp = models.DateTimeField(auto_now_add=True)
alert_station = models.ForeignKey(Station, on_delete=models.CASCADE)
alert_line = models.ForeignKey(Line, on_delete=models.CASCADE)
alert_remarks = models.TextField(blank=True, null=True)
def get_absolute_url(self):
return reverse('alerts:detail', kwargs={'pk':self.pk})
Here's a diagram of the models, but I don't really know how to connect Schedule to route, since the model Route wouldn't be "one route" but many instances (one route would be each instance of the same line attribute, and an order).
Diagram
How can I make my model more optimized ?
As of now, I've used the relationship Many-to-Many on the model Line, using through = 'routes'.
class Line(models.Model):
Line_number = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(100)])
Line_stations = models.ManyToManyField(Station, through="Route")
class Route(models.Model):
line = models.ForeignKey(Line, on_delete=models.CASCADE)
station = models.ForeignKey(Station, on_delete=models.CASCADE)
order = models.IntegerField()
I'm still not sure about this. Should the many-to-many field go to Station ? Should both have one ? I still don't know how to make a model schedule on top of this. Ordering each instance of Route is quite a pain in the ass by the way, since I'd like it to auto-increment for a line instance.

How to retrieve related instances without FK using one query in django

Imagine there are three models named Movie, Actor, and Participation.
class Movie(models.Model):
identifier = models.CharField()
class Actor(models.Model):
name = models.CharField()
class Participation(models.Model):
movie_identifier = models.CharField()
actor = models.ForgeinKey(Actor, on_delete=models.CASCADE)
Let's assume that I can't use ForgeinKey for the movie in the Participation model.
how can I retrieve all the participation records of a movie with only one query?
Here is the solution if I had a foreign key for the movie in the participation table:
qs = Movie.objects.filter(identifier="an_identiier").prefetch_related("participations_set")
How can I do this without having a Movie foreign key in the Participation model?
Thanks!
One of the most important things when designing a database (hence when designing your models) is database normalization [Wikipedia].
You talk about Participation being related to multiple models like Movie, Series, Episode, etc. this means that Movie, Series, Episode all can be said to have something in common or they can be said to be a specialization of another entity let us say Participatable for the lack of a better word, or we can say Participatable is a generalization of Movie, Series, Episode, etc.
How do we model these? Well we will just have an extra model that our other models will have a OneToOneField with:
class Participatable(models.Model):
# Any common fields here
MOVIE = 'M'
SERIES = 'S'
TYPE_CHOICES = [
(MOVIE, 'Movie'),
(SERIES, 'Series'),
]
subject = models.CharField(max_length=1, choices=TYPE_CHOICES)
class Movie(models.Model):
# uncommon fields
participatable = models.OneToOneField(
Participatable,
on_delete=models.CASCADE,
related_name='movie',
)
class Series(models.Model):
# uncommon fields
participatable = models.OneToOneField(
Participatable,
on_delete=models.CASCADE,
related_name='series',
)
class Participation(models.Model):
participatable = models.ForgeinKey(Participatable, on_delete=models.CASCADE)
actor = models.ForgeinKey(Actor, on_delete=models.CASCADE)
Other than this solution which I find is the best for such modelling you can go with using the content-types framework which will essentially do what you do currently. That is it will use a field that stores the related id and also a foreign key that points to an entry in a table that will simply describe which table this id is for.

Django better join

Here is my models. This is like a sports team org app. Event model is the sports event like a baseball game. Anyone can belong to multiple teams. So, the team member represents the membership to team.
class Team(models.Model):
name = models.CharField(max_length=100, blank=True)
class TeamMember(models.Model):
member = models.ForeignKey(User, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
class Event(models.Model):
team = models.ForeignKey(Team, on_delete=models.CASCADE)
I want to get the list of events my teams have.
def get_queryset(self):
teams = TeamMember.objects.filter(member=self.request.user).values('team')
return Event.objects.filter(team__in=teams)
This does work, but I want to make it to a single join. My ORM-fu is not that great.
As Docs says
Django offers a powerful and intuitive way to “follow” relationships
in lookups, taking care of the SQL JOINs for you automatically, behind
the scenes. To span a relationship, use the field name of related
fields across models, separated by double underscores, until you get
to the field you want.
...
It works backwards, too. To refer to a “reverse” relationship, use the
lowercase name of the model.
Event.objects.filter(team__teammember__member=self.request.user)

How to call a a field of one model A into another model B so that b can work as a view

I have created a model called Department, Course. Models are as follow
This is the model for departments and course
class Departments(models.Model):
Department_Id = models.IntegerField(primary_key=True)
Department_Name = models.CharField(max_length=200)
Department_Code = models.CharField(max_length=200)
class Course(models.Model):
Course_Id = models.IntegerField(primary_key=True)
Department_Id = models.ForeignKey(Departments, on_delete=models.CASCADE)
Course_Name = models.CharField(max_length=200)
Course_Code = models.CharField(max_length=200)
I want to create a model called view which can be later on called for search. I want a view model in a such a way that it consit of the data in concat form i.e. name= Department_name+ Course_Name
class View (models.model):
view_id= models.IntegerField(primary_key=True)
Name= Department_name(I want this from Departments table)
+ Course_Name(I want this from Course table)
I try using one to one relation . I would really appricate the help
It's not clear why you'd want to do that. It's never a good idea to duplicate data from one model into another one, as it can lead to inconsistencies.
You can add a ForeignKey in View to your Course model and then when you do f"{view.course.name} {view.course.department.name}" you already have your string:
class View(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
def name(self):
return f"{self.course.name} {self.course.department.name}"
Notes:
Don't call your foreign key Department_id because it's not referring to the id but to the object itself in the Django ORM: department = models.ForeignKey(Department, on_delete=models.CASCADE). As you can see, this makes reading the code much simpler: self.course.Department_id is a Department object not an integer, so self.course.department makes more sense.
Don't prefix your field names with the class, it just makes the code so much less readable: Do you prefer department.name or department.Department_name?
The View model is still a mystery to me, as you can search without it. You can search for example for courses with a matching department name like this:
Course.objects.filter(department__name__icontains="maths")
which will return all courses with "maths" in their department name.
Remove all the ids from your models, they are created automatically by Django anyway (and called id). Again, department.id is much easier to read than department.Department_id. Also in your code, you have to generate the ids yourself since you don't set them to auto-populate.

Query which spans and follows back several tables

My question is about creating a query which filter objects that are related through several intermediate tables. My relational database looks like this:
Any number of products can be uploaded by one user (one to many relationship). However, users also can rank products. A ranking can be completed by several users and a user can have several rankings (Many to many relationship). The same applies between Product and Ranking. I use explicit intermediate tables (Rank and Belong) which defines the M2M relationships by the through parameter, because they have additional information which describes the relationship.
The models code is something like this (I omitted irrelevant fields for simplicity):
class Product(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
belong= models.ManyToManyField(Ranking, through="Belong")
#...
#The M2M table which relates Product and Ranking
class Belong(models.Model):
ranking = models.ForeignKey(Ranking, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
#...
class Meta:
unique_together = (("ranking", "product"))
class Ranking(models.Model):
user= models.ManyToManyField(settings.AUTH_USER_MODEL, through="Rank")
created = models.DateTimeField(auto_now_add=True)
#...
#The M2M table which relates User and Ranking
class Rank(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
ranking = models.ForeignKey(Ranking, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
#...
class Meta:
unique_together = (("user", "ranking"))
#The AUTH_USER_MODEL, which is not included here
My question is: How can I create a query which filters products which has been ranked by a given user? This implies “following back” relations between Belong, Ranking and Rank tables. I tried this following the Django tutorial, but it didn´t work:
Product.objects.filter(belong__ranking__rank__user=”username”)
You're a bit confused between your M2M relationships, and their through models.
For example, I don't understand why your M2M from Product to Ranking is called "belong". It should be called "rankings". Your M2M from Ranking to User at least has the right basic name, but it points to many users so should be "users".
Nevertheless, the point is that when you follow the M2Ms, you don't need to take the through tables into account. And the other issue is that "user" is itself a model, so to compare with a username you would need to continue to follow to that field. So:
Product.objects.filter(belong__user__username="username")