How to approach complex circular relations between models? - python

I'm trying to set up a database for my media files, but can't find a way to translate the data to models and useful, but not redundant relationships.
There are Photos and Videos as models on the media side.
class Photo(MediaFile):
file = UniqueNameImageField(upload_to="photos/")
preview = UniqueNameImageField(upload_to="previews/", blank=True)
thumbnail = models.ImageField(upload_to="thumbnails/", blank=True)
mediatype = "photo"
class Video(MediaFile):
file = UniqueNameFileField(upload_to="videos/")
preview = UniqueNameFileField(upload_to="previews/", blank=True)
thumbnail = models.ImageField(upload_to="thumbnails/", blank=True)
mediatype = "video"
...
As Metadata I want to store Persons, Quotes, and Releases (contracts with the appearing person).
class Person(models.Model):
name = models.CharField(max_length=255)
age = models.IntegerField(null=True, blank=True)
city = models.CharField(max_length=255, blank=True)
district = models.CharField(max_length=255, blank=True)
occupation = models.CharField(max_length=255, blank=True)
def __str__(self):
return " ".join([self.name, "("+str(self.age)+")", self.city])
class Release(models.Model):
person = models.ForeignKey("Person", on_delete=models.PROTECT)
file = models.FileField(upload_to="release_docs/")
class Quote(models.Model):
person = models.ForeignKey("Person", on_delete=models.PROTECT)
#alternative by-line if it should not be name+age
alt_by = models.CharField(max_length=255)
text = models.TextField()
Every Release should be tied to a person.
Every Quote should be tied to a person.
But then every Person, Release, and Quote will be tied to the Photos and Videos.
The tricky part is this: Not every release counts for every photo a person is in, but rather just for a specific set of photos and videos.
Also, quotes don't belong to every photo and video a person appears in, but rather the ones from the day when the person is quoted.
This would bring me to set a many2many relationship between releases and photos/videos and also a many2many relationship between people and photos/videos and also a many2many relationships between quotes and photos/videos...
Now there is a lot of duplication because if a release is tied to a photo it already contains a connection to a person. However, the person object needs a separate relationship to the same photo.
In short, everything is tied to everything, but that leads to duplicated and maybe conflicting information in the DB.
I'm not looking for a solution to this case here, but rather a pointer in the right direction on how to handle these kinds of relations or simplify them. Surely this is not so uncommon. How to approach this / what keyword to look out for?
Thank you for your help.

Related

Django ForeignKey Relationship with an intermediate Table

I have the Goal Model and every Goal can have one Mastergoal and or multiple Subgoals.
Goals are connected with Links and every Link can have a different weight.
How do I achieve this with Django?
What I've got so far:
class Goal(models.Model):
user = models.ForeignKey(CustomUser, related_name='goals', on_delete=models.CASCADE)
name = models.CharField(max_length=300)
sub_goals = models.ManyToManyField(to='self', through='Link', symmetrical=False, related_name='master_goals')
class Link(models.Model):
master_goal = models.ForeignKey(Goal, on_delete=models.CASCADE, related_name="sub_links")
sub_goal = models.OneToOneField(Goal, on_delete=models.CASCADE, related_name="master_links")
weight = models.PositiveSmallIntegerField(default=1)
I can't just add an ForeignKey Field on Goal to itself, because every Link can have a different weight.
The solution I've got now works, but it feels wrong. I want to be able to access a Goal's Mastergoal like this: goal.master_goal

Building a contribution functionality in django (i do not necessarily need the code just an idea)

NOTE I am not necessarily asking for code to build this, just ideas on how to do this. Links and blog posts for pointers are welcome.
I am building a rest api.
I have a model
class Showcase(models.Model):
title = models.CharField(max_length=50)
description = models.TextField(null=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, related_name="Showcases")
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=255, unique=True)
def __str__(self):
return self.title
I am trying to build a functionality where the user that created a showcase can add users that contributed to the project which is the showcase. I was thinking of making this its own model like this:
class Collaborator(models.Model):
post = models.ForeignKey(Showcase, on_delete=models.CASCADE, related_name="collaborated_showcases")
owner = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, related_name="showcase_owner")
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, related_name="collaborators")
skill = models.ForeignKey(Skill, on_delete=models.CASCADE, null=True, related_name="creative_type")
role = models.TextField(null=True)
added_on = models.DateTimeField(null=True)
def __str__(self):
return f"{self.user.name} collaborated on {self.post.name}"
The user would have to search for users and then add them as a contributor to the showcase, which is where my brain scrambles a bit.
The other important thing is that I want to be able to randomly go to a user and get ALL the showcases he has contributed to.
As I see it, this structure works fine for your use case, though:
models should always be in singular case (Collaborator)
related_names should be lower case (related_name="showcases")
and I prefer to explicitly spell out all related_names, so I'd add
Collaborator.post related name collaborated_showcases
Collaborator.user related name collaborators
Showcase.user related_name owned_showcases
Then,
To find an user's owned showcases, Showcase.objects.filter(user=user)
To find an user's collaborated showcases, Showcase.objects.filter(collaborators=user) (I think)
I'd suggest having a Collaborator object for the owner user as well, so you can show their role more easily as well as make these queries simpler.

Listing all movies of a specific cinema - Foreign Key Django

I am new to Django and I have a question regarding the models and foreignkeys.
I have two models: Cinema and Movie.
One Cinema can have multiple movies, so I placed the foreign key of cinema in the Movie model.
class Cinema(models.Model):
name = models.CharField(max_length=255)
address = models.CharField(max_length=255)
class Movie(models.Model):
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
posting_cinema = models.ForeignKey('cinemas.Cinema', on_delete=models.CASCADE, null=True)
Now I want to list all the movies of a specific Cinema.
How can I do it?
The idea is the following:
The user clicks on a cinema, it opens a page with the cinema details and a button "see movies". If the user clicks this button, a new page opens and I want to have listed there the movies of that specific cinema.
I tried to figured out some solutions but sadly I am stuck. I was thinking about Movie.objects.filter(#something) but I am not sure
For a given Cinema you can use:
some_cinema.movie_set.all()
Or you can give the ForeignKey a more sensical name to query in reverse:
class Movie(models.Model):
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
posting_cinema = models.ForeignKey(
'cinemas.Cinema',
on_delete=models.CASCADE,
null=True,
# here we give the reverse relation a name
related_name='movies'
)
then you can query this with:
some_cinema.movies.all()
An alternative is to filter based on the ForeignKey, for example:
Movie.objects.filter(posting_cinema=some_cinema)
or if you have the primary key of the cinema:
Movie.objects.filter(posting_cinema__pk=some_cinema_pk)
(this can for example save a fetch of the Cinema object, if you never really need the Cinema itself).

When you have two models connected by a foreignkey in django do you need to do a join?

The Django docs are not plainly stating this and its getting annoying.
Observe the following models:
# models
class Venue(models.Model):
name = models.CharField(max_length=150, blank=False)
description = models.CharField(max_length=1000)
image = models.ImageField(upload_to=imgUnique('venueMedia/venueImages'))
streetAddress= models.CharField(max_length=100)
city = models.CharField(max_length=100, blank=False)
state = models.CharField(max_length=100, blank=False)
class Room(models.Model):
name = models.CharField(max_length=150, blank=False)
venue = models.ForeignKey(Venue, related_name='rooms', on_delete=models.CASCADE)
description = models.CharField(max_length=1000)
standingCapacity = models.IntegerField
seatedCapacity = models.IntegerField
image = models.ImageField(upload_to=imgUnique('venueMedia/venueImages'))
I have a foreign key relationship into Venue in the Room model in the rooms venue property.
Do I need to join those tables? Or will a Venue.objects.all() do that for me automatically? If not I have the raw sql as follows will this work:
venueList = Venue.objects.raw('''SELECT *
FROM venue_Venue
INNER JOIN venue_Room
ON venue_Venue_id=venue_Room_id''')
if not that then I understand there is a select_related() method and my understanding is that I would do the following:
Venue.objects.all().select_related('Room')
Can we get this clear? As you can see I have done alot of research but am still confused
select_related
select_related can be used when you want to select the objects connected by ForeignKey or OneToOne field, more precisely you can use this only when you want to select a single connected object. select_related takes a param can use it to lookup for related_field. The param related_name='rooms' specified in your model attributes is responsible for setting related names. select_related method searches for these related names and if it finds a match then it returns you the related objects. This line
Venue.objects.all().select_related('Room')
would result in a lookup error as you don't have any related names 'Room' but you have room so you also need to watch out for case sensitivity. Below one would work.
Venue.objects.all().select_related('room')
As mentioned above select_related can only be used for selecting single related objects. If you wish to fetch multiple related objects like for ManyToManyField then use prefetch_related

Django Extend Model with Optional Fields

I have a django model that looks something like
class Person(models.Model):
name = models.CharField(max_length=100)
favorite_color = models.CharField(max_length=100)
favorite_candy = models.CharField(max_length=100)
and I want to make a template model for it. Basically, I want a model that can have an arbitrary amount of Person's fields filled out. For instance, say I wanted to have a template for Person that likes chocolate - I'd say something like chocolate_template = PersonTemplate(favorite_color='chocolate') and if I wanted someone whose name is Gerald, I could say gerald_template = PersonTemplate(name='Gerald'). The thought is that I could use these template objects to later pre-fill a Person creation form.
My main question is an implementation question. It's totally possible for me to make a template like so
class PersonTemplate(models.Model):
name = models.CharField(max_length=100, blank=True)
favorite_color = models.CharField(max_length=100, blank=True)
favorite_candy = models.CharField(max_length=100, blank=True)
but the code is horrible in that I have to manually copy-paste the contents of the Person class. This means that if I change Person, I have to remember to update PersonTemplate. I was wondering if there's a prettier way to make a 'child' of a model where all of the fields are optional. Setting all of the fields of Person to blank=True and adding an isTemplate field is also a no-no because Person and PersonTemplate should be two different entities in the database. Any thoughts on this?
Yes of course :)
class PersonTemplate(Person):
field = models.CharField(max_length=100, blank=True)
Its mean you have every fields from Person and you can add more specific fields for PersonTemplate
class Person(models.Model):
Already extend from Model, its why you have access to created, modified, id, pk....
What is good its PersonTemplate 'extend' Person who 'extend' Model.
Since Django 1.10 you can overrride field like that :
class PersonTemplate(Person):
favorite_color = models.CharField(max_length=100, blank=True)
favorite_candy = models.CharField(max_length=100, blank=True)

Categories